Обработка списка в Java 8 - добавление элементов условно
У меня есть следующий кусок кода:
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;
Есть хороший способ добавить элементы условно, может быть, с использованием потоковых операций? Я хотел бы добавить элементы из method2 только в том случае, если список пуст, в противном случае вернуть и так далее.
Изменение: Стоит отметить, что методы содержат тяжелую логику, поэтому необходимо предотвратить выполнение.
Ответы
Ответ 1
Я бы просто использовал поток поставщиков и фильтр на 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()
предотвратит ненужные вызовы methodN()
когда один из методов methodN()
первый непустой список.
РЕДАКТИРОВАТЬ:
Как отмечено в комментариях ниже, если ваш объект list
не инициализируется чем-то еще, то имеет смысл просто возвращать результат потока напрямую:
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);
Ответ 2
Вы можете попробовать проверить возвращаемое значение addAll
. Он будет возвращать true
всякий раз, когда список был изменен, поэтому попробуйте это:
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;
Из-за ленивых вычислений первая операция addAll
которая добавила хотя бы один элемент, предотвратит вызов остальных. Мне нравится тот факт, что "||" выражает намерение довольно хорошо.
Ответ 3
Способ сделать это без повторения - это извлечь метод, который сделает это за вас:
private void addIfEmpty(List<Object> targetList, Supplier<Collection<?>> supplier) {
if (targetList.isEmpty()) {
targetList.addAll(supplier.get());
}
}
А потом
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;
Или даже использовать цикл for:
List<Supplier<Collection<?>>> suppliers = Arrays.asList(this::method1, this::method2, ...);
List<Object> list = new ArrayList<>();
suppliers.forEach(supplier -> this.addIfEmpty(list, supplier));
Теперь СУХОЙ не самый важный аспект. Если вы думаете, что ваш исходный код легче читать и понимать, то продолжайте в том же духе.
Ответ 4
Вы можете сделать свой код лучше, создав метод
public void addAllIfEmpty(List<Object> list, Supplier<List<Object>> method){
if(list.isEmpty()){
list.addAll(method.get());
}
}
Затем вы можете использовать его следующим образом (я предположил, что ваши методы не являются статическими, если они вам нужны, вы должны ссылаться на них, используя 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;
Если вы действительно хотите использовать поток, вы можете сделать это
Stream.<Supplier<List<Object>>>of(this::method1, this::method2, this::method3, this::method4, this::method5, this::method6)
.collect(ArrayList::new, this::addAllIfEmpty, ArrayList::addAll);
IMO это усложняет, в зависимости от того, как ссылки на ваши методы, может быть лучше использовать цикл
Ответ 5
Вы можете создать метод как таковой:
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);
}
и затем назовите это следующим образом:
lazyVersion(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6());
Название метода только для иллюстрации.