4

我正在尝试构建一种方法来合并两个地图的内容。我在这里环顾了一段时间,看不出有什么方法可以使它通用。如果可能的话,我想避免@SuppressWarnings("unchecked")注释。我有一个嵌套的映射结构,其中键是字符串,值是更多东西的映射,此结构中的“叶”节点始终是集合。所以在大多数情况下,我有两个结构如下的地图:

Map<String,Map<String,Set<String>>>

我想合并两个映射,以便最终得到两者的联合,并且这两个映射中的任何公共键都在结果映射中表示,其值是两个映射中两个值的合并。在代码中,这是我迄今为止所拥有的:

@SuppressWarnings("unchecked")
public Map<String,Object> merge(final Map<String, Object> map1,
                                final Map<String, Object> map2) {

    final Map<String,Object> merged = new HashMap<String,Object>(map1);
    for (final Map.Entry<String,Object> entry : merged.entrySet()) {
        final String key = entry.getKey();
        final Object value = entry.getValue();
        if (map2.containsKey(key)) {
            final Object value2 = map2.get(key);
            if ((value instanceof Map) && (value2 instanceof Map)) {
                merged.put(key, merge((Map<String, Object>) value, (Map<String, Object>) value2));
            } else if ((value instanceof Set) && (value2 instanceof Set)) {
                final Set<Object> set = new HashSet<Object>((Set<Object>)value);
                set.addAll((Set<Object>) value2);
                merged.put(key, set);
            } else {
                // throw up, should only ever be a map or a set
            }
       }
    }

    for (final String key : map2.keySet()) {
        if (!merged.containsKey(key)) {
            merged.put(key, map2.get(key));
        }
    }

    return merged;
}

它完成了这项工作,但我对它不满意,因为使用它你正在铸造一大堆东西,它还对你正在使用的 Set 和 Map 实现做出假设。鉴于我知道我一直在处理字符串到某物的映射,其中某物是一组字符串或另一个字符串到某物的映射,我试图弄清楚如何使用泛型来规范这一点。我在这里摆弄了其他答案中显示的类似但不完全相同的方法,例如方法签名,例如:

    public <T extends Map<String,T>> Map<String,T> merge(final T map1, final T map2)

但这并没有奏效,因为递归调用不喜欢我尝试输入Map<String,Set<String>>.

我承认以前不需要深入研究泛型。非常感谢任何指导。

4

1 回答 1

4

重新假设你正在使用的Set实现Map:如果你总是创建新的地图和集合,你至少可以避免这种情况——这有一个很好的附带好处,即如果有人修改了原始集合,它不会搞砸你的合并版本.

至于您的主要观点,没有强制转换就无法在 Java 中执行此操作。泛型无法帮助您,因为在运行时,编译器只知道

Map<String, Object> merge ( Map<String, ?> map1, Map<String, ?> map2 )

您实际上并不知道代码中是否有 a Map<String, Set<String>>、 aMap<String, Map<String, Set<String>>或 aMap<String, Object>某些值在哪里,Set<String>而其他值在哪里Map<String, Set<String>>——在第三种情况下它仍然有效,只要对于每个键,两个映射都具有相同的值类型。

矛盾的是,摆脱警告的最佳方法是摆脱泛型,此时,仅使用运行时可用的信息(Map或者Set,我们不在乎),所有强制转换都是安全的:

public Map<Object, Object> merge ( Map<?, ?> map1, Map<?, ?> map2 )
{
    Map<Object, Object> merged = new HashMap<Object, Object>();
    if ( map1 == null || map2 == null )
    {
        if ( map1 != null )
        {
            merged.putAll( map1 );
        }
        if ( map2 != null )
        {
            merged.putAll( map2 );
        }
        return merged;
    }

    Set<Object> allKeys = new HashSet<Object>();
    allKeys.addAll( map1.keySet() );
    allKeys.addAll( map2.keySet() );

    for ( Object key : allKeys )
    {
        Object v1 = map1.get( key );
        Object v2 = map2.get( key );
        if ( v1 instanceof Set || v2 instanceof Set )
        {
            Set<Object> newSet = new HashSet<Object>();
            if ( v1 instanceof Set )
            {
                newSet.addAll( (Set) v1 );
            }
            if ( v2 instanceof Set )
            {
                newSet.addAll( (Set) v2 );
            }
            merged.put( key, newSet );
        }
        else if ( v1 instanceof Map || v2 instanceof Map )
        {
            Map<?, ?> m1 = v1 instanceof Map ? (Map<?, ?>) v1 : null;
            Map<?, ?> m2 = v2 instanceof Map ? (Map<?, ?>) v2 : null;
            merged.put( key, merge( m1, m2 ) );
        }

    }
    return merged;
}
于 2013-04-24T00:12:36.983 回答