我是Streams and 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; });
但它并没有真正发挥作用而且我有点卡住了,有人能给我一些建议来帮我搞定这个吗?或者是否有同样有效的更好的方法来做到这一点?谢谢!
答案 0 :(得分:5)
Reduction 将所有元素减少为单个值。使用(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
循环相比,它们都没有太大的优势。
答案 1 :(得分:0)
虽然@Holger提供的普通for
循环解决方案已经足够好了,但我建议您尝试使用第三方库来解决这类常见问题。例如:StreamEx或JOOL。这是StreamEx的解决方案。
StreamEx.of(listItems).filter(e -> e.getCounterStatus() == CounterStatus.ACTIVE)
.scanLeft((a,b) -> { b.setStartCounter(a.getEndCounter()); return b; });