"的hashCode实现等于任何一些字段相等"

时间:2015-02-25 12:32:19

标签: java equals hashcode

如果一个的字段相等,我希望特定类的对象相等。如何为这样的类编写一致的hashCode方法?

(免责声明,因为我知道这不是最佳做法:该类是另一个类的包装器,应该用于Maps中的键。这两个不同的具有一个相等字段的对象将导致相同的Map实际上,每个字段都会自己识别底层对象,但我并不总是对两个可用的对象具有相同的识别字段。我无法控制并因此改变这种模糊的识别机制。解决这个问题也很受欢迎。)

是否有相应实施hashCode()的策略?我只知道在equals中涉及连词(和&&)的实现。如果其中一个字段相等,如何确保hashCode()是相同的?

这是我想要编写一致的hashCode()实现的简化equals方法:

public boolean equals(C other)
{
    return (field1 != null && field1.equals(other.field1))
            || (field2 != null && field2.equals(other.field2))
            || (field3 != null && field3.equals(other.field3));
}

编辑:根据输入数据,不会出现像(1,2,3)那样的情况等于(1,6,7)。只生成对象,使得某些字段可以为null,但不会像示例中那样自相矛盾。简单地说,实际上只有(1,2,3)的组合应该是(1,2,3),(1,null,null),(null,2,null),(1,2,null)和等等。我承认这种方法并不是特别强大。

6 个答案:

答案 0 :(得分:1)

您通常不会仅使用对象类的一个字段来实现equals()hashCode()。每个人都可能会建议你反对它。一般做法是确保您比较所有字段并确保它们全部相等,以便调用.equals()hashCode()使用.equals()来散列这些对象。但是,如果您可以控制自己正在做的事情,则可以简单地使用对象的特定字段的hashCode()并基于该覆盖.equals()和.hashCode()(但同样,不建议)

答案 1 :(得分:1)

您无法使用任何字段来实现hashCode,因为该字段始终不等。

您的hashCode方法需要始终为equals对象返回相同的值。由于equals方法中只有一个字段必须相同,并且它并不总是相同,因此您唯一的选择是在hashCode方法中返回常量。

实施效率低下但是有效且与equals一致。

实施可能是:

public int hashCode() {
    return 0;
}

答案 2 :(得分:1)

似乎唯一的解决方案就是这个

public int hashCode() {
    return 1;
}

答案 3 :(得分:1)

问题中equals()的实施

  public boolean equals(C other) {
    //TODO: you have to check if other != null...

    return (field1 != null && field1.equals(other.field1)) ||
           (field2 != null && field2.equals(other.field2)) ||
           (field3 != null && field3.equals(other.field3));
  }

错误 。实施等于时,我们必须确保

  a.equals(a)
  if a.equals(b) then b.equals(a)
  if a.equals(b) and b.equals(c) then a.equals(c)
第一条规则的

计数器示例所有字段的实例 比较field1, field2, field3)是null

  MyObject a = new MyObject();
  a.setField1(null);
  a.setField2(null);
  a.setField3(null);

  a.equals(a); // <- return false
3d规则的

计数器示例

  MyObject a = new MyObject();
  a.setField1("1"); // field2 and field3 == null 

  MyObject b = new MyObject();
  b.setField1("1"); // field3 == null 
  b.setField2("2"); 

  MyObject c = new MyObject();
  c.setField2("2");  // field1 and field3 == null

  a.equals(b); // true (by field1)
  b.equals(c); // true (by field2)
  a.equals(c); // false!

这就是hashCode()无解决方案的原因......

答案 4 :(得分:0)

从你说它不是一个好习惯的角度开始,我认为你可以实现这一点的一种方法是维护对每个对象中另一个对象的引用,并根据字段的相等性计算hashCode:

public class Test {
    private String field1;
    private Integer field2;

    private Test other;

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public int getField2() {
        return field2;
    }

    public void setField2(int field2) {
        this.field2 = field2;
    }

    public Test getOther() {
        return other;
    }

    public void setOther(Test other) {
        this.other = other;
    }

    @Override
    public int hashCode() {
        if (other == null) {
            return super.hashCode();
        }

        int hashCode = 1;
        if (field1 != null && field1.equals(other.field1)) {
            hashCode = 31 * hashCode + (field1 == null ? 0 : field1.hashCode());
        }
        if (field2 != null && field2.equals(other.field2)) {
            hashCode = 31 * hashCode + field2.hashCode();
        }
        if (hashCode == 1) {
            hashCode = super.hashCode();
        }
        return hashCode;
    }

    public boolean equals(Test other) {
        return (field1 != null && field1.equals(other.field1))
                || (field2 != null && field2.equals(other.field2));
    }

    public static void main(String[] args) {
        Test t1 = new Test();
        t1.setField1("a");
        t1.setField2(1);

        Test t2 = new Test();
        t2.setField1("a");
        t2.setField2(1);

        t1.setOther(t2);
        t2.setOther(t1);

        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());

        t2.setField2(2);
        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());

        t2.setField1("b");
        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());
    }
}

答案 5 :(得分:0)

  

也欢迎采取其他解决方案来解决这个问题。

我永远不会搞乱equals()&amp; hashCode()方法。只需为对象正确实现它们即可。编写满足您需求的自定义比较器,并使用支持自定义比较器(例如TreeSetTreeMap)的集合来执行查找。

示例比较器:

public class SingleFieldMatchComparator implements Comparator<Key> {

  public int compare(Key key1, Key key2) {
    if (key1 == null) {
      if (key2 == null) {
        return 0;
      }
      else {
        return 1;
      }
    } else if (key2 == null) {
      return -1;
    }

    int result = ObjectUtils.compare(key1.getField1(), key2.getField1());
    if (result == 0) {
      return 0;
    }

    result = ObjectUtils.compare(key1.getField2(), key2.getField2());
    if (result == 0) {
      return 0;
    }

    return ObjectUtils.compare(key1.getField3(), key2.getField3());
  }

}

注意:上面使用ObjectUtils来减少代码。如果依赖性不值得,可以用私有方法替换。

示例程序:

Map<Key,Key> myMap = new TreeMap<Key,Key>(new SingleFieldMatchComparator());
Key key = new Key(1, 2, 3);
myMap.put(key, key);
key = new Key(3, 1, 2);
myMap.put(key, key);

System.out.println(myMap.get(new Key(null, null, null)));
System.out.println(myMap.get(new Key(1, null, null)));
System.out.println(myMap.get(new Key(null, 2, null)));
System.out.println(myMap.get(new Key(null, null, 2)));
System.out.println(myMap.get(new Key(2, null, null)));

<强>输出:

null
1,2,3
1,2,3
3,1,2
null