比较两张地图

时间:2014-07-17 22:29:32

标签: java dictionary

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

3 个答案:

答案 0 :(得分:58)

快速回答

您应该使用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和m2代表相同   映射如果m1.entrySet()。equals(m2.entrySet())。这确保了   equals方法适用于不同的实现   地图界面。

Java Source(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;
    }

比较两种不同类型的地图

在比较toStringTreeMap时,{p> HashMap失败了,尽管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}

答案 1 :(得分:11)

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

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

可能不是您的具体情况,但如果您在地图中存储数组,可能会有点棘手,因为必须逐个值或使用Arrays.equals()进行比较。有关此内容的详细信息,请参阅here

答案 2 :(得分:0)

比较两个地图:

声明:

let personToString p =
    sprintf "%s %s [%d] - Salary %d" p.first_name p.last_name p.age p.salary_hour
    
let employeeToString e =
    match e with
    | Administrator a -> sprintf "Administrator: %s" (personToString a)
    | OfficeWorker o -> sprintf "Office: %s" (personToString o)
    | WarehouseWorker w -> sprintf "Warehouse: %s" (personToString w)
    
emps |> List.iter (fun e -> employeeToString e |> printfn "%s")

使用:

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

by y.