Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing two maps

I have two maps declared as Map<String, Object>. The Object here could be another Map<String, Object> (and so on). I want to check if two maps are exactly the same without knowing their depth. Instead of using recursion can I compare the output of the toString() called on each map? Or is there a simpler way to compare the maps?

like image 478
noMAD Avatar asked Jul 17 '14 22:07

noMAD


People also ask

How can I compare two maps in C++?

C++ Map Library - operator== Functionb The C++ function std::map::operator== tests whether two maps are equal or not.

How do I compare two map keys and values in Java?

If we want to compare hashmaps by keys i.e. two hashmaps will be equals if they have exactly same set of keys, we can use HashMap. keySet() function. It returns all the map keys in HashSet. We can compare the hashset of keys for both maps using Set.

How do you compare two hash sets?

The equals() method of java. util. HashSet class is used verify the equality of an Object with a HashSet and compare them. The list returns true only if both HashSet contains same elements, irrespective of order.

Can a map have 2 values?

A collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.


1 Answers

Quick Answer

You should use the equals method since this is implemented to perform the comparison you want. toString() itself uses an iterator just like equals but it is a more inefficient approach. Additionally, as @Teepeemm pointed out, toString is affected by order of elements (basically iterator return order) hence is not guaranteed to provide the same output for 2 different maps (especially if we compare two different maps).

Note/Warning: Your question and my answer assume that classes implementing the map interface respect expected toString and equals behavior. The default java classes do so, but a custom map class needs to be examined to verify expected behavior.

See: http://docs.oracle.com/javase/7/docs/api/java/util/Map.html

boolean equals(Object o) 

Compares the specified object with this map for equality. Returns true if the given object is also a map and the two maps represent the same mappings. More formally, two maps m1 and m2 represent the same mappings if m1.entrySet().equals(m2.entrySet()). This ensures that the equals method works properly across different implementations of the Map interface.

Implementation in Java Source (java.util.AbstractMap)

Additionally, java itself takes care of iterating through all elements and making the comparison so you don't have to. Have a look at the implementation of AbstractMap which is used by classes such as HashMap:

 // Comparison and hashing      /**      * Compares the specified object with this map for equality.  Returns      * <tt>true</tt> if the given object is also a map and the two maps      * represent the same mappings.  More formally, two maps <tt>m1</tt> and      * <tt>m2</tt> represent the same mappings if      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the      * <tt>equals</tt> method works properly across different implementations      * of the <tt>Map</tt> interface.      *      * <p>This implementation first checks if the specified object is this map;      * if so it returns <tt>true</tt>.  Then, it checks if the specified      * object is a map whose size is identical to the size of this map; if      * not, it returns <tt>false</tt>.  If so, it iterates over this map's      * <tt>entrySet</tt> collection, and checks that the specified map      * contains each mapping that this map contains.  If the specified map      * fails to contain such a mapping, <tt>false</tt> is returned.  If the      * iteration completes, <tt>true</tt> is returned.      *      * @param o object to be compared for equality with this map      * @return <tt>true</tt> if the specified object is equal to this map      */     public boolean equals(Object o) {         if (o == this)             return true;          if (!(o instanceof Map))             return false;         Map<K,V> m = (Map<K,V>) o;         if (m.size() != size())             return false;          try {             Iterator<Entry<K,V>> i = entrySet().iterator();             while (i.hasNext()) {                 Entry<K,V> e = i.next();                 K key = e.getKey();                 V value = e.getValue();                 if (value == null) {                     if (!(m.get(key)==null && m.containsKey(key)))                         return false;                 } else {                     if (!value.equals(m.get(key)))                         return false;                 }             }         } catch (ClassCastException unused) {             return false;         } catch (NullPointerException unused) {             return false;         }          return true;     } 

Comparing two different types of Maps

toString fails miserably when comparing a TreeMap and HashMap though equals does compare contents correctly.

Code:

public static void main(String args[]) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("2", "whatever2"); map.put("1", "whatever1"); TreeMap<String, Object> map2 = new TreeMap<String, Object>(); map2.put("2", "whatever2"); map2.put("1", "whatever1");  System.out.println("Are maps equal (using equals):" + map.equals(map2)); System.out.println("Are maps equal (using toString().equals()):"         + map.toString().equals(map2.toString()));  System.out.println("Map1:"+map.toString()); System.out.println("Map2:"+map2.toString()); } 

Output:

Are maps equal (using equals):true Are maps equal (using toString().equals()):false Map1:{2=whatever2, 1=whatever1} Map2:{1=whatever1, 2=whatever2} 
like image 156
Menelaos Avatar answered Sep 21 '22 14:09

Menelaos