最近我读了这篇文章 开发人员工作文件。

该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。

我如何决定有效地实现这些方法?


当前回答

假设你有一个类(A),它聚合了另外两个类(B) (C),你需要在哈希表中存储类(A)的实例。默认实现只允许区分实例,但不允许通过(B)和(C)。因此A的两个实例可以相等,但默认不允许您以正确的方式比较它们。

其他回答

String类和包装器类的equals()和hashCode()方法实现与Object类不同。Object类的equals()方法比较对象的引用,而不是内容。Object类的hashCode()方法为每个对象返回不同的hashCode,无论内容是否相同。

It leads problem when you use Map collection and the key is of Persistent type, StringBuffer/builder type. Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even though both have same contents. It will make the hashMap storing same content keys. Storing same content keys means it is violating the rule of Map because Map doesnt allow duplicate keys at all. Therefore you override equals() as well as hashCode() methods in your class and provide the implementation(IDE can generate these methods) so that they work same as String's equals() and hashCode() and prevent same content keys.

你必须重写hashCode()方法和equals(),因为equals()根据hashCode工作。

此外,与equals()一起重写hashCode()方法有助于完好无损equals()-hashCode()契约:“如果两个对象相等,那么它们必须具有相同的哈希码。”

什么时候需要为hashCode()编写自定义实现?

正如我们所知,HashMap的内部工作是基于哈希原理的。条目集存储在特定的存储桶中。您可以根据自己的需求定制hashCode()实现,以便相同的类别对象可以存储在相同的索引中。 当你使用put(k,v)方法将值存储到Map集合中时,put()的内部实现是:

put(k, v){
hash(k);
index=hash & (n-1);
}

意思是,它生成索引,索引是基于特定键对象的hashcode生成的。所以让这个方法根据你的需求生成哈希码,因为相同的哈希码条目集将存储在相同的桶或索引中。

就是这样!

它在使用值对象时很有用。以下摘自Portland Pattern Repository:

Examples of value objects are things like numbers, dates, monies and strings. Usually, they are small objects which are used quite widely. Their identity is based on their state rather than on their object identity. This way, you can have multiple copies of the same conceptual value object. So I can have multiple copies of an object that represents the date 16 Jan 1998. Any of these copies will be equal to each other. For a small object such as this, it is often easier to create new ones and move them around rather than rely on a single object to represent the date. A value object should always override .equals() in Java (or = in Smalltalk). (Remember to override .hashCode() as well.)

HashMap和HashSet等集合使用对象的hashcode值来确定该对象应该如何存储在集合中,然后再次使用hashcode来定位该对象 在它的收藏中。

哈希检索是一个两步过程:

找到正确的桶(使用hashCode()) 在桶中搜索正确的元素(使用equals())

下面是一个关于为什么我们应该重写equals()和hashcode()的小例子。

考虑一个Employee类,它有两个字段:年龄和名字。

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

现在创建一个类,将Employee对象插入到HashSet中并测试该对象是否存在。

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

它将打印以下内容:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

现在uncomment hashcode()方法,执行相同的方法,输出将是:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Now can you see why if two objects are considered equal, their hashcodes must also be equal? Otherwise, you'd never be able to find the object since the default hashcode method in class Object virtually always comes up with a unique number for each object, even if the equals() method is overridden in such a way that two or more objects are considered equal. It doesn't matter how equal the objects are if their hashcodes don't reflect that. So one more time: If two objects are equal, their hashcodes must be equal as well.

常见错误如下例所示。

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

绿色的车没有找到

2. hashCode()引起的问题

该问题是由未覆盖的hashCode()方法引起的。equals()和hashCode()之间的契约是:

如果两个对象相等,那么它们必须具有相同的哈希码。 如果两个对象具有相同的哈希码,则它们可能相等,也可能不相等。 公共int hashCode(){ 返回this.color.hashCode (); }

Java中的Equals和Hashcode方法

它们是java.lang. object类的方法,object类是所有类(自定义类以及java API中定义的其他类)的超类。

实现:

public boolean equals(对象obj) hashCode()

public boolean equals(对象obj)

这个方法只是检查两个对象引用x和y是否引用同一个对象。例如,它检查x是否== y。

它是自反的:对于任何参考值x, x = (x)应该返回true。

它是对称的:对于任何参考值x和y,当且仅当y = (x)返回true时,x = (y)应该返回true。

它是可传递的:对于任何参考值x、y和z,如果x = (y)返回true, y = (z)返回true,则x = (z)应该返回true。

它是一致的:对于任何参考值x和y, x.equals(y)的多次调用一致返回true或一致返回false,前提是对象上的等号比较中使用的信息没有被修改。

对于任何非空参考值x, x.equals(null)应该返回 假的。

hashCode()

此方法返回调用此方法的对象的哈希码值。此方法以整数形式返回哈希码值,支持基于哈希的集合类,如Hashtable、HashMap、HashSet等。必须在重写equals方法的每个类中重写此方法。

hashCode的一般契约是:

在Java应用程序的执行过程中,只要在同一个对象上多次调用hashCode方法,hashCode方法必须一致地返回相同的整数,前提是该对象上的等号比较中使用的信息没有被修改。

这个整数不需要在应用程序的一次执行和同一应用程序的另一次执行之间保持一致。

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

如果根据equals(java.lang.Object)方法,两个对象是不相等的,那么对每个对象调用hashCode方法必须产生不同的整数结果,这是不要求的。然而,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

相等的对象必须产生相同的哈希代码,只要它们是 相等但不相等的对象不需要产生不同的哈希码。

资源:

JavaRanch

图片