Eclipse 生成的 hashCode 函数好用吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11795104/
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-09-19 18:44:50  来源:igfitidea点击:

Is the hashCode function generated by Eclipse any good?

javaeclipsehashhashmaphashtable

提问by ams

Eclipse source menu has a "generate hashCode / equals method" which generates functions like the one below.

Eclipse 源菜单有一个“生成 hashCode/equals 方法”,它生成如下所示的函数。

String name; 
@Override
public int hashCode()
{
    final int prime = 31;
    int result = 1;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}

@Override
public boolean equals(Object obj)
{
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    CompanyRole other = (CompanyRole) obj;
    if (name == null)
    {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    return true;
}

If I select multiple fields when generating hashCode()and equals()Eclipse uses the same pattern shown above.

如果我在生成时选择多个字段hashCode()并且equals()Eclipse 使用上面显示的相同模式。

I am not an expert on hash functions and I would like to know how "good" the generated hash function is? What are situations where it will break down and cause too many collisions?

我不是散列函数的专家,我想知道生成的散列函数有多“好”?什么情况下它会崩溃并导致过多的碰撞?

采纳答案by sakthisundar

You can see the implementation of hashCode function in java.util.ArrayListas

您可以在java.util.ArrayListas 中看到 hashCode 函数的实现

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

It is one such example and your Eclipse generated code follows a similar way of implementing it. But if you feel that you have to implement your hashCode by your own, there are some good guidelines given by Joshua Bloch in his famous book Effective Java. I will post those important points from Item 9 of that book. Those are,

这是一个这样的示例,您的 Eclipse 生成的代码遵循类似的实现方式。但是如果你觉得你必须自己实现你的 hashCode,那么 Joshua Bloch 在他的名著Effective Java 中给出了一些很好的指导方针。我将发布那本书第 9 项中的重要观点。那些是,

  1. Store some constant nonzero value, say, 17, in an int variable called result.
  2. For each significant field f in your object (each field taken into account by the equals method, that is), do the following:

    a. Compute an int hash code c for the field:

    i. If the field is a boolean, compute (f ? 1 : 0).

    ii. If the field is a byte, char, short, or int, compute (int) f.

    iii. If the field is a long, compute (int) (f ^ (f >>> 32)).

    iv. If the field is a float, compute Float.floatToIntBits(f).

    v. If the field is a double, compute Double.doubleToLongBits(f), and then hash the resulting long as in step 2.a.iii.

    vi. If the field is an object reference and this class's equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, return 0 (or some other constant, but 0 is traditional)

    vii. If the field is an array, treat it as if each element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine these values per step 2.b. If every element in an array field is significant, you can use one of the Arrays.hashCode methods added in release 1.5.

    b. Combine the hash code c computed in step 2.a into result as follows:

       result = 31 * result + c;
    
  3. Return result.

  4. When you are finished writing the hashCode method, ask yourself whether equal instances have equal hash codes. Write unit tests to verify your intuition! If equal instances have unequal hash codes, figure out why and fix the problem.

  1. 将一些恒定的非零值(例如 17)存储在名为 result 的 int 变量中。
  2. 对于对象中的每个重要字段 f(即 equals 方法考虑的每个字段),执行以下操作:

    一种。计算该字段的 int 哈希码 c:

    一世。如果该字段是布尔值,则计算 (f ? 1 : 0)。

    ii. 如果字段是字节、字符、短整型或整数,则计算 (int) f。

    三、如果该字段是 long,则计算 (int) (f ^ (f >>> 32))。

    四、如果该字段是浮点数,则计算 Float.floatToIntBits(f)。

    v. 如果该字段是双精度型,则计算 Double.doubleToLongBits(f),然后按照步骤 2.a.iii 中的方法散列所得的 long。

    六. 如果该字段是一个对象引用并且该类的equals方法通过递归调用equals来比较该字段,则对该字段递归调用hashCode。如果需要更复杂的比较,请计算此字段的“规范表示”并在规范表示上调用 hashCode。如果该字段的值为空,则返回 0(或其他一些常量,但 0 是传统的)

    七. 如果该字段是一个数组,则将其视为每个元素都是一个单独的字段。也就是说,通过递归地应用这些规则来计算每个重要元素的哈希码,并在每个步骤 2.b 中组合这些值。如果数组字段中的每个元素都很重要,您可以使用 1.5 版中添加的 Arrays.hashCode 方法之一。

    湾 将步骤 2.a 中计算出的哈希码 c 组合成如下结果:

       result = 31 * result + c;
    
  3. 返回结果。

  4. 完成 hashCode 方法的编写后,问问自己相等的实例是否具有相等的哈希码。编写单元测试来验证您的直觉!如果相等的实例具有不相等的哈希码,找出原因并解决问题。

Java language designers and Eclipse seem to follow similar guidelines I suppose. Happy coding. Cheers.

我想 Java 语言设计者和 Eclipse 似乎遵循类似的指导方针。快乐编码。干杯。

回答by leftbit

Since Java 7 you can use java.util.Objectsto write short and elegant methods:

从 Java 7 开始,您可以使用java.util.Objects编写简短而优雅的方法:

class Foo {
  private String name;
  private String id;

  @Override
  public int hashCode() {
    return Objects.hash(name,id);
  }

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Foo) {
      Foo right = (Foo) obj;
      return Objects.equals(name,right.name) && Objects.equals(id,right.id);
    }
    return false;
  }
}

回答by Eugene

Generally it is good, but:

一般来说它是好的,但是:

  1. Guava does it somehow better, I prefer it. [EDIT: It seems that as of JDK7 Java provides a similar hash function].
  2. Some frameworks can cause problems when accessing fields directly instead of using setters/getters, like Hibernatefor example. For some fields that Hibernate creates lazy, it creates a proxy not the real object. Only calling the getter will make Hibernate go for the real value in the database.
  1. 番石榴以某种方式做得更好,我更喜欢它。[编辑:从 JDK7 开始,Java 似乎提供了类似的哈希函数]。
  2. 某些框架在直接访问字段而不是使用 setter/getter 时可能会导致问题,例如Hibernate。对于 Hibernate 创建的某些字段,它会创建一个代理而不是真正的对象。只有调用 getter 才会让 Hibernate 去获取数据库中的真实值。

回答by Martijn Courteaux

Yes, it is perfect :) You will see this approach almost everywhere in the Java source code.

是的,它是完美的 :) 您几乎可以在 Java 源代码中的任何地方看到这种方法。

回答by Heiko Schmitz

It's a standard way of writing hash functions. However, you can improve/simplify it if you have some knowledge about the fields. E.g. you can ommit the null check, if your class guarantees that the field never be null (applies to equals() as well). Or you can of delegate the field's hash code if only one field is used.

这是编写散列函数的标准方式。但是,如果您对这些领域有一些了解,则可以改进/简化它。例如,如果您的类保证该字段永远不会为空(也适用于 equals()),则您可以省略空检查。或者,如果只使用一个字段,您可以委托该字段的哈希码。

回答by Ashutosh Jindal

I would also like to add a reference to Item 9, in Effective Java 2nd Edition by Joshua Bloch.

我还想在 Joshua Bloch 的 Effective Java 2nd Edition 中添加对第 9 条的引用。

Here is a recipe from Item 9 : ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS

这是一个食谱 Item 9 : ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS

  1. Store some constant nonzero value, say, 17, in an int variable called result.
  2. For each significant field f in your object (each field taken into account by the equals method, that is), do the following:
  1. 将一些恒定的非零值(例如 17)存储在名为 result 的 int 变量中。
  2. 对于对象中的每个重要字段 f(即 equals 方法考虑的每个字段),执行以下操作:
    a. Compute an int hash code c for the field:            
        i.   If the field is a boolean, compute (f ? 1 : 0).
        ii.  If the field is a byte, char, short, or int, compute (int) f.
        iii. If the field is a long,compute(int)(f^(f>>>32)).
        iv.  If the field is a float, compute Float.floatToIntBits(f).
        v.   If the field is a double, compute Double.doubleToLongBits(f), and then hash the resulting long as in step 2.a.iii.
        vi.  If the field is an object reference and this class's equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, return 0 (or some other constant, but 0 is traditional).
        vii. If the field is an array, treat it as if each element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine these values per step 2.b. If every element in an array field is significant, you can use one of the Arrays.hashCode methods added in release 1.5. 
   b. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c;
3. Return result.
4. When you are finished writing the hashCode method, ask yourself whether equal instances have equal hash codes. Write unit tests to verify your intuition! If equal instances have unequal hash codes, figure out why and fix the problem.
   b. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c;
3. Return result.
4. When you are finished writing the hashCode method, ask yourself whether equal instances have equal hash codes. Write unit tests to verify your intuition! If equal instances have unequal hash codes, figure out why and fix the problem.

回答by 2787184

If you are using Apache Software Foundation (commons-lang library) then below classes will help you to generate hashcode/equals/toString methods using reflection.

如果您使用的是 Apache Software Foundation(commons-lang 库),那么下面的类将帮助您使用反射生成 hashcode/equals/toString 方法。

You don't need to worry about regenerating hashcode/equals/toString methods when you add/remove instance variables.

添加/删除实例变量时,您无需担心重新生成 hashcode/equals/toString 方法。

EqualsBuilder- This class provides methods to build a good equals method for any class. It follows rules laid out in Effective Java , by Joshua Bloch. In particular the rule for comparing doubles, floats, and arrays can be tricky. Also, making sure that equals() and hashCode() are consistent can be difficult.

EqualsBuilder- 此类提供了为任何类构建良好的 equals 方法的方法。它遵循 Joshua Bloch 在 Effective Java 中制定的规则。特别是比较双精度数、浮点数和数组的规则可能很棘手。此外,确保 equals() 和 hashCode() 一致可能很困难。

HashCodeBuilder- This class enables a good hashCode method to be built for any class. It follows the rules laid out in the book Effective Java by Joshua Bloch. Writing a good hashCode method is actually quite difficult. This class aims to simplify the process.

HashCodeBuilder- 此类可以为任何类构建良好的 hashCode 方法。它遵循 Joshua Bloch 在 Effective Java 一书中列出的规则。编写一个好的 hashCode 方法实际上是相当困难的。本课程旨在简化过程。

ReflectionToStringBuilder- This class uses reflection to determine the fields to append. Because these fields are usually private, the class uses AccessibleObject.setAccessible(java.lang.reflect.AccessibleObject[], boolean) to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are set up correctly.

ReflectionToStringBuilder- 此类使用反射来确定要附加的字段。因为这些字段通常是私有的,该类使用 AccessibleObject.setAccessible(java.lang.reflect.AccessibleObject[], boolean) 来更改字段的可见性。这将在安全管理器下失败,除非正确设置了适当的权限。

Maven Dependency:

Maven 依赖:

<dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>${commons.lang.version}</version>
</dependency>

Sample Code:

示例代码:

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

public class Test{

    instance variables...
    ....

    getter/setter methods...
    ....

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }
}

回答by kc2001

One potential drawback is that all objects with null fields will have a hash code of 31, thus there could be many potential collisions between objects that only contain null fields. This would make for slower lookups in Maps.

一个潜在的缺点是所有具有空字段的对象的哈希码为 31,因此仅包含空字段的对象之间可能存在许多潜在的冲突。这将使Maps.

This can occur when you have a Mapwhose key type has multiple subclasses. For example, if you had a HashMap<Object, Object>, you could have many key values whose hash code was 31. Admittedly, this won't occur that often. If you like, you could randomly change the values of the prime to something besides 31, and lessen the probability of collisions.

当您Map的键类型具有多个子类时,可能会发生这种情况。例如,如果您有一个HashMap<Object, Object>,您可能有许多哈希码为 31 的键值。诚然,这种情况不会经常发生。如果您愿意,您可以将质数的值随机更改为 31 以外的值,并减少碰撞的可能性。