比较两个地图
我有两个地图声明为Map<String, Object>
。 这里的Object
可以是另一个Map<String, Object>
(依此类推)。 我想检查两张地图是否完全一样,不知道它们的深度。 可以使用recursion来比较每个映射上调用的toString()
的输出吗? 还是有比较简单的方法来比较地图?
快速回答
您应该使用equals
方法,因为这是执行比较你想要的。 toString()
本身就像equals
一样使用迭代器,但是效率更低。 另外,正如@Teepeemm所指出的,String受元素顺序(基本上是迭代器返回顺序)的影响,因此不能为2个不同的地图提供相同的输出(特别是如果我们比较两个不同的地图)。
注意/警告 :你的问题和我的答案假定实现地图接口尊重的类预期toString
和equals
行为。 默认的java类是这样做的,但是需要检查自定义的map类来validation预期的行为。
请参阅: http : //docs.oracle.com/javase/7/docs/api/java/util/Map.html
boolean equals(Object o)
将指定的对象与此映射进行比较以获得相等性。 如果给定的对象也是一个映射,并且这两个映射表示相同的映射,则返回true。 更正式地说, 如果m1.entrySet()。equals(m2.entrySet()) ,则两个映射m1和m2表示相同的映射 。 这确保了equals方法在Map接口的不同实现之间正常工作。
Java源代码实现(java.util.AbstractMap)
此外,java本身负责遍历所有元素,并进行比较,因此您不必这样做。 看看HashMap
等类所使用的AbstractMap
的实现:
// 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; }
比较两种不同types的地图
toString
在比较TreeMap
和HashMap
时失败,尽pipeequals
会正确地比较内容。
码:
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()); }
输出:
Are maps equal (using equals):true Are maps equal (using toString().equals()):false Map1:{2=whatever2, 1=whatever1} Map2:{1=whatever1, 2=whatever2}
只要您覆盖地图中包含的每个键和值的equals()
,则m1.equals(m2)
应该可靠,以检查地图是否相等。
同样的结果也可以通过比较每个地图的toString()
来得到,但是使用equals()
是一个更直观的方法。
可能不是你的具体情况,但是如果你在地图中存储数组,可能会有点棘手,因为它们必须按值进行比较,或者使用Arrays.equals()
。 关于这个的更多细节请看这里 。
我做了这个testing,工作:
import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import org.junit.Assert; import org.junit.Test; ... private void put(String key, String value, Map<String,String> map1, Map<String, String> map2){ map1.put(key, value); map2.put(key, value); } @Test public void testEqualsMap() throws Exception { Map<String, String> hashmap = new HashMap<String, String>(); Map<String, String> treemap = new TreeMap<String, String>(); put("voltage", "110/220", treemap, hashmap); put("color", "blue", treemap, hashmap); Assert.assertTrue(hashmap.equals(treemap)); treemap.put("color", "red"); Assert.assertFalse(hashmap.equals(treemap)); }