Java流:对链接使用可选的filter()操作

Alk*_*dis 6 java lambda predicate java-8 java-stream

注意:这个问题是相关的java.util.Optional.

在处理流时,我经常使用这样的逻辑:

 Stream<FooBar> stream = myInitialStream();
 if (needsFilter1) stream = stream.filter(c -> whatever1());
 if (needsFilter2) stream = stream.filter(c -> whatever2());
 ...
 return stream.collect(toList());
Run Code Online (Sandbox Code Playgroud)

我想要实现的是使用链接将上面的代码转换为单个表达式.我发现这更具可读性和直接性.到目前为止,我发现实现这一目标的唯一方法是:

return myInitialStream()
       .filter(needsFilter1? c->whatever1() : c->true)
       .filter(needsFilter2? c->whatever2() : c->true)
       .collect(toList());
Run Code Online (Sandbox Code Playgroud)

尽管如此,这会对那些琐碎的c->truelamdas 进行不必要的调用,这可能会在扩展时产生一些性能成本.

所以我的问题是:是否有更好的方法来生成包含可选过滤的链式流表达式?

更新:也许我没有说清楚,但我的问题是找到单表达式解决方案.如果非要使用多个语句(初始化谓词,例如),我还可以用我的问题基本上不相同的第一个代码块.

Nik*_*las 11

使用Predicate::and返回新的谓词来根据条件链接谓词.

Predicate<FooBar> predicate = c -> whatever();

if (condition1) { predicate = predicate.and(c -> whatever1()); }
if (condition2) { predicate = predicate.and(c -> whatever2()); }

List<FooBar> dest = list.stream()
    .filter(predicate)
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

在请求单个表达式的更新时.无论如何,您需要一个映射条件来源来预测.对于数据结构Map<Supplier<Boolean>, Predicate<Integer>>,其中键是Supplier决定是否Predicate<FooBar>应使用value()的条件.

降低地图的一个新的条目Predicate<FooBar>使用这些链接PredicatesPredicate::and,为此,他们的Supplier<Boolean>收益true(条件是有效的).

有一个Map条件:

Map<Supplier<Boolean>, Predicate<FooBar>> map = new HashMap<>();
map.put(() -> needsFilter1, c -> whatever1());
map.put(() -> needsFilter2, c -> whatever2());
...
Run Code Online (Sandbox Code Playgroud)

这是一个单一的Stream声明:

List<Integer> dest = list
        .stream()
        .filter(map.entrySet()                            // filter with a predicate ...
                .stream()
                .filter(e -> e.getKey().get())            // .. where a condition is 'true'
                .map(Entry::getValue)                     // .. get Predicates
                .reduce(i -> true, (l, r) -> l.and(r)))   // .. reduce them using AND
        .collect(Collectors.toList());               
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用`BooleanSupplier`代替`Supplier <Boolean>`.此外,代替`.reduce(i - > true,(l,r) - > l.and(r))`,你可以使用`.reduce(Predicate :: and).orElse(i - > true)`这样可以避免将现有的谓词与`i-> true`组合在一起(实际上,如果只剩下一个,你将获得原始谓词). (3认同)
  • @Nikolas非常感谢!现在这是天才的想法.因此,据我了解您的方法,我可以使用地图或集合来构建我的流.这些将提供要使用的条件和过滤器.在最坏的情况下,我会有一个o-> true过滤的开销,但这只是一个.filter()部分也可以进入一个只写一次并在任何地方使用的utils函数.对我来说很棒! (2认同)