有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

对象如果我重写Java中的“equals”方法,为什么需要重写hashcode?

我知道,每当在Java中重写equals方法时,都需要重写哈希代码。那只是一份合同。我试图理解这背后的逻辑。我正在阅读Joshua Bloch的*Effective Java,我偶然发现了以下代码(第45页第9项):

import java.util.HashMap;
import java.util.Map;

public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix, int lineNumber) {
        rangeCheck(areaCode, 999, "area code");
        rangeCheck(prefix, 999, "prefix");
        rangeCheck(lineNumber, 9999, "line number");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }

    private static void rangeCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max)
            throw new IllegalArgumentException(name + ": " + arg);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof PhoneNumber))
            return false;
        PhoneNumber pn = (PhoneNumber) o;
        return pn.lineNumber == lineNumber && pn.prefix == prefix
                && pn.areaCode == areaCode;
    }

    // Broken - no hashCode method!

    // A decent hashCode method - Page 48
    // @Override public int hashCode() {
    // int result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // return result;
    // }

    // Lazily initialized, cached hashCode - Page 49
    // private volatile int hashCode; // (See Item 71)
    //
    // @Override public int hashCode() {
    // int result = hashCode;
    // if (result == 0) {
    // result = 17;
    // result = 31 * result + areaCode;
    // result = 31 * result + prefix;
    // result = 31 * result + lineNumber;
    // hashCode = result;
    // }
    // return result;
    // }

    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
        m.put(new PhoneNumber(707, 867, 5309), "Jenny");
        System.out.println(m.get(new PhoneNumber(707, 867, 5309)));
    }
}

这就是他在文中提到的,我很难理解

At this point, you might expect m.get(new PhoneNumber(707, 867, 5309)) to return "Jenny", but it return null. Notice that two PhoneNumber instances are involved: one is used for insertion into the HashMap and a second, equal, instance is used for (attempted) retrieval. The PhoneNumber class's failure to override hashCode causes the two equal instances to have unequal hashcodes, in violation of the hashcode contract. Therefore the get method is likely to look for the phone number in a different hash bucket from the one in which it was stored by the put method

我不明白他说的两个电话号码是什么。我在m.put(new PhoneNumber(707, 867, 5309), "Jenny")中只创建了一个实例。我还要再次查找这个对象,它应该返回相同的hashcode,即使它从对象类继承了hashcode方法。 为什么会这样?这里的一些解释会很有帮助


共 (3) 个答案

  1. # 1 楼答案

    如果不将hashcodeequals一起重写,则每个实例(例如“new PhoneNumber(707, 867, 5309)")将具有不同的哈希代码

    因此,从HashMap的角度来看,它们将被视为两个不同的条目。 请阅读更多关于hashmap工作原理的信息。因此,如果两个可能相等但hascode不同的对象将存储在不同的存储桶中

  2. # 2 楼答案

    hashCode()的目的是快速识别对象不相等的事物;对hashCode()的一次调用将立即显示一个对象不等于任何其hashashCode()方法已被调用且已返回不同值的对象。这是一个非常强大的功能,但它要求任何两个“相等”的对象必须返回相同的hashCode值。如果两个对象不返回相同的hashCode值,一些集合类型将假定它们不可能相等,并且不会费心调用equals来查看它们是否相等

  3. # 3 楼答案

    谢谢你的提问

    Also I look for this object again, which should return the same hashcode even if it inherits the hashCode method from Object Class.

    检查对象#hashCode文档here

    As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)

    换句话说,hashCode不会为两个对象返回相同的整数,除非覆盖它