Java 8列表处理-有条件地添加元素

时间:2018-12-28 11:24:19

标签: java collections java-8 java-stream

我有以下代码:

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 :(得分:67)

您可以尝试检查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操作添加了至少一个元素将阻止其余的调用。我喜欢“ ||”的事实表达意图很好。

答案 1 :(得分:45)

我只使用供应商流并在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);

答案 2 :(得分:17)

一种无需重复自己的方法是提取一种为您执行的方法:

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

现在,干燥不是最重要的方面。如果您认为您的原始代码更易于阅读和理解,请保持原样。

答案 3 :(得分:11)

您可以通过创建方法使代码更好

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,可以这样做

 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使它变得更加复杂,具体取决于如何引用您的方法,使用循环可能会更好。

答案 4 :(得分:6)

您可以这样创建一个方法:

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

方法名称仅用于说明目的。