在使用先前元素值对每个元素执行操作之后,使用Java 8 Stream Reduce返回List

我是Streams和Reduce的新手,所以我正在尝试并遇到问题:

我有一个计数器列表,它有一个启动计数器和结束计数器。 项目的startcounter始终是前一个的终结者。 我有一个列表,这些计数器listItems ,我想有效地循环,过滤掉非活动记录,然后将列表减少到一个新的列表,其中设置了所有的StartCounters。 我有以下代码:

 List active = listItems.stream() .filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE) .reduce(new ArrayList(), (a,b) -> { b.setStartCounter(a.getEndCounter()); return b; }); 

但它并没有真正奏效,我有点卡住了,有人能给我一些建议来帮我搞定吗? 或者是否有同样有效的更好的方法来做到这一点? 谢谢!

减少将所有元素减少为单个值。 使用(a,b) -> bforms的缩小函数会将所有元素减少到最后一个元素,因此当您想要获得包含所有(匹配)元素的List时,它是不合适的。

除此之外,您正在修改输入值,这违反了该操作的合同。 还要注意,该函数必须是关联的 ,即当处理三个流时,流是否将执行f(f(e₁,e₂),e₃))f(e₁,f(e₂,e₃))具有缩减function的后续流元素。

或者,将它放在一行中,您没有使用正确的工具来完成工作。

最干净的解决方案不是混合这些不相关的操作:

 List active = listItems.stream() .filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE) .collect(Collectors.toList()); for(int ix=1, num=active.size(); ix 

第二个循环也可以使用forEach实现,但由于其有状态性质,它需要内部类:

 active.forEach(new Consumer() { CounterChain last; public void accept(CounterChain next) { if(last!=null) next.setStartCounter(last.getEndCounter()); last = next; } }); 

或者,使用基于索引的流:

 IntStream.range(1, active.size()) .forEach(ix -> active.get(ix).setStartCounter(active.get(ix-1).getEndCounter())); 

但是,与普通for循环相比,它们都没有太多优势。

虽然@Holger提供的带for循环的解决方案已经足够好了,但我建议你尝试使用第三方库来解决这类常见问题。 例如: StreamEx或JOOL。 这是StreamEx的解决方案。

 StreamEx.of(listItems).filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE) .scanLeft((a,b) -> { b.setStartCounter(a.getEndCounter()); return b; });