19

我有两张地图:

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

我需要接收这些地图之间的差异。是否存在可能是 apache utils 如何接收这种差异?现在似乎需要获取每个映射的条目集并找到 diff1 = set1 - set2 和 diff2 = set2- set1。create summary map =diff1 + diff2 后看起来很别扭。是否存在另一种方式?谢谢。

4

7 回答 7

44

谷歌番石榴怎么样?:

Maps.difference(map1,map2)
于 2012-10-04T06:52:40.870 回答
22

这是一个简单的片段,您可以使用它来代替大量的 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;
}

查看整个工作示例

于 2014-07-17T21:53:51.883 回答
7

如果我理解得很好,您正在尝试计算两个地图条目集之间的对称差异。

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

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

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

如图所示计算差异后,输出:

System.out.println(Arrays.deepToString(diff12.toArray()));

给出:

[e=5, c=3, b=2]

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

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

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

[e=5, a=1, d=4, d=4, b=2, a=1, c=3]

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

在第一个示例中,地图填充了 int 值,这些值会自动装箱为 Integer 值。

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

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

Set 接口中removeAll 方法的API 文档说明如下:

从此集合中删除指定集合中包含的所有元素(可选操作)。如果指定的集合也是一个集合,这个操作有效地修改了这个集合,使得它的值是两个集合的不对称集合差。

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

这就是问题所在:如果两个数值相同,整数的 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

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

于 2012-10-04T16:16:19.063 回答
4
    Set<Entry<String, Object>> diff = new HashSet<Entry<String, Object>>((map1.entrySet()));
    diff.addAll(map2.entrySet());//Union
    Set<Entry<String, Object>> tmp = new HashSet<Entry<String, Object>>((map1.entrySet()));
    tmp.retainAll(map2.entrySet());//Intersection
    diff.removeAll(tmp);//Diff
于 2012-10-04T06:50:37.500 回答
1

基于Vlad 的示例来处理不同大小的地图

public static <K, V> Map<K, V> mapDiff(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(left.size() <= right.size() ? left.entrySet() : right.entrySet());

        return difference;
    }
于 2017-05-19T20:51:14.440 回答
0

尝试使用番石榴的MapDifference

于 2012-10-04T06:54:10.840 回答
0

简单的方法来做到这一点。如果你想要复杂的方式,你可以改变过滤器来比较值。

    Map<String, Object> map1 = new HashMap<String, Object>() {{
        put("A", "1");
        put("B", "2");
        put("C", "3");
    }};
    Map<String, Object> map2 = new HashMap<String, Object>() {{
        put("A", "1");
        put("B", "2");
        put("D", "3");
    }};
    Map<String, Object> newList = map1.keySet().stream().filter(str -> !map2.containsKey(str)).collect(Collectors.toMap(v -> v, v -> map1.get(v)));
    Map<String, Object> oldList = map2.keySet().stream().filter(str -> !map1.containsKey(str)).collect(Collectors.toMap(v -> v, v -> map2.get(v)));

    System.out.println(newList);
    System.out.println(oldList);
于 2020-06-19T08:20:52.310 回答