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

use*_*171 5 java reduce java-8 java-stream

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

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

List<CounterChain> active = listItems.stream()
                .filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
                .reduce(new ArrayList<CounterChain>(), (a,b) -> { b.setStartCounter(a.getEndCounter()); return b; });
Run Code Online (Sandbox Code Playgroud)

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

Hol*_*ger 5

减少降低到一个值的所有元素.使用(a,b) -> b表单的缩小功能会将所有元素减少到最后一个,因此当您想要List包含所有(匹配)元素时,它是不合适的.

除此之外,您正在修改输入值,这违反了该操作的合同.进一步注意,该函数需要是关联的,即流是否将执行f(f(e?,e?),e?))f(e?,f(e?,e?))在使用缩减函数处理三个后续流元素时无关紧要.

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

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

List<CounterChain> active = listItems.stream()
    .filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
    .collect(Collectors.toList());
for(int ix=1, num=active.size(); ix<num; ix++)
    active.get(ix).setStartCounter(active.get(ix-1).getEndCounter());
Run Code Online (Sandbox Code Playgroud)

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

active.forEach(new Consumer<CounterChain>() {
    CounterChain last;
    public void accept(CounterChain next) {
        if(last!=null) next.setStartCounter(last.getEndCounter());
        last = next;
    }
});
Run Code Online (Sandbox Code Playgroud)

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

IntStream.range(1, active.size())
    .forEach(ix -> active.get(ix).setStartCounter(active.get(ix-1).getEndCounter()));
Run Code Online (Sandbox Code Playgroud)

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