繁体   English   中英

hashCode实现“等于任何一些字段相等”

[英]hashCode implementation for “equals any of some fields are equal”

如果其中一个字段相等,我希望特定类的对象相等。 如何为这样的类编写一致的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)和等等。 我承认这种方法并不是特别强大。

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

您不能使用任何字段来实现hashCode因为该字段始终不等于。

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

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

实施可能是:

public int hashCode() {
    return 0;
}

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

public int hashCode() {
    return 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() 解决方案 ......

从你说它不是一个好习惯的角度开始,我认为你可以实现这一点的一种方法是维护对每个对象中另一个对象的引用,并根据字段的相等性计算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());
    }
}

解决这个问题的替代解决方案也是受欢迎的。

我永远不会搞乱equals()和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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM