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

发布于 2024-07-12 07:41:02 字数 542 浏览 7 评论 0原文

我有一个类,其相等性基于两个字段,如果其中一个字段相等,则该类型的对象被视为相等。 我如何为这样的 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;
}

i have a class whose equality is based on 2 fields such that if either one is equal then the objects of this type are considered equal. how can i write a hashCode() function for such an equals() so that the general contract of hashCode being equal when equals returns true is preserved?

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

how do i write a hashCode() function for this class? and i want to avoid the trivial case here of returning a constant like so:

public int hashCode() {
  return 1;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

只是一片海 2024-07-19 07:41:02

我认为不存在重要的哈希码。 此外,您的 equals() 违反了一般合同 API 中声明 --- 它不可传递

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

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

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


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

声明哈希码必须是平凡的常量函数。

证明:设 (a,b) 和 (c,d) 是具有不同哈希码的两个对象,即 h(a,b) ≠ h(c,d)。 考虑对象(a,d)。 根据OP的定义,(a,d)等于(a,b),而(a,d)等于<代码>(c,d)。 它遵循哈希码合约 h(a,d) = h(a,b) = h(c,d); 矛盾。

I don't think a nontrivial hashcode exists. Also, your equals() violates the general contract as stated in the API --- it is not transitive:

(1,2) equals (1,3)

(4,3) equals (1,3)

But (4,3) is not equal to (1,2).


For the sake of completeness, I present to you the Skeet-Niko proof =)

Claim: The hashcode must be the trivial constant function.

Proof: Let (a,b) and (c,d) be two objects with distinct hashcodes, i.e. h(a,b) ≠ h(c,d). Consider the object (a,d). By the OP's definition, (a,d) is equal to (a,b), and (a,d) is equal to (c,d). It follows from the hashcode contract that h(a,d) = h(a,b) = h(c,d); a contradiction.

单身狗的梦 2024-07-19 07:41:02

好的,在您的场景中,暂时忽略 API 要求,不存在非常量哈希函数

想象一下,有一个哈希函数,其

(a,b)、(a,c)、b!=c 具有不同的值,然后哈希(a,b)!=哈希(a,c),尽管(a,b)=(a,c)。

类似地,(b,a) 和 (c,a) 必须发出相同的 hashCode。

我们将哈希函数称为 h。 我们发现:

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

因此,唯一满足您要求的 hashFunction 是常量。

Ok, in your scenario, ignoring the API requirements for a second, there is no non-constant hash function

Imagine there were a hashfunction which has different values for

(a,b), (a,c), b!=c, then hash(a,b) != hash(a,c), eventhough (a,b) = (a,c).

Similarly, (b,a) and (c,a) must emit the same hashCode.

Let's call our hash-function h. We find:

h(x,y) = h(x,w) = h(v,w) forall x,y,v,w.

Hence, the only hashFunction that does what you want is constant.

野却迷人 2024-07-19 07:41:02

我很确定扎克是对的——没有不平凡的哈希码可以做到这一点。

伪证明:

考虑任意两个不相等的值,X=(id1, name1) 和 Y=(id2, name2)。

现在考虑 Z=(id2,name1)。 这等于 X 和 Y,因此必须与 X 和 Y 具有相同的哈希码。因此 X 和 Y 必须具有相同的哈希码 - 这意味着所有值必须具有相同的哈希码。

你陷入奇怪的境地是有原因的——你打破了平等的传递性。 事实上 X.equals(Z) 和 Z.equals(Y) 应该意味着 X.equals(Y) - 但事实并非如此。 您对平等的定义不适合正常的平等契约。

I'm pretty sure Zach's right - there's no non-trivial hashcode to do this.

Pseudo-proof:

Consider any two non-equal values, X=(id1, name1) and Y=(id2, name2).

Now consider Z=(id2,name1). This is equal to both X and Y, so must have the same hashcode as both X and Y. Therefore X and Y must have the same hashcode - which means all values must have the same hashcode.

There's a reason why you've got into a weird situation - you're breaking the transitive nature of equals. The fact that X.equals(Z) and Z.equals(Y) should mean that X.equals(Y) - but it doesn't. Your definition of equality isn't suitable for the normal contract of equals.

思慕 2024-07-19 07:41:02

我认为你不能。 原因是,您的 equals() 方法不具有传递性。

传递性意味着对于三个非空的x、y、z,如果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.equals(z) 为 false。 每个 equals() 方法都应该具有此属性,请参阅 Java API 文档。

回到散列函数:每个函数都会产生一个由 f(x)==f(y) 定义的等价函数。 这意味着,如果您对函数值的比较感兴趣,并希望它在 x==y 时返回 true(可能在其他情况下),您将收到传递关系,这意味着您有至少考虑对象等价性的传递闭包。 在你的情况下,传递闭包是微不足道的关系(一切都等于任何东西)。 这意味着您无法通过任何函数来区分不同的对象。

I think you can't. The reason is, your equals() method is not transitive.

Transitivity means for three non-null x, y, z, if x.equals(y), y.equals(z), then x.equals(z). In your example, an object x={id: 1, name: "ha"}, y={id: 1, name: "foo"}, z={id: 2, name: "bar"} have this property (x.equals(y) and y.equals(z)). However, x.equals(z) is false. Every equals() method should have this property, see the Java API docs.

Back to hashing functions: Every function yields an equivalence defined by f(x)==f(y). That means if you are interested in comparison of the function values and want it to return true if x==y (and possibly in other cases), you'll receive a transitive relation, which means you have to consider at least a transitive closure of objects' equivalence. In your case, the transitive closure is the trivial relation (everything equals to anything). Which means you can't distinguish different objects by any function.

似最初 2024-07-19 07:41:02

您是否有意将相等定义为 id 相等或名称相等。“OR”不应该是“AND”吗?

如果您的意思是“AND”,那么您的哈希码应该使用与 equals() 相同或更少的字段(但切勿使用 equals 未使用的字段)来计算。

如果您的意思是“OR”,那么您的 hashgcode 不应在其 hashcode 计算中包含 id 或 name,这实际上没有意义。

Have you intentionally defined equality as when ids are equal OR names are equal.. Shouldnt the "OR" be a "AND" ?

If you meant "AND" then your hashcode should be calculated using the very same or less (but never use fields not used by equals) fields you are by equals().

If you meant "OR" then you r hashgcode should not include id or name in its hashcode calculation which doesnt really make sense.

痴者 2024-07-19 07:41:02

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

--

我将使用 commons-lang jar。

对成员 hashCode 进行异或应该有效。 因为他们应该正确实现 hashCode() 和 equals() 。

但是,如果不保护 hashCode,您的代码可能会出错。
一旦经过哈希处理,就不应更改。 应该防止这种情况发生。

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

EDIT: I didn't read the question carefully.

--

I'll use commons-lang jar.

XOR the members hashCode should works. As they should implements hashCode() and equals() correctly.

However, your code may wrong if you don't protect your hashCode.
Once it was hashed, it should not be changed. It should be prevented from being happen.

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

or

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

or

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();
   }
}
苏佲洛 2024-07-19 07:41:02

重新阅读问题后。

当其中一个字段更新时,您可以自动填写另一个字段。

--

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

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

编辑 2:

问题上的代码可能会错误,因为除非您同时设置了 id 和 id,否则它总是返回 true。 姓名。

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

After re-read the question.

You may auto-complete the another field when one of them being updated.

--

EDIT: My code may say better then my English.

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

EDIT 2:

The code on the question may wrong as will always return true unless you've set both the id & name.

If you really want a method that do partial equals, create you own API that named "partialEquals()".

何以畏孤独 2024-07-19 07:41:02

最简单的方法是对每个单独字段的哈希码进行异或。 这在某些情况下有轻微的丑陋(例如,在 X,Y 坐标中,当您翻转 X 和 Y 时,它会导致具有相等哈希值的潜在不良情况),但总体而言非常有效。 如果需要提高效率,请根据需要进行调整以减少碰撞。

The simplest route is to XOR the hashcodes of each individual field. This has minor ugliness in some situations (e.g. in X,Y coordinates, it causes the potentially poor situation of having equal hashes when you flip X and Y), but is overall pretty effective. Tweak as needed to reduce collisions if necessary for efficiency.

泪是无色的血 2024-07-19 07:41:02

这个怎么样

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

这个函数应该总是返回一个“有效”散列...

编辑:刚刚注意到你使用“或”而不是“和”:P好吧我怀疑这个问题有什么好的解决方案...

How about this

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

The function should allways return a "valid" hash...

Edit: just noticed that you use "or" not "and" :P well i doubt there is any good solution to this problem...

怎么样

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

How about

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文