问题描述
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",300,100));
objs.add(new MyObject("1",200,300));
objs.add(new MyObject("3",true,200));
objs.add(new MyObject("1",500,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,true : { 1 : { 1300,{ 3 : { 1300,1000 } } }
由于我传递了一个对象作为身份,我相信每个组都会更新同一个对象。由于无法将 lambda 传递给 reduction
方法,有没有办法解决这个问题?
解决方法
您可以使用返回新实例的 static
合并函数:
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
实例。
如果您不想删除现有方法,如果将方法引用替换为以下 lambda 表达式,您仍然可以添加 static
方法:
(o1,o2)->MyObject.merge(o1,o2)
无需添加 static
方法,您可以使用以下 lambda 表达式替换 MyObject::merge
方法引用:
(o1,o2)-> new MyObject().merge(o1).merge(o2)
,
基于上面Holger's中的answer注释,使用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)));