Collectors.reducing 方法在用作 Collectors.partitionBy 的下游时正在更新相同的标识
Collectors.reducing method is updating the same identity when used as downstream for Collectors.partitionBy
我有一个 class 类似于下面的 MyObject
。
public class MyObject {
private String key; // not unique. multiple objects can have the same key.
private boolean isPermanent;
private double value1;
private double value2;
public MyObject merge(MyObject m){
value1 += m.getValue1();
value2 += m.getValue2();
return this;
}
// getters, setters and constructors...
}
示例数据如下:
List<MyObject> objs = new ArrayList<>();
objs.add(new MyObject("1", false, 100, 200));
objs.add(new MyObject("2", false, 300, 100));
objs.add(new MyObject("1", false, 200, 300));
objs.add(new MyObject("3", true, 100, 200));
objs.add(new MyObject("1", true, 500, 100));
objs.add(new MyObject("1", true, 100, 100));
我想根据 isPermanent
和 key
组合这些对象并执行了以下操作:
(请注意,我在下面的代码中添加了 import static java.util.stream.Collectors.*
以导入 groupingBy
、partitioningBy
和 reducing
)
objs.stream()
.collect(partitioningBy(MyObject::isPermanent,
groupingBy(MyObject::getKey,
reducing(new MyObject(), MyObject::merge))));
返回的地图类型为Map<Boolean, Map<String, MyObject>>
。
我希望返回的地图如下所示(忽略 value1
和 value2
以外的字段)
{false : { 1 : { 300, 500 } } , { 2 : { 300, 100 } }, true : { 1 : { 600, 200 } } , { 3 : { 100, 200 } } }
但我得到的结果是这样的:
{false : { 1 : { 1300, 1000 } } , { 2 : { 1300, 1000 } }, true : { 1 : { 1300, 1000 } } , { 3 : { 1300, 1000 } } }
由于我将一个对象作为身份传递,我相信每个组都会更新同一个对象。由于无法将 lambda 传递给 reduction
方法,请问有什么办法可以解决这个问题吗?
您可以使用 static
合并函数 returns 新实例:
public static MyObject merge(MyObject one,MyObject two) {
MyObject merged = new MyObject ();
merged.setValue1(one.getValue1()+two.getValue1());
merged.setValue2(one.getValue2()+two.getValue2());
return merged;
}
您必须删除现有的非静态 merge
方法,以便编译器选择 static
方法来代替方法引用。
这样,MyObject::merge
每次都会产生一个新的MyObject
实例。
如果您不想删除现有方法,您仍然可以添加 static
方法,前提是您将方法引用替换为以下 lambda 表达式:
(o1,o2)->MyObject.merge(o1,o2)
在不添加static
方法的情况下,可以将MyObject::merge
方法引用替换为以下lambda表达式:
(o1,o2)-> new MyObject().merge(o1).merge(o2)
基于Holger's comment in the above ,不用reducing
,最好用Collector.of
.
对于给定的 List<MyObject> objs
问题,下面使用 reducing
的代码将生成 7
个 MyObject
类型的新对象。 (生成一个新对象作为标识,每次归约生成一个对象)。
groupingBy(MyObject::getKey, reducing((o1,o2)-> new MyObject().merge(o1).merge(o2)));
但是使用 mutable reduction 和 Collectors
,创建的对象数量将减少到 4
,这是给定输入需要创建的最佳对象数量有问题。 (每个 true
和 false
分区减少了 2 个对象)。
Collector<MyObject, MyObject, MyObject> objCollector = Collector.of(MyObject::new,
MyObject::merge,
MyObject::merge);
objs.stream()
.collect(partitioningBy(MyObject::isPermanent,
groupingBy(MyObject::getKey, objCollector)));
我有一个 class 类似于下面的 MyObject
。
public class MyObject {
private String key; // not unique. multiple objects can have the same key.
private boolean isPermanent;
private double value1;
private double value2;
public MyObject merge(MyObject m){
value1 += m.getValue1();
value2 += m.getValue2();
return this;
}
// getters, setters and constructors...
}
示例数据如下:
List<MyObject> objs = new ArrayList<>();
objs.add(new MyObject("1", false, 100, 200));
objs.add(new MyObject("2", false, 300, 100));
objs.add(new MyObject("1", false, 200, 300));
objs.add(new MyObject("3", true, 100, 200));
objs.add(new MyObject("1", true, 500, 100));
objs.add(new MyObject("1", true, 100, 100));
我想根据 isPermanent
和 key
组合这些对象并执行了以下操作:
(请注意,我在下面的代码中添加了 import static java.util.stream.Collectors.*
以导入 groupingBy
、partitioningBy
和 reducing
)
objs.stream()
.collect(partitioningBy(MyObject::isPermanent,
groupingBy(MyObject::getKey,
reducing(new MyObject(), MyObject::merge))));
返回的地图类型为Map<Boolean, Map<String, MyObject>>
。
我希望返回的地图如下所示(忽略 value1
和 value2
以外的字段)
{false : { 1 : { 300, 500 } } , { 2 : { 300, 100 } }, true : { 1 : { 600, 200 } } , { 3 : { 100, 200 } } }
但我得到的结果是这样的:
{false : { 1 : { 1300, 1000 } } , { 2 : { 1300, 1000 } }, true : { 1 : { 1300, 1000 } } , { 3 : { 1300, 1000 } } }
由于我将一个对象作为身份传递,我相信每个组都会更新同一个对象。由于无法将 lambda 传递给 reduction
方法,请问有什么办法可以解决这个问题吗?
您可以使用 static
合并函数 returns 新实例:
public static MyObject merge(MyObject one,MyObject two) {
MyObject merged = new MyObject ();
merged.setValue1(one.getValue1()+two.getValue1());
merged.setValue2(one.getValue2()+two.getValue2());
return merged;
}
您必须删除现有的非静态 merge
方法,以便编译器选择 static
方法来代替方法引用。
这样,MyObject::merge
每次都会产生一个新的MyObject
实例。
如果您不想删除现有方法,您仍然可以添加 static
方法,前提是您将方法引用替换为以下 lambda 表达式:
(o1,o2)->MyObject.merge(o1,o2)
在不添加static
方法的情况下,可以将MyObject::merge
方法引用替换为以下lambda表达式:
(o1,o2)-> new MyObject().merge(o1).merge(o2)
基于Holger's comment in the above reducing
,最好用Collector.of
.
对于给定的 List<MyObject> objs
问题,下面使用 reducing
的代码将生成 7
个 MyObject
类型的新对象。 (生成一个新对象作为标识,每次归约生成一个对象)。
groupingBy(MyObject::getKey, reducing((o1,o2)-> new MyObject().merge(o1).merge(o2)));
但是使用 mutable reduction 和 Collectors
,创建的对象数量将减少到 4
,这是给定输入需要创建的最佳对象数量有问题。 (每个 true
和 false
分区减少了 2 个对象)。
Collector<MyObject, MyObject, MyObject> objCollector = Collector.of(MyObject::new,
MyObject::merge,
MyObject::merge);
objs.stream()
.collect(partitioningBy(MyObject::isPermanent,
groupingBy(MyObject::getKey, objCollector)));