可变对象上的流上Java reduce
在阅读了OP说应该对不可变对象执行reduce操作的地方之后,下面的用法是否错误?如果是,为什么?它生成我所期望的结果,即结果“foo”对象中的数字4可变对象上的流上Java reduce,java,java-stream,immutability,Java,Java Stream,Immutability,在阅读了OP说应该对不可变对象执行reduce操作的地方之后,下面的用法是否错误?如果是,为什么?它生成我所期望的结果,即结果“foo”对象中的数字4 @Test public void foo() { List<Foo> foos = new ArrayList<>(); foos.add(new Foo(1)); foos.add(new Foo(1)); foos.add(new Foo(1)); foos.add(ne
@Test
public void foo() {
List<Foo> foos = new ArrayList<>();
foos.add(new Foo(1));
foos.add(new Foo(1));
foos.add(new Foo(1));
foos.add(new Foo(1));
Foo foo = foos.stream().reduce(new Foo(0), Foo::merge);
System.out.println();
}
static class Foo {
int foo;
Foo(int f) {
foo = f;
}
Foo merge(Foo other) {
foo += other.foo;
return this;
}
}
@测试
公共图书馆{
List foos=new ArrayList();
添加(新的Foo(1));
添加(新的Foo(1));
添加(新的Foo(1));
添加(新的Foo(1));
Foo-Foo=foos.stream().reduce(新的Foo(0),Foo::merge);
System.out.println();
}
静态类Foo{
int foo;
Foo(内部文件){
foo=f;
}
Foo合并(Foo其他){
foo+=other.foo;
归还这个;
}
}
考虑以下几点。整数是不可变的,Foo是可变的。
分别创建两个列表
List<Foo> foos = IntStream.range(1, 1001).mapToObj(Foo::new)
.collect(Collectors.toList());
List<Integer> ints = IntStream.range(1,1001).boxed()
.collect(Collectors.toList());
印刷品
500500
500500
570026
500500
两者都是正确的
现在通过并行流使用线程再次减少它们,使用第三个参数组合不同的线程以减少
Foo foo = foos.parallelStream().reduce(new Foo(0), Foo::merge, Foo::merge);
Integer integer = ints.parallelStream().reduce(Integer.valueOf(0), (a,b)->a+b, (a,b)->a+b);
System.out.println(foo);
System.out.println(integer);
印刷品
500500
500500
570026
500500
哎呀!这个问题与多个线程和foo对象在没有任何适当同步的情况下同时更新有关
如果将Foo
类合并方法修改为以下内容,则一切正常
Foo merge(Foo other) {
return new Foo(this.foo + other.foo);
}
因此,Foo仍然可以通过setter进行修改,但您不应该在缩减操作中使用setter。始终返回新实例,而不是修改当前实例
class Foo {
int foo;
Foo(int f) {
foo = f;
}
Foo merge(Foo other) {
foo+=other.foo;
return new Foo(foo);
}
public String toString() {
return foo + "";
}
}
}
由于有一种趋势,开发人员会回答“但如果我不使用并行”,所以值得注意的是,这种错误使用的问题并不局限于并行处理。例如,
Map m=IntStream.range(11001).mapToObj(Foo::new).collect(Collectors.groupby(f->f.Foo%8,Collectors.reduced(new Foo(0),Foo::merge));System.out.println(m.values().stream().mapToInt(f->f.foo.sum())
此外,还可以使用collect
,而不是使用返回新对象的合并函数。例如,Foo-Foo=foos.parallelStream().collect(()->new-Foo(0),Foo::merge,Foo::merge)代码>使用原始的可变类可以完美地工作…非常有趣,我认为只有当它并行运行时,问题才会出现。你能详细解释一下这个地图的例子吗?为什么它不能像一些人所期望的那样工作?@Holger可以给我一个提示,在分组过程中,是什么导致了这种奇怪的行为,谢谢?@fishysushireduce
的第一个参数是可以在任意位置合并的标识元素,任意次数,因为合同要求这没有任何区别。当你修改那个对象时,你就是在破坏契约。在实践中,这表明当在不同的工作线程中执行多个并行缩减时,或者当由于分组而执行多个缩减时,每个组一个。可以通过并行分组最大化。未来可能会带来其他业务,其中多次或部分削减是有益的。