如何在java中获取地图的差异?

use*_*818 18 java collections map set set-difference

我有两张地图:

Map<String, Object> map1;
Map<String, Object> map2;
Run Code Online (Sandbox Code Playgroud)

我需要收到这些地图之间的差异.是否存在可能是apache utils如何获得这种差异?现在似乎需要获取每个地图的入口集,并找到diff1 = set1 - set2和diff2 = set2- set1.创建汇总map = diff1 + diff2之后看起来非常笨拙.还存在另一种方式吗?谢谢.

Koe*_*err 38

如何谷歌番石榴

Maps.difference(map1,map2)
Run Code Online (Sandbox Code Playgroud)

  • @ user710818你不会后悔 - 这是一个很棒的图书馆 (6认同)
  • @ user710818您应该在项目中使用它 (3认同)
  • 谢谢.我想过番石榴,但是为了这个需要在项目中引入新的库,不要这样做. (2认同)
  • 如果使用简单的数学运算,它可以在java中实现.没有必要为此引入其他库. (2认同)

Vla*_*iev 20

这是一个简单的代码片段,您可以使用它而不是大量的Guava库:

public static <K, V> Map<K, V> mapDifference(Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right) {
    Map<K, V> difference = new HashMap<>();
    difference.putAll(left);
    difference.putAll(right);
    difference.entrySet().removeAll(right.entrySet());
    return difference;
}
Run Code Online (Sandbox Code Playgroud)

查看整个工作示例

  • 需要注意的一件事是,这将丢失任何仅更改值的条目,因此这是键上的差异 (2认同)

lin*_*ski 6

如果我理解得很好,你试图计算两个地图入口集之间的对称差异.

Map<String, Object> map1;
Map<String, Object> map2;

Set<Entry<String, Object>> diff12 = new HashSet<Entry<String, Object>>(map1.entrySet());
Set<Entry<String, Object>> diff21 = new HashSet<Entry<String, Object>>(map2.entrySet());
Set<Entry<String, Object>> result;

diff12.removeAll(map2.entrySet());
diff21.removeAll(map1.entrySet());
diff12.addAll(diff21);
Run Code Online (Sandbox Code Playgroud)

考虑到你提到的尴尬行为,让我们仔细看看上面的代码行为.例如,如果我们从上面给出的链接中获取数值示例:

Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("a", 1);
map1.put("b", 2);
map1.put("c", 3);
map1.put("d", 4);

Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("a", 1);    
map2.put("d", 4);
map2.put("e", 5);
Run Code Online (Sandbox Code Playgroud)

在计算出差异后,输出如下:

System.out.println(Arrays.deepToString(diff12.toArray()));
Run Code Online (Sandbox Code Playgroud)

得到:

[e=5, c=3, b=2]
Run Code Online (Sandbox Code Playgroud)

这是正确的结果.但是,如果我们这样做:

public class CustomInteger {
    public int val;

    public CustomInteger(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return String.valueOf(val);
    }        
}   

map1.put("a", new CustomInteger(1));
map1.put("b", new CustomInteger(2));
map1.put("c", new CustomInteger(3));
map1.put("d", new CustomInteger(4));

map2.put("a", new CustomInteger(1));    
map2.put("d", new CustomInteger(4));
map2.put("e", new CustomInteger(5));
Run Code Online (Sandbox Code Playgroud)

相同的算法给出以下输出:

[e=5, a=1, d=4, d=4, b=2, a=1, c=3]
Run Code Online (Sandbox Code Playgroud)

这是不正确的(可能被描述为尴尬:))

在第一个示例中,地图填充了int值,这些值将自动加框为Integer值.

Integer类有自己的equalshashCode方法实现.

CustomInteger类不实现这些方法,因此它从无所不在的Object类继承它们.

对于该API文档RemoveAll方法Set接口说以下内容:

从此集合中删除指定集合中包含的所有元素(可选操作).如果指定的集合也是一个集合,则此操作会有效地修改此集合,以使其值为两个集合的不对称集合差异.

为了确定两个集合中包含哪些元素,removeAll方法使用集合元素的equals方法.

这就是问题:如果两个数值相同,则Integer的equals方法返回true,而Object的equals方法只有在它是同一个对象时才返回true ,例如:

Integer a = 1; //autoboxing
Integer b = new Integer(1);
Integer c = 2;

a.equals(b); //  true
a.equals(c); //  false

CustomInteger d = new CustomInteger(1);
CustomInteger e = new CustomInteger(1);
CustomInteger f = new CustomInteger(2);

d.equals(e); //false
d.equals(f) // false

d.val == e.val //true
d.val == f.val //false
Run Code Online (Sandbox Code Playgroud)

如果它仍然有点模糊,我强烈建议阅读以下教程: