繁体   English   中英

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

[英]Using Java 8 Stream Reduce to return List after performing operation on each element using previous elements values

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

我有一个包含开始计数器和结束计数器的计数器列表。 项的起始计数器始终是上一个的终止计数器。 我有这些计数器listItems的列表,我希望它们可以高效地循环通过,过滤掉不活动的记录,然后将列表缩小为一个新的List,其中设置了所有StartCounters。 我有以下代码:

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

但这并没有真正起作用,我有点受阻,有人可以给我一些建议来帮助我完成这项工作吗? 还是有同样有效的更好方法来做到这一点? 谢谢!

减少将所有元素减少为单个值。 使用(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());

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

active.forEach(new Consumer<CounterChain>() {
    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; });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM