比对两张地图

我有两张地图申报为 Map<String, Object>。这里的 Object可以是另一个 Map<String, Object>(以此类推)。我想在不知道深度的情况下检查两张地图是否完全相同。我可以不使用递归来比较每个映射上调用的 toString()的输出吗?还是有比较地图更简单的方法?

112892 次浏览

只要在映射中包含的每个键和值上覆盖 equals(),那么 m1.equals(m2)应该可以检查映射是否相等。

同样的结果也可以通过比较每张地图的 toString()来获得,但是使用 equals()是一种更直观的方法。

可能不是您特定的情况,但是如果您在映射中存储数组,可能会有点棘手,因为它们必须按值进行比较,或者使用 Arrays.equals()。关于这一点的更多细节见 给你

快速回答

您应该使用 equals方法,因为实现这个方法是为了执行所需的比较。toString()本身也使用类似于 equals的迭代器,但它是一种效率更低的方法。此外,正如@Teepeemm 指出的,toString受到元素顺序(基本上是迭代器返回顺序)的影响,因此不能保证为2个不同的映射提供相同的输出(特别是如果我们比较两个不同的映射)。

注意/警告 : 您的问题和我的答案假设实现映射接口的类尊重预期的 toStringequals行为。默认的 Java 类这样做,但是需要检查自定义映射类以验证预期的行为。

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

boolean equals(Object o)

将指定对象与此映射进行比较以求相等。返回 true 如果给定的对象也是一个 map,并且两个 map < strong > 表示相同 映射 . 更正式地说,两个映射 m1和 m2表示相同 如果 m1.entrySet () . equals (m2.entrySet ()) 则映射 方法的不同实现之间正常工作 地图界面。

在 Java 源中实现(Java.util.AbstractMap)

另外,Java 本身负责遍历所有元素并进行比较,这样您就不必这样做了。看一下 AbstractMap的实现,它被诸如 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;
}

比较两种不同类型的地图

当比较 TreeMapHashMap时,toString失败得很惨,尽管 equals确实正确地比较了内容。

密码:

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}

比较两张地图:

声明:

enum Activity{
ADDED,
REMOVED,
MODIFIED
}


@Data
@NoArgsConstructor
@AllArgsConstructor
static class FileStateRow {
String key;
String value;
Activity activity;
}


BiFunction<Map<String, Object>, Map<String, Object>, Map<String, FileStateRow>>
mapCompare = (newMap, oldMap) -> {
Map<String, FileStateRow> resMap = new HashMap<>();
newMap.forEach((k, v) -> {
if (!oldMap.containsKey(k)) {
System.out.println("newMap key:" + k + " is missing in oldMap - ADDED");
resMap.put(k, new FileStateRow(k, (String) v, Activity.ADDED));
} else {
if (oldMap.get(k) != null && !oldMap.get(k).equals(v)) {
System.out.println("newMap value change for key:" + k + ", old:" + oldMap.get(k) + ", new " + v);
resMap.put(k, new FileStateRow(k, (String) v, Activity.MODIFIED));
}
}
});


oldMap.forEach((k, v) -> {
if (!newMap.containsKey(k)) {
System.out.println("newMap key:" + k + " is missing in oldMap");
resMap.put(k, new FileStateRow(k, (String) v, Activity.REMOVED));
}
});
return resMap;
};

用途:

Map<String, Object> map1 = .. // initiate and put values in..
Map<String, Object> map2 = .. // initiate and put values in..


// compare...
Map<String, FileStateRow> res = mapCompare.apply(map1, map2);


// print results
res.forEach((k, v) -> {
System.out.println("key:" + k + ",  value " + v);
});

被我。