java - map 与泛型的递归合并

标签 java generics recursion

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

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

我想合并两个映射,这样我最终得到两个映射的并集,这两个映射中的任何公共(public)键都在结果映射中表示,其值是两个值的合并 map 。在代码中,这是我到目前为止所拥有的:

@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>> 的参数。 .

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

最佳答案

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

至于您的要点,如果不进行强制转换,就无法在 Java 中执行此操作。泛型帮不了你,因为在运行时,编译器只知道

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

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

自相矛盾的是,摆脱警告的最好方法是摆脱泛型,此时,仅使用运行时可用的信息( MapSet ,我们不关心什么), 所有的转换都是安全的:

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;
}

关于java - map 与泛型的递归合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15959733/

相关文章:

java - 将 entrySet 转换为数组

c# - 具有在提供的参数中指定的协变泛型类型的调用方法

使用递归反转堆栈的 C 程序

java - 哪一个更可取以及为什么

java - 如何让 ScrollView 自动滚动到底部?

ios - RxSwift - 无法推断通用参数 'Self'

c - C 中的递归快速排序

java - 使递归函数迭代

java - Android 致命信号错误 11 SIGSEGV 在不同版本的 JellyBean 上

java - 构造函数注入(inject),可测试性设计