Обработка списка в Java 8 - добавление элементов условно - PullRequest
0 голосов
/ 28 декабря 2018

У меня есть следующий фрагмент кода:

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 только в том случае, если список пуст, в противном случае вернуть и так далее.

Редактировать: Стоит упомянуть, что методы содержат тяжелую логику, поэтому их следует избегать выполнения.

Ответы [ 5 ]

0 голосов
/ 28 декабря 2018

Вы можете попробовать проверить возвращаемое значение 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, которая добавила хотя бы один элемент, предотвратит вызов остальных элементов.,Мне нравится тот факт, что "||"выражает намерение довольно хорошо.

0 голосов
/ 28 декабря 2018

Вы можете создать метод следующим образом:

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());

имя метода только для иллюстрации.

0 голосов
/ 28 декабря 2018

Вы могли бы сделать свой код лучше, создав метод

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, это усложнит, в зависимости от того, как вашссылки на методы, может быть лучше использовать цикл

0 голосов
/ 28 декабря 2018

Я бы просто использовал поток поставщиков и фильтр на 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(), когда первый непустой список будет возвращен одним изметоды.

РЕДАКТИРОВАТЬ: Как отмечено в комментариях ниже, если ваш 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);
0 голосов
/ 28 декабря 2018

Один из способов сделать это без повторения - это извлечь метод, который делает это за вас:

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));

Теперь СУХОЙ не самый важный аспект.Если вы считаете, что ваш исходный код легче читать и понимать, оставьте его таким.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...