web-dev-qa-db-fra.com

Java 8 traitement de la liste - ajout conditionnel d'éléments

J'ai le code suivant:

List<Object> list = new ArrayList<>();
list.addAll(method1());
if(list.isEmpty()) { list.addAll(method2()); }
if(list.isEmpty()) { list.addAll(method3()); }
if(list.isEmpty()) { list.addAll(method4()); }
if(list.isEmpty()) { list.addAll(method5()); }
if(list.isEmpty()) { list.addAll(method6()); }
return list;

Existe-t-il un moyen intéressant d’ajouter des éléments de manière conditionnelle, en utilisant éventuellement des opérations de flux? Je voudrais ajouter des éléments de method2 que si la liste est vide sinon retournez et ainsi de suite.

Edit: Il convient de mentionner que les méthodes contiennent une logique lourde, il est donc nécessaire d’empêcher leur exécution.

51
ionut

Je voudrais simplement utiliser un flux de fournisseurs et filtrer sur List.isEmpty:

Stream.<Supplier<List<Object>>>of(() -> method1(), 
                                  () -> method2(), 
                                  () -> method3(), 
                                  () -> method4(), 
                                  () -> method5(), 
                                  () -> method6())
    .map(Supplier<List<Object>>::get)
    .filter(l -> !l.isEmpty())
    .findFirst()
    .ifPresent(list::addAll);

return list;

findFirst() empêchera les appels inutiles à methodN() lorsque la première liste non vide est renvoyée par l'une des méthodes.

EDIT:
Comme indiqué dans les commentaires ci-dessous, si votre objet list n'est pas initialisé avec quoi que ce soit d'autre, il est alors logique de renvoyer directement le résultat du flux:

return  Stream.<Supplier<List<Object>>>of(() -> method1(), 
                                          () -> method2(), 
                                          () -> method3(), 
                                          () -> method4(), 
                                          () -> method5(), 
                                          () -> method6())
    .map(Supplier<List<Object>>::get)
    .filter(l -> !l.isEmpty())
    .findFirst()
    .orElseGet(ArrayList::new);
45
ernest_k

Vous pouvez essayer de vérifier la valeur de retour de addAll. Il retournera true chaque fois que la liste aura été modifiée, alors essayez ceci:

List<Object> list = new ArrayList<>();
// ret unused, otherwise it doesn't compile
boolean ret = list.addAll(method1())
    || list.addAll(method2()) 
    || list.addAll(method3())
    || list.addAll(method4())
    || list.addAll(method5())
    || list.addAll(method6());
return list;

En raison d'une évaluation paresseuse, la première opération addAll ajoutant au moins un élément empêchera que le reste soit appelé. J'aime le fait que "||" exprime très bien l'intention.

67
Dorian Gray

Une façon de le faire sans vous répéter est d’extraire une méthode le faisant pour vous:

private void addIfEmpty(List<Object> targetList, Supplier<Collection<?>> supplier) {
    if (targetList.isEmpty()) {
        targetList.addAll(supplier.get());
    }
}

Et alors

List<Object> list = new ArrayList<>();
addIfEmpty(list, this::method1);
addIfEmpty(list, this::method2);
addIfEmpty(list, this::method3);
addIfEmpty(list, this::method4);
addIfEmpty(list, this::method5);
addIfEmpty(list, this::method6);
return list;

Ou même utiliser une boucle for:

List<Supplier<Collection<?>>> suppliers = Arrays.asList(this::method1, this::method2, ...);
List<Object> list = new ArrayList<>();
suppliers.forEach(supplier -> this.addIfEmpty(list, supplier));

Maintenant DRY n’est pas l’aspect le plus important. Si vous pensez que votre code original est plus facile à lire et à comprendre, conservez-le ainsi.

17
JB Nizet

Vous pourriez rendre votre code plus agréable en créant la méthode

public void addAllIfEmpty(List<Object> list, Supplier<List<Object>> method){
    if(list.isEmpty()){
        list.addAll(method.get());
    }
}

Ensuite, vous pouvez l’utiliser comme ceci (j’ai supposé que vos méthodes ne sont pas des méthodes statiques, si vous devez les référencer à l’aide de ClassName::method1)

List<Object> list = new ArrayList<>();
list.addAll(method1());
addAllIfEmpty(list, this::method2);
addAllIfEmpty(list, this::method3);
addAllIfEmpty(list, this::method4);
addAllIfEmpty(list, this::method5);
addAllIfEmpty(list, this::method6);
return list;

Si vous voulez vraiment utiliser un flux, vous pouvez le faire

 Stream.<Supplier<List<Object>>>of(this::method1, this::method2, this::method3, this::method4, this::method5, this::method6)
                .collect(ArrayList::new, this::addAllIfEmpty, ArrayList::addAll);

OMI cela rend les choses plus compliquées, en fonction de la façon dont vos méthodes sont référencées, il peut être préférable d’utiliser une boucle.

12
Ricola

Vous pouvez créer une méthode en tant que telle:

public static List<Object> lazyVersion(Supplier<List<Object>>... suppliers){
      return Arrays.stream(suppliers)
                .map(Supplier::get)
                .filter(s -> !s.isEmpty()) // or .filter(Predicate.not(List::isEmpty)) as of JDK11
                .findFirst()
                .orElseGet(Collections::emptyList);
}

puis appelez-le comme suit:

lazyVersion(() -> method1(),
            () -> method2(),
            () -> method3(),
            () -> method4(),
            () -> method5(),
            () -> method6());

nom de la méthode à des fins d'illustration uniquement.

6
Aomine