How to receive difference of maps in java?

前端 未结 7 950
不知归路
不知归路 2021-02-05 09:36

I have two maps:

Map map1;
Map map2;

I need to receive difference between these maps. Does exist ma

相关标签:
7条回答
  • 2021-02-05 10:09

    If I understood well you are trying to calculate symmetric difference beetween the two maps entry sets.

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

    Considering the awkward behavior you mentioned, let's take a closer look at the above code behavior. For example if we take the numerical example from the above given link:

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

    After you calculate the difference as shown, the output:

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

    gives:

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

    which is the correct result. But, if we do it like this:

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

    the same algorithm gives the following output:

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

    which is not correct (and might be described as awkward :) )

    In the first example the map is filled with int values wich are automatically boxed to Integer values.

    The class Integer has its own implementation of equals and hashCode methods.

    The class CustomInteger does not implement these methods so it inherits them from the omnipresent Object class.

    The API doc for the removeAll method from the Set interface says the following:

    Removes from this set all of its elements that are contained in the specified collection (optional operation). If the specified collection is also a set, this operation effectively modifies this set so that its value is the asymmetric set difference of the two sets.

    In order to determine which elements are contained in both collections, the removeAll method uses the equals method of the collection element.

    And that's the catch: Integer's equals method returns true if the two numeric values are the same, while Object's equals method will return true only if it is the same object, e.g. :

    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
    

    If it's still a bit fuzzy I strongly suggest reading the following tutorials:

    • Learning the Java language
    • Collections
    0 讨论(0)
提交回复
热议问题