如何在单次Java Stream中同时过滤对象并统计不同条件实例数
单次Stream操作完成双需求的实现方案
当然可以在单次Stream遍历中完成这两个需求,不用两次遍历列表。核心是在遍历每个元素时同时判断两个条件,分别更新筛选列表和计数器。下面给两种常用实现方式:
方法一:自定义Collector(推荐,适配并行流)
先定义一个用来存储结果的简单类,把筛选列表和计数器封装进去:
class ProcessResult { private final List<MyClass> filteredItems = new ArrayList<>(); private int nonXAndZCount = 0; // 处理单个元素的逻辑 public void accept(MyClass item) { if (item.isX() && item.isY()) { filteredItems.add(item); } else if (!item.isX() && item.isZ()) { nonXAndZCount++; } // 不满足条件的元素直接跳过 } // 并行流时合并多个结果容器 public ProcessResult merge(ProcessResult other) { this.filteredItems.addAll(other.filteredItems); this.nonXAndZCount += other.nonXAndZCount; return this; } // 获取结果的方法 public List<MyClass> getFilteredItems() { return filteredItems; } public int getNonXAndZCount() { return nonXAndZCount; } }
然后用Stream的collect方法来使用这个类:
ProcessResult result = myClasses.stream() .collect( ProcessResult::new, // 创建结果容器 ProcessResult::accept, // 处理每个元素 ProcessResult::merge // 合并并行处理的结果 ); List<MyClass> filteredClasses = result.getFilteredItems(); int isNotXandZCounter = result.getNonXAndZCount();
这种方式完全符合Stream的设计规范,并行流场景下也不需要额外做线程同步,Collector会自动处理结果合并。
方法二:用容器变量直接处理(简洁,串行流友好)
如果不想额外定义类,可以用数组或者原子类来包装计数器(因为lambda里不能修改基本类型变量),配合列表一起处理:
List<MyClass> filteredList = new ArrayList<>(); int[] counter = {0}; // 用数组包装int,让lambda能修改值 myClasses.stream().forEach(item -> { if (item.isX() && item.isY()) { filteredList.add(item); } else if (!item.isX() && item.isZ()) { counter[0]++; } });
要是用并行流的话,得注意线程安全,换成同步列表和原子计数器就行:
List<MyClass> filteredList = Collections.synchronizedList(new ArrayList<>()); AtomicInteger counter = new AtomicInteger(0); myClasses.parallelStream().forEach(item -> { if (item.isX() && item.isY()) { filteredList.add(item); } else if (!item.isX() && item.isZ()) { counter.incrementAndGet(); } });
另外提一句:你原来的代码里filter(MyClass::isX() && MyClass::isY())是语法错误,正确写法应该是filter(item -> item.isX() && item.isY()),方法引用不能直接用逻辑运算符组合。
内容的提问来源于stack exchange,提问作者artificialdeathinthewest
相关产品推荐
相关产品推荐

