【问题标题】:Java Differences between maps including valuesJava映射之间的差异,包括值
【发布时间】:2018-05-22 11:03:52
【问题描述】:

我只需要找到两张地图之间的差异,而差异可能是缺少键或键值不同。

我找到了Differences between maps 的一般答案

sources.removeAll(targets) ... leaves only entries in sources that are only in sources, not in target

sources.retainAll(targets) ... leaves only entries that are in both sets

但我不确定它是否比下面的代码更好,因为除了键存在之外我还需要检查值是否不同

    Map<K, V> updatedMap = new EnumMap<>(K.class);
    for (Map.Entry<K, V> finalSet : secondMap.entrySet()) {
        K currentKey = finalSet.getKey();
        if (!firstMap.containsKey(currentKey) || firstMap.get(currentKey) != finalSet.getValue()) {
            updatedMap.put(currentKey, finalSet.getValue());
            firstMap.remove(currentKey);
        }
    }
    for (Map.Entry<K, V> currentSet : firstMap.entrySet()) {
        K currentKey = currentSet.getKey();
        if (!secondMap.containsKey(currentKey)) {
            updatedMap.put(currentKey, currentSet.getValue());
        } else if (secondMap.get(currentKey) != currentSet.getValue()) {
            updatedMap.put(currentKey, secondMap.get(currentKey));
        }
    }

他们是找到包括值在内的地图之间差异的更好方法吗?

【问题讨论】:

  • .entrySet() 和使用番石榴 Sets.symmetricDifference 怎么样?
  • @Eugene 我目前不用番石榴,不过你可以回答
  • 在我这样做之前假设leftMap = [{1, "one"}, {2, "two"}]rightMap = [{1, "uno"}, {2, "duo"}] 你有什么预期的结果? [{2=two}, {1=one}, {2=duo}, {1=uno}]?
  • @Eugene 仅来自 secondMap [{2=uno}, {1=duo}] 的值

标签: java


【解决方案1】:

好吧,您可以比较 Entrys 和 Map,因为该类会以您想要的方式覆盖 equals/hashCode。目前尚不完全清楚您想要保留哪些条目,来自左地图右地图或其中任何一个。

例如,这可以通过:

Map<Integer, String> allDifs = 
             Sets.symmetricDifference(left.entrySet(), right.entrySet())
                 .stream()
                 .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

另一方面,如果您只想保留第二个(右)Map 的条目:

 Map<Integer, String> result = 
             Sets.difference(right.entrySet(), left.entrySet())
                 .stream()
                 .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

    System.out.println(result); 

显然你需要guavajava-8 来做这个......

编辑

Collectors.toMap 无法实现您真正想要的,但您可以通过以下方式实现:

    Map<Integer, String> result = new HashMap<>();
    Sets.symmetricDifference(right.entrySet(), left.entrySet())
            .stream()
            .forEachOrdered(x -> {
                String previousValue = result.putIfAbsent(x.getKey(), x.getValue());
                if (previousValue != null) {
                    result.replace(x.getKey(), right.get(x.getKey()));
                }
            });

【讨论】:

  • 我在球衣依赖中有番石榴,所以我会检查一下
  • symmetricDifference 几乎是我需要的,但我参加了测试java.lang.IllegalStateException: Duplicate key NA
  • @user7294900 这可能是因为你有两个Key 作为NA,所以Collectors.toMap 失败......在这种情况下 - 当存在重复键时你想做什么?这是你需要考虑的事情。例如,您可以将结果设为Map&lt;String, List&lt;String&gt;&gt; 以在List 中保留重复的键值,或者始终保留first(或last)一个......所以是哪一个?
  • 第二个地图值应该克服/覆盖
【解决方案2】:

使用 Guava 的Maps.difference(Map, Map)

Their wiki 解释了它的工作原理,但您可以在下面找到问题的解决方案。

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> right = ImmutableMap.of("b", 2, "c", 4, "d", 5);
MapDifference<String, Integer> diff = Maps.difference(left, right);

Map<String, Integer> output = new HashMap<>();
output.putAll(diff.entriesOnlyOnLeft());
output.putAll(diff.entriesOnlyOnRight());
for (Map.Entry<String,MapDifference.ValueDifference<Integer>> e: diff.entriesDiffering().entrySet()) {
  // Java 10 and later : for (var e: diff.entriesDiffering().entrySet())
  output.put(e.getKey(), e.getValue().rightValue());
}
System.out.println(output); // {a=1, c=4, d=5}

【讨论】:

  • 我想要一个只有第二个覆盖第一个地图的差异的地图,在您的示例中为 (a,1 ,c ,4 ,d ,5)
  • 如果你有两张地图,leftright,你只知道left 中的哪些元素是你想要的和不想要的,right 也是如此。您所要做的就是采取entriesOnlyOnLeft()。我会根据您的确切要求更新我的答案。
  • 我使用 java 8(不是 9)并且您的代码无法编译:没有 .entries().getRight()
  • @user7294900 已修复。我做了太多的捷径,没有测试。当前代码已经过全面测试。
  • 谢谢,它有效,但我想我会选择symmetricDifference,这样可以节省更多的代码行
猜你喜欢
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 2017-04-19
  • 1970-01-01
相关资源
最近更新 更多