简体   繁体   English

具有相同哈希值的对象应该相等吗?

[英]Should to objects which have the same hash be equal?

In this below example, I create two objects which have exactly the same internal structure.在下面的示例中,我创建了两个具有完全相同内部结构的对象。 Both carry nothing but the value 1 as an instance variable.两者都只携带值 1 作为实例变量。 My thinking is that if I take the hash of e1 it should be the same as the hash of e2 and therefore e1.equals(e2) should return true.我的想法是,如果我取e1的哈希值,它应该与e2的哈希值相同,因此e1.equals(e2)应该返回 true。

class EqualsChecker {

    public static void main(String[] args) {

        Elem e1 = new Elem(1);
        Elem e2 = new Elem(1);


        System.out.println(e1);                                // EqualsChecker$Elem@6ff3c5b5
        System.out.println(e2);                                // EqualsChecker$Elem@3764951d
        System.out.println("e1.equals(e2): " + e1.equals(e2)); // returns false
    }


    static class Elem {
        private int v;
        public Elem(int i) {
            this.v = i;
        }   
    }   
}

Why does equals return false here?为什么这里的equals返回 false? I think I have the middle case in the below sketch:我想我在下面的草图中有中间情况: 在此处输入图片说明

equals(Object) 's default implementation checks if the two objects are the same instance (ie they are == ). equals(Object)的默认实现检查两个对象是否是同一个实例(即它们是== )。 If you want some different logic, you'll have to implement it yourself.如果你想要一些不同的逻辑,你必须自己实现它。 Note that if you do this, you should also implement your own hashCode() , so that two objects that are equal will also have matching hash codes.请注意,如果您这样做,您还应该实现自己的hashCode() ,以便两个相等的对象也将具有匹配的哈希码。 Eg:例如:

class Elem {
    private int v;

    @Override
    public boolean equals(final Object o) {
        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }
        Elem elem = (Elem) o;
        return this.v == elem.v;
    }

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

Look at the following points from https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode--https://docs.oracle.com/javase/9​​/docs/api/java/lang/Object.html#hashCode看以下几点——

  1. Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.在 Java 应用程序执行期间,只要在同一个对象上多次调用它,hashCode 方法必须始终返回相同的整数,前提是在对象的 equals 比较中使用的信息没有被修改。 This integer need not remain consistent from one execution of an application to another execution of the same application.该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
  2. If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.如果根据 equals(Object) 方法两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
  3. It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.如果根据 equals(java.lang.Object) 方法两个对象不相等,则不需要对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。 However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.但是,程序员应该意识到为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
  4. As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.尽可能实用,类 Object 定义的 hashCode 方法确实为不同的对象返回不同的整数。 (The hashCode may or may not be implemented as some function of an object's memory address at some point in time.) (在某个时间点,hashCode 可能会也可能不会被实现为对象内存地址的某个函数。)

Now, look at the following code and its output:现在,查看以下代码及其输出:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Output:输出:

true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
false
employee2.hashCode(): 1297685781

Since employee3 is pointing to the same object as employee1 , you are getting the same hashcode for them while employee2 is pointing to a different object (although it has the same content, the keyword, new will create a separate object in the memory) and therefore, you may rarely get the same hashcode for employee2 as point#4 mentioned above from the documentation states: As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.由于employee3指向同一对象employee1 ,你所得到的相同的哈希码为他们而employee2指向一个不同的对象(尽管它具有相同的内容,关键字, new会在内存中创建一个单独的对象),因此,您可能很少从文档状态中获得与上面提到的第 4 点相同的employee2哈希码: As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.

You must override hashCode method in a way which should return the same hashcode for two objects having the same content eg您必须以某种方式覆盖hashCode方法,该方法应该为具有相同内容的两个对象返回相同的哈希码,例如

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

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

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Output:输出:

true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
false
employee2.hashCode(): 128107556

The implementation of hashCode given above produces the same hashcode for employee1 and employee2 even though equals returns false (check as point#3 mentioned above from the documentation).上面给出的hashCode的实现为employee1employee2生成相同的hashcode,即使equals返回false (检查文档中提到的第3 点)。

A wrong way of overriding of hashCode may result in even the same objects returning different hashcodes eg覆盖hashCode错误方式可能会导致即使相同的对象返回不同的哈希码,例如

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((code == null) ? 0 : (int) (code.length() * (Math.random() * 100)));
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee1.hashCode() again: " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Output:输出:

true
employee1.hashCode(): 66066760
employee1.hashCode() again: 66069457
employee3.hashCode(): 66073797
false
employee2.hashCode(): 66074882

This is the wrong way of overriding hashCode because invoking hashCode on the same object more than once during an execution of a Java application must consistently return the same integer (check as point#1 mentioned above from the documentation).这是覆盖hashCode的错误方法,因为在 Java 应用程序执行期间多次调用同一个对象上的hashCode必须始终如一地返回相同的整数(检查文档中提到的第 1 点)。

Now, look at the following code and its output:现在,查看以下代码及其输出:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

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

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Output:输出:

true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
true
employee2.hashCode(): 1297685781

Since employee1.equals(employee2) returns true , the hashcode should also be returned same (check point#2 mentioned above from the documentation).由于employee1.equals(employee2)返回true ,哈希码也应该返回相同的(检查点#2上面提到的文档)。 However, the hashcode values of employee1 and employee2 are different which is not correct.但是, employee1employee2的hashcode 值不同,这是不正确的。 This difference is because we haven't overridden the hashCode method.这种差异是因为我们没有覆盖hashCode方法。 So, whenever you override equals , you should also override hashCode in a correct way.因此,每当您覆盖equals ,您还应该以正确的方式覆盖hashCode

Finally, given below is a correct way of implementing hashCode and equals :最后,下面给出了实现hashCodeequals的正确方法:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((code == null) ? 0 : code.hashCode());
        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;
        MyEmployee other = (MyEmployee) obj;
        if (age != other.age)
            return false;
        if (code == null) {
            if (other.code != null)
                return false;
        } else if (!code.equals(other.code))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Output:输出:

true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
true
employee2.hashCode(): 128107556

You need to override the equals method, otherwise the Object 's equals method will be used to compare the two instances.您需要覆盖equals方法,否则将使用Objectequals方法来比较两个实例。

@Override
public boolean equals(Object that) {
    if (this == that) return true;
    if (that instanceof Elem) {
        Elem thatElem = (Elem) that;
        return thatElem.v == this.v;
    }
    return false;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM