生成包含两个地图中包含的 Intersection/Union 个值的地图 Map<X,Set<Y>>

Generate an Map containing an Intersection/Union of values contained in two maps Map<X,Set<Y>>

我有两个映射参数:Map<X,Set<Y>> map1Map<X,Set<Y>> map2

我正在寻找一种方法来编写一种生成 新地图 的方法,其中包含 交点 对于 keys 存在于两个映射中,Union of values 对于 存在于 map1map2.

换句话说,对于任何 X x 如果它在 map1 的域中但不在 map2 的域中,那么它的值将是 map1.get(x) .反之亦然。如果它们都在,那么我想 return 一个集合,它是 map1.get(x)map2.get(x) 的交集。

假设我知道 class X 是哪个,可以使用以下代码完成:

public Map<X,Set<Y>> unifyAndIntersect(Map<X,Set<Y>> map1, Map<X,Set<Y>> map2) {
    Map<X,Set<Y>> combined = new HashMap();
    combined.putAll(map1);
    for(X x : map2.keySet()){
          if(combined.contains(x)){
                Set<Y> res = Sets.newSet();
                res.addAll(map1.get(x));
                res.retainAll(map2.get(x));
                combined.put(x,res);
          }
          else{
                combined.put(x,map2.get(x));
          }
    }
}

但是,我想让这个方法通用,在某种意义上它适用于任何 XY。我尝试使用 Object,但在从我的 class 类型转换为 Object...

时出错

你能告诉我正确的方法是什么吗?

为了声明一个泛型方法,你需要在方法修饰符和return类型(for more information, see)之间提供泛型类型参数<X,Y>。如果没有映射类型 Map<X,Set<Y>> 中的 XY 将不会被视为类型的通用“占位符”,而是类型本身,编译器会抱怨类型 XY 未知。

不要忘记右侧的 diamond <>,在实例化通用 class 时,没有菱形的 new HashMap(); 会创建 row[= 的地图34=]类型。

您提供的代码也存在不一致之处:如果存在键,则两个映射都会将 新集 作为值添加到生成的映射中,但是如果根据您的代码密钥仅包含在其中一个中,则将使用现有的一组。我最好通过为每个值生成一个新集来确保后续对结果映射值的修改不会影响 map1map2 的状态。

public <X,Y> Map<X, Set<Y>> unifyAndIntersect(Map<X,Set<Y>> map1,
                                              Map<X,Set<Y>> map2) {
    
    Map<X, Set<Y>> combined = new HashMap<>();
    for(Map.Entry<X, Set<Y>> entry: map1.entrySet()){
        Set<Y> union = new HashSet<>(entry.getValue());
        if (map2.containsKey(entry.getKey())) {
            union.retainAll(map2.get(entry.getKey()));
        }
        combined.put(entry.getKey(), union);
    }
    return combined;
}

可以使用 Stream 实现相同的逻辑API:

public <X,Y> Map<X, Set<Y>> unifyAndIntersect(Map<X,Set<Y>> map1,
                                              Map<X,Set<Y>> map2) {
    return map1.entrySet().stream()
        .map(entry -> {
            Set<Y> set2 = map2.get(entry.getKey());
            return set2 == null ? Map.entry(entry.getKey(), new HashSet(entry.getValue())) : 
                Map.entry(entry.getKey(),
                entry.getValue().stream()
                    .filter(set2::contains)
                    .collect(Collectors.toSet()));
        })
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}