代码之家  ›  专栏  ›  技术社区  ›  Jack

当equals()基于多个独立字段时,hashCode()方法

  •  9
  • Jack  · 技术社区  · 16 年前

    我有一个类,它的相等性基于两个字段,如果其中一个字段相等,那么这种类型的对象就被认为相等。如何为这样的equals()编写hashCode()函数,以便在equals返回true时保持hashCode相等的一般约定?

    public class MyClass {
      int id;
      String name;
    
      public boolean equals(Object o) {
        if (!(o instanceof MyClass))
          return false;
        MyClass other = (MyClass) o;
        if (other.id == this.id || other.name == this.name)
          return true;
        return false;
      }
    }
    

    如何为这个类编写hashCode()函数?我想避免在这里返回一个常数这样的小例子:

    public int hashCode() {
      return 1;
    }
    
    12 回复  |  直到 16 年前
        1
  •  24
  •   Community CDub    7 年前

    我认为不存在非平凡的哈希代码。还有,你的 equals() stated in the API ---是的 :

    (1,2) 等于 (1,3)

    (4,3) 等于 (1,3)

    (4,3) 不相上下


    为了完整起见,我向您介绍 Skeet - Niko 证明=)

    宣称 hashcode必须是平凡常量函数。

    证据 (a,b) (c,d) 可以是具有不同哈希代码的两个对象,即。 h(a,b) ≠ h(c,d) . 考虑对象 (a,d) . 根据OP的定义, 等于 (a、d) 等于 (c、d) . 它来自于 hashcode contract h(a,d) = h(a,b) = h(c,d)

        2
  •  9
  •   nes1983    16 年前

    (a,b),(a,c),b=c、 然后散列(a,b)!=hash(a,c),eventhough(a,b)=(a,c)。

    类似地,(b,a)和(c,a)必须发出相同的哈希代码。

    对于所有x,y,v,w,h(x,y)=h(x,w)=h(v,w)。

    因此,唯一一个执行所需操作的hashFunction是常量。

        3
  •  6
  •   Jon Skeet    16 年前

    我很确定Zach是对的——没有非平凡的哈希代码可以做到这一点。

    伪证明:

    现在考虑Z=(ID2,NAME1)。这等于X和Y,因此必须具有与X和Y相同的哈希代码。因此X和Y必须具有相同的哈希代码-这意味着 全部的

    你陷入一种奇怪的境地是有原因的——你打破了平等的传递性。X等于(Z)和Z等于(Y)的事实 意味着X等于(Y)-但它不是。你对平等的定义不适用于一般的平等契约。

        4
  •  2
  •   jpalecek    16 年前

    我想你不能。原因是,你的 equals() 方法是不可传递的。

    传递性是指三个非空的x,y,z,if x.equals(y) , y.equals(z) x.equals(z) . 在您的示例中,一个对象 x={id: 1, name: "ha"} , y={id: 1, name: "foo"} , z={id: 2, name: "bar"} (x.equals(y) and y.equals(z)) x、 等于(z) false 等于() 方法应该具有此属性,请参阅JavaAPI文档。

    回到散列函数:每个函数产生一个由 f(x)==f(y) . 这意味着,若您对函数值的比较感兴趣,并且希望它在以下情况下返回true: x==y (也可能在其他情况下),你会收到一个传递关系,这意味着你必须考虑至少一个对象等价的传递闭包。在您的例子中,传递闭包是平凡的关系(一切等于一切)。这意味着你不能用任何函数来区分不同的对象。

        5
  •  2
  •   mP.    16 年前

    如果您的意思是“AND”,那么您的hashcode应该使用与equals()相同或更少的字段(但绝不使用equals未使用的字段)进行计算。

        6
  •  0
  •   Dennis C    16 年前

    编辑:我没有仔细阅读这个问题。

    --

    我将使用commons lang jar。

    XOR成员哈希代码应该可以工作。因为它们应该正确地实现hashCode()和equals()。

    但是,如果不保护哈希代码,代码可能会出错。

    public hashCode(){
       return new AssertionError();
    }
    

     public class MyClass {
       final int id;
       final String name;
       // constructor
     }
    

    public class MyClass {
       private int id;
       private String name;
       boolean hashed=false;
       public void setId(int value){
         if(hashed)throw new IllegalStateException();
         this.id=value;
       }
       public void setName(String value){
         if(hashed)throw new IllegalStateException();
         this.name=value;
       }
       // your equals() here
       public hashCode(){
         hashed=true;
         return new HashCodeBuilder().append(id).append(name).toHashCode();
       }
    }
    
        7
  •  0
  •   Dennis C    16 年前

    当其中一个字段被更新时,您可以自动完成另一个字段。

    编辑:我的代码可能比我的英语更好。

    void setName(String value){
      this.id=Lookup.IDbyName(value);
    }
    void setID(String value){
      this.name=Lookup.NamebyId(value);
    }
    

    编辑2:

    如果您确实想要一个执行部分相等的方法,请创建自己的名为“partialEquals()”的API。

        8
  •  -1
  •   Brian    16 年前

        9
  •  -1
  •   Peter    16 年前

    这个怎么样

    public override int GetHashCode()
    {
        return (id.ToString() + name.ToString()).GetHashCode();
    }
    

    函数应该始终返回一个“有效”哈希。。。

    编辑:刚刚注意到你使用了“或”不“和”:P哦,我怀疑这个问题是否有好的解决方案。。。

        10
  •  -2
  •   Ed Swangren    16 年前

    怎么样

    public override int GetHashCode()
    {
        return id.GetHashCode() ^ name.GetHashCode();
    }