8

我有以下带有属性键和值的 HashMap:

private HashMap<String, Object> prop_values;

我需要检查它的一个实例是否等于另一个实例。在过去,我只是这样做:

if (prop_values_1.equals(prop_values_2)){
   //  do something
}

这一直有效,直到我获得Object[]了价值。所以,我之前的表达式总是falseHashMap任何Object[]值返回。

所以,我必须实现这个方法:

private boolean isPropValuesEquals(HashMap<String, Object> pv1, HashMap<String, Object> pv2){
   boolean isEquals = true;

   if (pv1 == null || pv2 == null){
      return false;
   }

   if (!pv1.keySet().equals(pv2.keySet())){
      return false;
   }

   for (String key : pv1.keySet()){

      Object cur_pv1 = pv1.get(key);
      Object cur_pv2 = pv2.get(key);

      if (cur_pv1 instanceof Object[]){
         if (cur_pv2 instanceof Object[]){
            isEquals = Arrays.equals((Object[])cur_pv1, (Object[])cur_pv2);
         } else {
            return false;
         }
      } else {
         isEquals = isEquals && cur_pv1.equals(cur_pv2);
      }

      if (!isEquals){
         return false;
      }
   }

   return isEquals;

}

它有效,但它似乎是某种黑客,我不确定这是实现我需要的最佳方式。

所以,这里有两个问题:

  • 为什么 Object[].equals() 与 Arrays.equals() 不一样?似乎很痛苦。

  • 有没有更好的比较方法HashMap<String, Object>,如果值可以是Object[]

4

6 回答 6

3

深层问题是没有办法覆盖equals()数组。为什么它一开始不写为“相同顺序的相等元素”,我不知道。肯定是可以的(除非有一些不为人知的理由不这样做;我想不出任何理由;如果您想检查参考相等性,请使用==,那么有什么工作 equals()危害?)。

你的解决方案是要走的路。只需考虑几个细节:

  • x instanceof Object[]您可以使用代替x.getClass().isArray(),因此它也适用于其他数组,例如int[](它不是的子类Object[])。缺点:您可能必须单独检查 if xis null

  • 如果数组可能包含嵌套数组,请考虑使用Arrays.deepEquals().

原始数组不是Object[]s的演示:

    Object a = new int[1];
    System.out.println("" + (a instanceof Object[])); // false
    System.out.println("" + a.getClass().isArray()); // true

另一个令人头疼的问题是,即使您发现它x是一个数组,您仍然必须分别处理所有不同原始元素类型的情况。在 Java 的类型系统中,没有办法以通用的方式处理它们。当然,如果我们的地图中没有原始数组,则无需处理这种情况。

于 2012-04-19T07:56:31.960 回答
0
why Object[].equals() is not the same as Arrays.equals()?
  • Object1.equals(Object2)与 相同Object1 == Object2,即比较对象的地址(这不是很多开发人员所期望的,但不幸的是这是真的)
  • Arrays.equals(array1, array2) 比较数组的内容。

对于第一点:
这是最抽象的equals()方法,就像所有类都从Object扩展而来一样。String.equals(String)是该方法的覆盖情况,它的工作方式类似于Arrays.equals(array1, array2)。通过这样做,他们为其他开发人员覆盖它铺平了道路,以使其达到最灵活的目的。

最好使用List<Object>而不是Object[],因为它已经准备好所有方法并且可以满足所有目的。

于 2012-04-19T07:59:00.143 回答
0

Object 类的 equals 方法实现了对象上最有区别的可能等价关系;也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象(x == y 的值为 true)时,此方法才返回 true。

于 2012-04-19T07:59:08.577 回答
0

相等可以是不同的东西:它是同一个引用(引用相等)吗?还是有相同的内容?

数组的问题在于它们是可变的。equalshashCode对象必须始终成对出现,并且哈希码不应更改(否则无法在哈希表中找到对象)。因此,普通equals(和hashCode)不能使用数组内容,它们必须依赖数组的另一个(非可变)属性,即引用。

于 2012-04-19T07:50:12.420 回答
0

Joonas 是正确的,但是我会将您的代码重构为以下内容。您可以自己检查以找出差异。

private static boolean isPropValuesEquals(Map<String, Object> pv1, Map<String, Object> pv2) {
    if (pv1 == null || pv2 == null || pv1.size() != pv2.size())
        return false;

    for (Map.Entry<String, Object> entry : pv1.entrySet()) {
        Object cur_pv1 = entry.getValue();
        Object cur_pv2 = pv2.get(entry.getKey());

        if (cur_pv1 == null ^ cur_pv2 == null) // if exactly one is null they're "not equal"
            return false;

        if (cur_pv1 != null) {
            if (cur_pv1.getClass().isArray()) {
                if (Arrays.deepEquals((Object[]) cur_pv1, (Object[]) cur_pv2))
                    return false;
            } else {
                if (!cur_pv1.equals(cur_pv2)) {
                    return false;
                }
            }
        }
    }

    return true;
}
于 2012-04-19T18:13:53.167 回答
0

一个不错但效率低下的实现是

  • 复制地图
  • 用包装列表替换任何数组
  • 使用标准 .equals() 进行比较,这将起作用,因为列表包装器将对底层数组进行适当的深度等于。

代码

public static Map<String, Object> arraysToLists(Map<String, Object> map) {
    Map<String, Object> copy = new HashMap<String, Object>(map);
    for (Entry<String, Object> entry : copy.entrySet()) {
        if (entry.getValue() instanceof Object[]) {
            entry.setValue(Arrays.asList((Object[]) entry.getValue()));
        }
    }
    return copy;
}

public static boolean mapCompare(Map<String, Object> map1,
        Map<String, Object> map2) {
    return arraysToLists(map1).equals(arraysToLists(map2));
}

测试用例

public static void main(String[] args) {
    Map<String, Object> testA = new HashMap<String, Object>();
    testA.put("foo", new Object[] { "1", "2" });
    Map<String, Object> testB = new HashMap<String, Object>();
    testB.put("foo", new Object[] { "1" });
    Map<String, Object> testC = new HashMap<String, Object>();
    testC.put("foo", new Object[] { "1", "2" });

    // demonstrate equals() broken with Object arrays
    System.out.println(testA.equals(testB)); // expect false
    System.out.println(testA.equals(testC)); // expect true

    // demonstrate new function works OK
    System.out.println(mapCompare(testA, testB)); // expect false
    System.out.println(mapCompare(testA, testC)); // expect true
}

输出

false
false
false
true
于 2012-04-19T08:40:13.970 回答