Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 list processing - add elements conditionally

I have the following piece of code:

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;

Is there a nice way to add elements conditionally, maybe using stream operations? I would like to add elements from method2 only if the list is empty otherwise return and so on.

Edit: It's worth to mention that the methods contain heavy logic so need to be prevented from execution.

like image 919
ionut Avatar asked Dec 28 '18 11:12

ionut


People also ask

How do I add an element to a List object in Java?

You insert elements (objects) into a Java List using its add() method. Here is an example of adding elements to a Java List using the add() method: List<String> listA = new ArrayList<>(); listA. add("element 1"); listA.

Can you add items to a List in Java?

There are two methods to add elements to the list. add(E e): appends the element at the end of the list. Since List supports Generics, the type of elements that can be added is determined when the list is created. add(int index, E element): inserts the element at the given index.


3 Answers

You could try to check the return value of addAll. It will return true whenever the list has been modified, so try this:

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;

Because of lazy evaluation, the first addAll operation that added at least one element will prevent the rest from bein called. I like the fact that "||" expresses the intent quite well.

like image 120
Dorian Gray Avatar answered Oct 29 '22 06:10

Dorian Gray


I would simply use a stream of suppliers and filter on 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() will prevent unnecessary calls to methodN() when the first non-empty list is returned by one of the methods.

EDIT:
As remarked in comments below, if your list object is not initialized with anything else, then it makes sense to just return the result of the stream directly:

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);
like image 45
ernest_k Avatar answered Oct 29 '22 04:10

ernest_k


A way of doing it without repeating yourself is to extract a method doing it for you:

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

And then

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;

Or even use a for loop:

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

Now DRY is not the most important aspect. If you think your original code is easier to read and understand, then keep it like that.

like image 17
JB Nizet Avatar answered Oct 29 '22 06:10

JB Nizet