我有一个对象流(List),想要从该流创建新对象,以便插入到Set中。但是,传入List中的两个或多个对象可能会散列到Set中的相同Key,在这种情况下,我想将第n个List对象中的String追加到Set中已经存在的String,而不是创建一个新的。
这样的东西,但是以功能形式:
HashSet<ClassB> mySet = new HashSet<>();
for (ClassA instanceA : classAList) {
if (mySet.contains(ClassB.key(instanceA))) { //static method call to find the key
mySet.get(instanceA).appendFieldA(instanceA.getFieldA());
} else {
mySet.add(new ClassB(instanceA));
}
}
return mySet;
在功能形式上,我创造了这样的东西:
List classAList = new ArrayList<>();
classAList.stream()
.map(instanceA -> new ClassB(instanceA))
.collect(Collectors.toSet());
但是当然忽略了hashmap而且我不能将我的多个ClassA实例组合在一起,这些实例都将解析为同一个ClassB。我不知道怎么把它放在那里。我是否需要忽略map()调用并创建一个自定义收集器来代替这项工作?似乎有不止一种方法可以做到这一点,但我是Streams的新手。
答案 0 :(得分:3)
很难理解你真正想要的是什么,因为你的代码示例根本不起作用。问题是Set
不像Map
那样工作,你不能要求它包含等效对象。除此之外,您还在为contains(…)
和get(…)
电话使用不同的对象。此外,还不清楚ClassB.key(instanceA)
和new ClassB(instanceA)
之间的区别是什么。
让我们尝试重新定义它:
假设我们有一个键类型Key
和一个方法Key.key(instanceA)
来定义候选组。然后我们有ClassB
这是结果类型,通过new ClassB(instanceA)
为单个(或主ClassA
实例)创建,具有.appendFieldA(…)
方法来接收另一个的值合并两个组成员时的ClassA
实例。然后,原始(Java 8之前的)代码将如下所示:
HashMap<Key, ClassB> myMap = new HashMap<>();
for(ClassA instanceA: classAList) {
Key key=Key.key(instanceA);
if(myMap.containsKey(key)) {
myMap.get(key).appendFieldA(instanceA.getFieldA());
} else {
myMap.put(key, new ClassB(instanceA));
}
}
然后,myMap.values()
为您提供了ClassB
个实例的集合。如果必须是Set
,您可以通过
Set<ClassB> result=new HashSet<>(myMap.values());
请注意,当Key
和ClassB
与您的代码中的相同时,这也有效,但您可能会问自己,是否真的需要两者,通过{{创建的实例1}}和通过.key(instanceA)
...
这可以通过Java 8 API简化为:
new ClassB(instanceA)
或者,如果你想让它看起来更具功能性:
for(ClassA instanceA: classAList) {
myMap.compute(Key.key(instanceA), (k,b)-> {
if(b==null) b=new ClassB(instanceA);
else b.appendFieldA(instanceA.getFieldA());
return b;
});
}
对于流解决方案,存在一个问题,即合并函数将获得两个相同类型的实例,此处为classAList.forEach(instanceA ->
myMap.compute(Key.key(instanceA), (k,b)-> {
if(b==null) b=new ClassB(instanceA);
else b.appendFieldA(instanceA.getFieldA());
return b;
})
);
,并且无法通过周围的上下文访问ClassB
实例我们使用上面的ClassA
解决方案。对于流解决方案,我们需要compute
中的一个方法,该方法返回第一个ClassB
实例,我们将其传递给它的构造函数,例如ClassA
。然后我们可以使用:
getFirstInstanceA()
答案 1 :(得分:1)
您可以将条目分组到将散列键映射到元素列表的地图中,然后再次调用map以将该地图转换为您所追求的集合。像这样:
List classAList = new ArrayList<>();
classAList.stream()
.collect(Collectors.groupingBy(instanceA -> ClassB.key(instanceB)))
.entrySet()
.map(entry -> entry.getValue().stream()
.map(instanceA -> new ClassB(instanceA))
.reduce(null, (a,b) -> a.appendFieldA(b)))
.collect(Collectors.toSet());