如何将 Map<Set<A>, B> 转换为 Map<A, Set<C>>
How to transform a Map<Set<A>, B> into a Map<A, Set<C>>
我在寻找最佳解决方案时遇到问题(这不是 forEach
的多次使用)。
我有对象 A
、B
和 C
。对象 B
包含一组对象 C (i.e.
B->Set`).
有人向我提供 Map<Set<A>, B>
,我需要将其转换为 Map<A,Met<C>>
。
streams 库中是否有提供此特定解决方案的函数?
我暂时想到的解决办法是通过forEach
.
处理这张地图
每次迭代,我都会为地图 Map<A,Set<C>>
中的每个 A
合并 C
组。这是解决方案的概述(里面发生了很多事情,b->c 是发生的事情的快捷方式)。
通过创建 Map<A,Set<C>>
,在每次迭代中,我为每个迭代提供了先前集合的合并。这是解决方案的概要(里面发生了很多事情,b->c 是正在发生的事情的快捷方式)。
Map<A, Set<C>> aSetCMap = new HashMap<>();
for (Map.Entry<Set<A>, B> entry : setABMap.EntrySet()) {
var setA = entry.getKey();
var setC = b.transformC();
setA.forEach(item -> aSetCMap.merge(item, setC,
(current, added) -> Streams.concat(current.stream(), added.stream())
.collect(Collectors.toSet())));
}
我想出的解决方案是一种在 O(n^3) 时间内完成的靠不住的方法。我正在使用它的东西需要高性能,因此我的问题。
您需要展平每个条目 Map.Entry<Set<A>, B>
。并将每个对象 A
与从对象 B
.
中提取的 Set<C>
相关联
之后,通过使用带有三个参数的 Collectors.toMap()
风格来应用 collect()
:一个 keyMapper,一个 valueMapper 和一个 mergeFunction(需要组合 sets 映射到同一个对象 A
)
Map<Set<A>, B> source = getSourceMap();
Map<A, Set<C>> result =
source.entrySet().stream()
.flatMap(entry -> entry.getKey().stream()
.map(a -> Map.entry(a, entry.getValue().getC())))
.collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue,
(set1, set2) -> {set1.addAll(set2); return set1;}));
如果 Set<C>
可以在恒定时间内从对象 B
获得,则上述方法适用。在这里,我依靠你的话:
B
contains set of objects C
如果此操作需要额外处理(我在您的代码中看到一个方法 transformC()
)并在每次调用 transformC()
后创建一个新集,那么对于源映射中的每个条目它必须只执行一次(即使用多行 lambda)。
关于 stream-based 解决方案的时间复杂度,您对性能提升的期望 不会 实现(假设迭代解决方案没有逻辑错误。
注意,将可变对象 (Set<A>
) 用作键的情况本身就是恶性的,可能会导致不正确的行为。
我在寻找最佳解决方案时遇到问题(这不是 forEach
的多次使用)。
我有对象 A
、B
和 C
。对象 B
包含一组对象 C (i.e.
B->Set`).
有人向我提供 Map<Set<A>, B>
,我需要将其转换为 Map<A,Met<C>>
。
streams 库中是否有提供此特定解决方案的函数?
我暂时想到的解决办法是通过forEach
.
每次迭代,我都会为地图 Map<A,Set<C>>
中的每个 A
合并 C
组。这是解决方案的概述(里面发生了很多事情,b->c 是发生的事情的快捷方式)。
通过创建 Map<A,Set<C>>
,在每次迭代中,我为每个迭代提供了先前集合的合并。这是解决方案的概要(里面发生了很多事情,b->c 是正在发生的事情的快捷方式)。
Map<A, Set<C>> aSetCMap = new HashMap<>();
for (Map.Entry<Set<A>, B> entry : setABMap.EntrySet()) {
var setA = entry.getKey();
var setC = b.transformC();
setA.forEach(item -> aSetCMap.merge(item, setC,
(current, added) -> Streams.concat(current.stream(), added.stream())
.collect(Collectors.toSet())));
}
我想出的解决方案是一种在 O(n^3) 时间内完成的靠不住的方法。我正在使用它的东西需要高性能,因此我的问题。
您需要展平每个条目 Map.Entry<Set<A>, B>
。并将每个对象 A
与从对象 B
.
Set<C>
相关联
之后,通过使用带有三个参数的 Collectors.toMap()
风格来应用 collect()
:一个 keyMapper,一个 valueMapper 和一个 mergeFunction(需要组合 sets 映射到同一个对象 A
)
Map<Set<A>, B> source = getSourceMap();
Map<A, Set<C>> result =
source.entrySet().stream()
.flatMap(entry -> entry.getKey().stream()
.map(a -> Map.entry(a, entry.getValue().getC())))
.collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue,
(set1, set2) -> {set1.addAll(set2); return set1;}));
如果 Set<C>
可以在恒定时间内从对象 B
获得,则上述方法适用。在这里,我依靠你的话:
B
contains set of objectsC
如果此操作需要额外处理(我在您的代码中看到一个方法 transformC()
)并在每次调用 transformC()
后创建一个新集,那么对于源映射中的每个条目它必须只执行一次(即使用多行 lambda)。
关于 stream-based 解决方案的时间复杂度,您对性能提升的期望 不会 实现(假设迭代解决方案没有逻辑错误。
注意,将可变对象 (Set<A>
) 用作键的情况本身就是恶性的,可能会导致不正确的行为。