比较两张地图


69

我有两个地图声明为Map<String, Object>。在Object这里可以是另一个Map<String, Object>(依此类推)。我想检查两个地图是否完全相同,但不知道其深度。除了使用递归,我还可以比较toString()每个地图上被调用的输出吗?还是有比较简单的方法比较地图?


22
可能对其他人来说这是愚蠢的,但这对您来说很重要,因此最终它会变得受人尊敬和重要。
Kick Buttowski14年

3
toString()如果地图条目以不同顺序出现,则使用将失败,这是完全可能的。
Teepeemm 2014年

@ user3580294如果一个映射是a TreeMap,另一个映射是a HashMap(在我的回答中包括示例:-)。
最佳黄金

1
.toString()本身将具有递归行为。你为什么讨厌它?

@ maythesource.com是的,在我发布评论后不久就意识到了我评论的缺陷,但是由于某种原因没有收到您的回复通知...无论如何,我最初以为OP正在谈论HashMap实例,因为我的阅读技能很糟糕,但是后来意识到,即使对于我所说的那些,这也不是真的。在那之后,我意识到OP仅具有Map实例。无论如何,您是正确的。
awksp 2014年

Answers:


70

快速回答

您应该使用该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 。更正式地说,如果m1.entrySet()。equals(m2.entrySet()),则两个映射m1和m2表示相同的映射。这样可确保equals方法可在Map接口的不同实现中正常工作。

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

比较两种不同类型的地图

toString比较a时失败TreeMapHashMapequals正确地比较了内容。

码:

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}

lambda表达式会使条件更好吗?
Kick Buttowski

@Kick Buttowski你是什么意思?
最佳总理

您说回答这个问题很复杂,并且由于我尝试用Java学习Lambda表达式,所以我想知道使用lambda表达式是否有帮助?
Kick Buttowski

@KickButtowski我还没有使用Java 8,但是可以确定是否将地图弄平了:)
noMAD

2
应该强调的是,即使两个映射都具有相同的类型,即两个都是HashMaps,也无法保证迭代顺序相同,因此比较toString()结果将失败。遇到的顺序是一个实现细节,它取决于键的哈希码当前容量(即使大小相同,可能也会有所不同),此外,链接存储在同一数组元素中的条目(也称为具有哈希冲突)顺序取决于地图的历史记录,即插入顺序或是否删除了其他键等
。– Holger

11

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

通过比较toString()建议的每个地图也可以得到相同的结果,但是使用equals()是一种更直观的方法。

可能不是您的特定情况,但是如果您将数组存储在地图中,则可能会有些棘手,因为必须逐个值比较它们,或者使用Arrays.equals()。有关此的更多详细信息,请参见此处

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.