Java 集合的 hashCode 方法的最佳实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/113511/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:25:08  来源:igfitidea点击:

Best implementation for hashCode method for a collection

javahashequalshashcode

提问by Omnipotent

How do we decide on the best implementation of hashCode()method for a collection (assuming that equals method has been overridden correctly) ?

我们如何决定hashCode()集合方法的最佳实现(假设 equals 方法已被正确覆盖)?

采纳答案by dmeister

The best implementation? That is a hard question because it depends on the usage pattern.

最好的实现?这是一个很难的问题,因为它取决于使用模式。

A for nearly all cases reasonable good implementation was proposed in Josh Bloch's Effective Javain Item 8 (second edition). The best thing is to look it up there because the author explains there why the approach is good.

Josh BlochEffective Java第 8 条(第二版)中,几乎所有情况都提出了合理的良好实现。最好的办法是在那里查找,因为作者在那里解释了为什么这种方法是好的。

A short version

一个简短的版本

  1. Create a int resultand assign a non-zerovalue.

  2. For every fieldftested in the equals()method, calculate a hash code cby:

    • If the field f is a boolean: calculate (f ? 0 : 1);
    • If the field f is a byte, char, shortor int: calculate (int)f;
    • If the field f is a long: calculate (int)(f ^ (f >>> 32));
    • If the field f is a float: calculate Float.floatToIntBits(f);
    • If the field f is a double: calculate Double.doubleToLongBits(f)and handle the return value like every long value;
    • If the field f is an object: Use the result of the hashCode()method or 0 if f == null;
    • If the field f is an array: see every field as separate element and calculate the hash value in a recursive fashionand combine the values as described next.
  3. Combine the hash value cwith result:

    result = 37 * result + c
    
  4. Return result

  1. 创建一个int result并分配一个非零值。

  2. 对于方法中测试的每个字段f,通过以下equals()方式计算哈希码c

    • 如果字段 f 是一个boolean:计算(f ? 0 : 1)
    • 如果字段 f 是 a byte, char,shortint: 计​​算(int)f;
    • 如果字段 f 是一个long:计算(int)(f ^ (f >>> 32))
    • 如果字段 f 是一个float:计算Float.floatToIntBits(f)
    • 如果字段 f 是 a doubleDouble.doubleToLongBits(f)像每个 long 值一样计算和处理返回值;
    • 如果字段 f 是一个对象:使用hashCode()方法的结果或 0 如果f == null
    • 如果字段 f 是一个数组:将每个字段视为单独的元素,并以递归方式计算散列值,然后如下所述组合这些值。
  3. 将哈希值cresult

    result = 37 * result + c
    
  4. 返回 result

This should result in a proper distribution of hash values for most use situations.

对于大多数使用情况,这应该会导致哈希值的正确分布。

回答by Chris Carruthers

For a simple class it is often easiest to implement hashCode() based on the class fields which are checked by the equals() implementation.

对于一个简单的类,基于由 equals() 实现检查的类字段来实现 hashCode() 通常是最简单的。

public class Zam {
    private String foo;
    private String bar;
    private String somethingElse;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        Zam otherObj = (Zam)obj;

        if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
            if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
                return true;
            }
        }

        return false;
    }

    public int hashCode() {
        return (getFoo() + getBar()).hashCode();
    }

    public String getFoo() {
        return foo;
    }

    public String getBar() {
        return bar;
    }
}

The most important thing is to keep hashCode() and equals() consistent: if equals() returns true for two objects, then hashCode() should return the same value. If equals() returns false, then hashCode() should return different values.

最重要的是保持 hashCode() 和 equals() 一致:如果 equals() 对两个对象返回 true,那么 hashCode() 应该返回相同的值。如果equals() 返回false,那么hashCode() 应该返回不同的值。

回答by SquareCog

@about8 : there is a pretty serious bug there.

@about8:那里有一个非常严重的错误。

Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");

same hashcode

相同的哈希码

you probably want something like

你可能想要类似的东西

public int hashCode() {
    return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();

(can you get hashCode directly from int in Java these days? I think it does some autocasting.. if that's the case, skip the toString, it's ugly.)

(这些天你能直接从 Java 中的 int 获取 hashCode 吗?我认为它会进行一些自动转换......如果是这样,跳过 toString,它很丑。)

回答by Grey Panther

First make sure that equals is implemented correctly. From an IBM DeveloperWorks article:

首先确保正确实现了equals。来自IBM DeveloperWorks 文章

  • Symmetry: For two references, a and b, a.equals(b) if and only if b.equals(a)
  • Reflexivity: For all non-null references, a.equals(a)
  • Transitivity: If a.equals(b) and b.equals(c), then a.equals(c)
  • 对称性:对于两个引用 a 和 b,a.equals(b) 当且仅当 b.equals(a)
  • 自反性:对于所有非空引用,a.equals(a)
  • 传递性:如果 a.equals(b) 和 b.equals(c),则 a.equals(c)

Then make sure that their relation with hashCode respects the contact (from the same article):

然后确保他们与 hashCode 的关系尊重联系人(来自同一篇文章):

  • Consistency with hashCode(): Two equal objects must have the same hashCode() value
  • 与 hashCode() 的一致性:两个相等的对象必须具有相同的 hashCode() 值

Finally a good hash function should strive to approach the ideal hash function.

最后一个好的散列函数应该努力接近理想的散列函数

回答by VonC

Just a quick note for completing other more detailed answer (in term of code):

只是完成其他更详细的答案(在代码方面)的快速说明:

If I consider the question how-do-i-create-a-hash-table-in-javaand especially the jGuru FAQ entry, I believe some other criteria upon which a hash code could be judged are:

如果我考虑how-do-i-create-a-hash-table-in-java 的问题,尤其是jGuru FAQ entry,我相信可以判断哈希码的其他一些标准是:

  • synchronization (does the algo support concurrent access or not) ?
  • fail safe iteration (does the algo detect a collection which changes during iteration)
  • null value (does the hash code support null value in the collection)
  • 同步(算法是否支持并发访问)?
  • 失败安全迭代(算法是否检测到在迭代过程中发生变化的集合)
  • 空值(哈希码是否支持集合中的空值)

回答by Olaf Kock

As you specifically asked for collections, I'd like to add an aspect that the other answers haven't mentioned yet: A HashMap doesn't expect their keys to change their hashcode once they are added to the collection. Would defeat the whole purpose...

正如您特别要求集合一样,我想添加一个其他答案尚未提到的方面:一旦将它们添加到集合中,HashMap 不希望它们的键更改它们的哈希码。会破坏整个目的......

回答by Mario Ortegón

If I understand your question correctly, you have a custom collection class (i.e. a new class that extends from the Collection interface) and you want to implement the hashCode() method.

如果我正确理解您的问题,您有一个自定义集合类(即从 Collection 接口扩展的新类)并且您想要实现 hashCode() 方法。

If your collection class extends AbstractList, then you don't have to worry about it, there is already an implementation of equals() and hashCode() that works by iterating through all the objects and adding their hashCodes() together.

如果你的集合类扩展了 AbstractList,那么你不必担心它,已经有一个 equals() 和 hashCode() 的实现,它们通过遍历所有对象并将它们的 hashCodes() 加在一起来工作。

   public int hashCode() {
      int hashCode = 1;
      Iterator i = iterator();
      while (i.hasNext()) {
        Object obj = i.next();
        hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
      }
  return hashCode;
   }

Now if what you want is the best way to calculate the hash code for a specific class, I normally use the ^ (bitwise exclusive or) operator to process all fields that I use in the equals method:

现在,如果您想要的是计算特定类的哈希码的最佳方法,我通常使用 ^(按位异或)运算符来处理我在 equals 方法中使用的所有字段:

public int hashCode(){
   return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}

回答by Chii

any hashing method that evenly distributes the hash value over the possible range is a good implementation. See effective java ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result) , there is a good tip in there for hashcode implementation (item 9 i think...).

任何在可能的范围内均匀分布哈希值的哈希方法都是一个很好的实现。请参阅有效的 java ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&result=en&sa=X&renum=book)在那里进行哈希码实现(我认为第 9 项......)。

回答by Rudi Adianto

There's a good implementation of the Effective Java's hashcode()and equals()logic in Apache Commons Lang. Checkout HashCodeBuilderand EqualsBuilder.

有一个很好的贯彻实施有效的Javahashcode()equals()逻辑的Apache Commons Lang中。签出HashCodeBuilderEqualsBuilder

回答by Rudi Adianto

about8.blogspot.com, you said

about8.blogspot.com,你说

if equals() returns true for two objects, then hashCode() should return the same value. If equals() returns false, then hashCode() should return different values

如果 equals() 对两个对象返回 true,则 hashCode() 应该返回相同的值。如果 equals() 返回 false,则 hashCode() 应该返回不同的值

I cannot agree with you. If two objects have the same hashcode it doesn't have to mean that they are equal.

我不能同意你的看法。如果两个对象具有相同的哈希码,并不一定意味着它们相等。

If A equals B then A.hashcode must be equal to B.hascode

如果 A 等于 B,则 A.hashcode 必须等于 B.hascode

but

if A.hashcode equals B.hascode it does not mean that A must equals B

如果 A.hashcode 等于 B.hascode 并不意味着 A 必须等于 B