我有这样的结构(简化):
class C1 {
Integer id;
Set<C2> c2Set;
public C1 add(C2 c2) {
c2Set.add(c2);
return this;
}
//equals and hashcode methods based on id field.
@Override public String toString() {
return "C1: {" + id + ", " + c2Set + "}";
}
}
class C2 {
Integer id;
//equals and hashcode methods based on id field.
@Override
public String toString() {
return "C2: " + id;
}
}
要填充此结构,请执行以下查询:
SELECT c1.id, c2.id
FROM tablex c1
INNER JOIN tablex_tabley d ON d.c1id = c1.id
INNER JOIN tabley c2 ON c2.id = d.c2id
ORDER BY c1.id;
这是直接从ResultSet
(使用JDBI)逐行读取的。读取的数据结构与此类似:
List<C1> c1List = asList(
new C1(1).add(new C2(1)),
new C1(1).add(new C2(2)),
new C1(2).add(new C2(1))
);
我需要展平此列表,使其看起来像是这样创建的:
List<C1> c1List = asList(
new C1(1).add(new C2(1)).add(new C2(2)),
new C1(2).add(new C2(1))
);
目前我使用这段代码来完成这项工作:
Map<C1, C1> c1Map = new LinkedHashMap<>();
for (C1 c1 : c1List) {
if (!c1Map.containsKey(c1)) {
c1Map.put(c1, c1);
} else {
C1 prevC1 = c1Map.get(c1);
for (C2 c2 : c1.c2Set) {
prevC1.add(c2);
}
}
}
List<C1> c1ListReduce = new ArrayList<>(c1Map.values());
//just to check the results
System.out.println(c1ListReduce);
如何使用Java 8流实现相同的目标?我无法找到一种方法来对属于C1实例的所有C2实例进行分组,并返回List<C1>
(或任何其他集合,可能是Set<C1>
),在这种情况下,如果不知道我已经访问过的元素并检索它们以添加它们。
答案 0 :(得分:3)
看起来你要做的就是按照公共id
对元素进行分组,然后将它们合并到一个C1
中:
List<C1> newC1List = c1List.stream()
.collect(Collectors.groupingBy(C1::getId/*getters should be implementer*/))
.entrySet()
.stream()
.map(entry -> /*constructor should be implemented*/
new C1(entry.getKey(), entry.getValue().stream().flatMap(c1 -> c1.getC2Set().stream()).collect(Collectors.toSet())))
.collect(toList());
让我们一步一步地解释这段代码:
collect
行正在创建新的Map
,该id
会将id
映射到具有此Stream
的元素列表。Map.Entry
对(实际为id
),其中键为C1
,值为id
列表}的,有C1
。下一步操作是将值中的所有C1
合并为一个Map.Entry<Integer, List<C1>> -> C1
(id
)。 List
取自入口键,但我可以使用id
中第一个元素的id,但看起来更难看:)
因此,entry.getKey()
元素为c2Set
,而Set
是C1
内所有Set
合并为一个flatMap
1}}。使用Stream
(来自Haskell的concatMap
类似物)的Stream
操作可以轻松完成此操作。
我使用List
方法将此collect
转换为{{1}}。
答案 1 :(得分:3)
与德米特里·金兹堡的精神相同,但实施方式不同。
List<C1> list = c1List.stream()
.collect(toMap(c -> c.id, c -> c.c2Set, (set1, set2) -> Stream.concat(set1.stream(), set2.stream()).collect(toSet())))
.entrySet()
.stream()
.map(e -> new C1(e.getKey(), e.getValue()))
.collect(toList());
它使用toMap()
收集器。在List<C1>
,您可以创建一个Map<Integer, Set<C2>>
,将每个C1的ID映射到其集c2Set
。如果您有一个类似的id,则将两个值(即集合)合并到另一个新集合中。
最后,您获得条目集并将每个条目映射到新的C1实例,并将所有实例收集到List中。
(我假设有一个接受Set as参数的构造函数)