如何将 Map<Set<A>, B> 转换为 Map<A, Set<C>>

How to transform a Map<Set<A>, B> into a Map<A, Set<C>>

我在寻找最佳解决方案时遇到问题(这不是 forEach 的多次使用)。

我有对象 ABC。对象 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>) 用作键的情况本身就是恶性的,可能会导致不正确的行为。