调用Java Stream的collect方法得意外结果,求原因及修正方案
Java Stream collect方法问题分析与修正
问题根源
你用的三参数collect方法要求supplier必须返回可变的结果容器,但你用了Integer作为容器——而Integer是不可变类,所有的赋值操作(比如result += element * element)只会创建新的Integer对象,根本不会修改supplier提供的初始容器(也就是那个0)。
具体来说:
- supplier返回的
0是一个不可变的Integer实例,后续所有累加操作都只是在临时变量上创建新对象,原容器始终保持初始值0 - accumulator和combiner里的操作完全无效,因为它们没有修改到实际的结果容器
修正方案
方案1:用AtomicInteger作为可变容器
AtomicInteger是线程安全的可变数值容器,适合用来做累加:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Supplier<AtomicInteger> supplier = () -> new AtomicInteger(0); BiConsumer<AtomicInteger, Integer> accumulator = (result, element) -> result.addAndGet(element * element); BiConsumer<AtomicInteger, AtomicInteger> combiner = (result1, result2) -> result1.addAndGet(result2.get()); Integer sumOfSquares = numbers.stream() .collect(supplier, accumulator, combiner) .get(); System.out.println(sumOfSquares); // 输出55
方案2:自定义简单可变整数类
如果不需要线程安全,自己写个轻量级的可变容器更高效:
// 自定义可变整数容器 class MutableInt { int value; MutableInt(int value) { this.value = value; } } public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Supplier<MutableInt> supplier = () -> new MutableInt(0); BiConsumer<MutableInt, Integer> accumulator = (result, element) -> result.value += element * element; BiConsumer<MutableInt, MutableInt> combiner = (result1, result2) -> result1.value += result2.value; Integer sumOfSquares = numbers.stream() .collect(supplier, accumulator, combiner) .value; System.out.println(sumOfSquares); // 输出55 } }
方案3:用reduce简化实现
如果只是做数值累加,没必要用三参数collect,直接用reduce更简洁:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Integer sumOfSquares = numbers.stream() .map(x -> x * x) .reduce(0, Integer::sum); System.out.println(sumOfSquares); // 输出55
核心注意点
三参数collect的设计目的是处理可变容器的归并,比如集合、字符串拼接器(StringBuilder)这类可以原地修改的对象。用不可变类型当容器,所有修改操作都是白费功夫,最终只会返回初始值。
内容的提问来源于stack exchange,提问作者Gyonder
相关产品推荐
相关产品推荐

