Integer 是不可变的,而 Integer[] 数组是可变的。累加器应该是有状态的。
假设您有 2 个对 2 个 Integer 对象的引用。
Integer a = 1;
Integer b = 2;
本质上,您所指的实例是不可变的:它们一旦创建就无法修改。
Integer a = 1; // {Integer@479}
Integer b = 2; // {Integer@480}
您决定使用a 作为累加器。
a += b;
a 当前持有的值满足您的需求。这是3。但是,a 不再是指你之前的那个{Integer@479}。
我在您的Collector 中添加了调试语句并让事情变得清晰。
public static <T> Collector<T, Integer, Integer> summingInt(ToIntFunction<? super T> mapper) {
return Collector.of(
() -> {
Integer zero = 0;
System.out.printf("init [%d (%d)]\n", zero, System.identityHashCode(zero));
return zero;
},
(a, t) -> {
System.out.printf("-> accumulate [%d (%d)]\n", a, System.identityHashCode(a));
a += mapper.applyAsInt(t);
System.out.printf("<- accumulate [%d (%d)]\n", a, System.identityHashCode(a));
},
(a, b) -> a += b,
a -> a
);
}
如果你使用它,你会注意到类似的模式
init [0 (6566818)]
-> accumulate [0 (6566818)]
<- accumulate [1 (1029991479)]
-> accumulate [0 (6566818)]
<- accumulate [2 (1104106489)]
-> accumulate [0 (6566818)]
<- accumulate [3 (94438417)]
尽管+= 的所有尝试都失败了,但0 (6566818) 没有被更改。
如果您将其重写为使用AtomicInteger
public static <T> Collector<T, AtomicInteger, AtomicInteger> summingInt(ToIntFunction<? super T> mapper) {
return Collector.of(
() -> {
AtomicInteger zero = new AtomicInteger();
System.out.printf("init [%d (%d)]\n", zero.get(), System.identityHashCode(zero));
return zero;
},
(a, t) -> {
System.out.printf("-> accumulate [%d (%d)]\n", a.get(), System.identityHashCode(a));
a.addAndGet(mapper.applyAsInt(t));
System.out.printf("<- accumulate [%d (%d)]\n", a.get(), System.identityHashCode(a));
},
(a, b) -> { a.addAndGet(b.get()); return a;}
);
}
您将看到一个真正的累加器(作为mutable reduction 的一部分)在运行
init [0 (1494279232)]
-> accumulate [0 (1494279232)]
<- accumulate [1 (1494279232)]
-> accumulate [1 (1494279232)]
<- accumulate [3 (1494279232)]
-> accumulate [3 (1494279232)]
<- accumulate [6 (1494279232)]