具有相同散列的对象应该相等吗?

问题描述 投票:1回答:3

在下面的示例中,我创建了两个内部结构完全相同的对象。两者都只携带值1作为实例变量。我的想法是,如果我将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;
        }   
    }   
}

为什么equals在这里返回false?我认为下面的草图中有中间的情况:enter image description here

java equality
3个回答
1
投票

https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode--看以下几点

  1. [只要在Java应用程序执行期间在同一个对象上多次调用它,只要未修改在该对象的equals比较中使用的信息,hashCode方法就必须始终返回相同的整数。从一个应用程序的一次执行到同一应用程序的另一次执行,此整数不必保持一致。
  2. 如果根据equals(Object)方法两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。
  3. 如果两个对象根据equals(java.lang.Object)方法不相等,则在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
  4. 在合理可行的范围内,由Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (在某个时间点,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;
    }
}

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());
    }
}

输出:

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

由于employee3指向与employee1相同的对象,所以当employee2指向不同的对象时,它们将获得相同的哈希码(尽管其内容相同,关键字new将在内存中创建一个单独的对象),因此,您很少会从文档状态中获得与上述point#4相同的employee2哈希码:As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.

您必须以一种方式覆盖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());
    }
}

输出:

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

即使hashCode返回employee1,以上给出的employee2的实现也会为equalsfalse产生相同的哈希码(从文档中以上述[[point#3

进行检查)。[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()); } }

输出:

true employee1.hashCode(): 66066760 employee1.hashCode() again: 66069457 employee3.hashCode(): 66073797 false employee2.hashCode(): 66074882
这是覆盖hashCode的错误方法,因为在Java应用程序执行期间多次调用同一对象上的hashCode必须一致地返回相同的整数(请检查上述

point#1

文档)。
现在,看下面的代码及其输出:

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()); } }

输出:

true employee1.hashCode(): 511833308 employee3.hashCode(): 511833308 true employee2.hashCode(): 1297685781
由于employee1.equals(employee2)返回true,所以散列码也应返回相同(检查文档中的上述

point#2

)。但是,employee1employee2的哈希码值不同,这是不正确的。这种差异是因为我们没有覆盖hashCode方法。因此,每当您覆盖equals时,还应该以正确的方式覆盖hashCode
最后,下面给出的是实现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()); } }

输出:

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

1
投票
equals(Object)的默认实现检查两个对象是否是同一实例(即它们是==)。如果您需要一些不同的逻辑,则必须自己实现。请注意,如果执行此操作,则还应该实现自己的hashCode(),以便两个相等的对象也将具有匹配的哈希码。例如:

0
投票
您需要重写equals方法,否则Objectequals方法将用于比较两个实例。
© www.soinside.com 2019 - 2024. All rights reserved.