JavahashCode()方法

示例

当Java类覆盖该equals方法时,它也应覆盖该hashCode方法。根据方法合同中的定义:

  • 在Java应用程序执行期间,只要在同一对象上多次调用它,该hashCode方法就必须一致地返回相同的整数,前提是不修改该对象的equals比较中使用的信息。从一个应用程序的执行到同一应用程序的另一执行,此整数不必保持一致。

  • 如果根据该equals(Object)方法两个对象相等,则hashCode在两个对象中的每个对象上调用该方法必须产生相同的整数结果。

  • 如果两个对象根据该equals(Object)方法不相等,则不需要在两个对象中的hashCode每一个上调用该方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

散列码在散列实现,如使用HashMap,HashTable,和HashSet。该hashCode函数的结果确定将放置对象的存储桶。如果提供的hashCode实现良好,则这些哈希实现效率更高。良好hashCode实现的重要属性是hashCode值的分布是均匀的。换句话说,将多个实例存储在同一存储桶中的可能性很小。

用于计算哈希码值的算法可能类似于以下内容:

public class Foo {
    private int field1, field2;
    private String field3;

    public Foo(int field1, int field2, String field3) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
    }

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

        Foo f = (Foo) obj;
        return field1 == f.field1 &&
               field2 == f.field2 &&
               (field3 == null ? f.field3 == null : field3.equals(f.field3);
    }

    @Override
    public int hashCode() {
        int hash = 1;
        hash = 31 * hash + field1;
        hash = 31 * hash + field2;
        hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
        return hash;
    }
}

使用作为一个捷径Arrays.hashCode()

Java SE 1.2

在Java 1.2及更高版本中,代替开发一种算法来计算哈希码,可以java.util.Arrays#hashCode通过提供一个包含字段值的Object或基本数组来生成一个:

@Override
public int hashCode() {
    return Arrays.hashCode(new Object[] {field1, field2, field3});
}
Java SE 7

Java 1.7引入了java.util.Objects提供便捷方法的类,该类hash(Object... objects)根据提供给它的对象的值计算哈希码。此方法的工作方式与相同java.util.Arrays#hashCode。

@Override
public int hashCode() {
    return Objects.hash(field1, field2, field3);
}

注意:这种方法效率低下,并且每次hashCode()调用您的自定义方法时都会产生垃圾对象:

  • 将Object[]创建一个临时文件。(在版本中,该数组是通过“ varargs”机制创建的。)Objects.hash()

  • 如果任何字段是原始类型,则必须将它们装箱,这可能会创建更多临时对象。

  • 必须填充该数组。

  • 该数组必须通过Arrays.hashCodeorObjects.hash方法进行迭代。

  •  不能内联对此或必须(可能)进行的调用。Object.hashCode()Arrays.hashCodeObjects.hash

哈希码的内部缓存

由于对象哈希码的计算可能很昂贵,因此在第一次计算哈希值时将其缓存在对象中会很有吸引力。例如

public final class ImmutableArray {
    private int[] array;
    private volatile int hash = 0;

    public ImmutableArray(int[] initial) {
        array = initial.clone();
    }

    // 其他方法

    @Override
    public boolean equals(Object obj) {
         // ...
    }

    @Override
    public int hashCode() {
        int h = hash;
        if (h == 0) {
            h = Arrays.hashCode(array);
            hash = h;
        }
        return h;
    }
}

这种方法权衡了(重复)计算哈希码的成本与缓存哈希码的额外字段的开销。这是否可以作为性能优化的回报,将取决于给定对象散列(查找)的频率以及其他因素。

您还将注意到,如果事件的真实哈希码ImmutableArray恰好为零(2 32中为1的机会),则缓存无效。

最后,如果我们要哈希的对象是可变的,则这种方法很难正确实现。但是,如果哈希码发生更改,则存在更大的担忧;参见上面的合同。