如何覆写java中的equals和hashcode方法

          这篇文章算是一个翻译,原文在:点击打开链接,但我并没有一字不差全部翻译,只是选出一些重点,大家有兴趣可以看看原文

          Equals和 hashCode是java中一个对象的两个基本方法和core java的重要组成部分。Equals用来比较对象的相等性,hashcode用来生成相应对象的整数形编码。Equals  hashCodecore java中有广泛的应用,例如在hashmap中插入和取回数据。

Equals方法覆写遵循以下规则:

(1)自反性。一个对象必须和它自身相等;

(2)  对称性。如果a.equals(b) 为true 那么 b.equals(a) 也必须为true;

(3)  传递性。如果 a.equals(b) 为true 并且 b.equals(c) 为 true 那么 c.equals(a) 也必须 为 true;

(4)  一致性。除非字段的值改变,否则多次调用equals()方法应该返回相同的结果。

(5)  Null比较。任何对象和null比较必须返回false,并且不能够出现NullPointerException结果。

Equals hashCode的关系

Equals()hashcode()必须遵守以下规则:

(1)         如果两个对象执行equals()方法是相等的,那么执行hashcode()方法的结果也必须是相等的;

(2)         如果两个对象执行equals()方法不相等,那么执行hashcode()方法的结果可以相等页可以不相等。

重写equals方法的步骤

这是大多数java程序员重写equals方法的标准做法:

(1)  做this检查。如果是this则返回true;

(2)  做null检查。如果是null则返回false;

(3) instanceof检查。如果instanceof 返回false,那么equals方法就返回false。在做了一些研究之后,我发现可以用getClass()方法代替instanceof 来比较类型的相等性,因为instanceof 在检查子类的时候也会返回true。所以在需要商业逻辑的时候它并不是一个严格的相等关系。如果你的类是不变类,没有类会继承它,那么使用instanceof 时合适的。所以可以使用一下方式代替instanceof :

if((obj == null) || (obj.getClass() != this.getClass()))
        return false;

(4)  对象类型转换。

(5)  以数值型属性开始比较每个属性值,因为数值型属性比较最快而且在结合检查的时候可以使用短路操作。如果第一个属性不匹配,那么就可以返回false而不需要匹配剩余的属性。在每个属性调用equals方法前也要记得做null检查,避免在递归equals检查时出现NullPointerException

覆写equlas方法的代码样例

/** 
 * Person class with equals and hashcode implementation in Java
 * @author Javin Paul
 */
public class Person {
    private int id;
    private String firstName;
    private String lastName;

    public int getId() { return id; }
    public void setId(int id) { this.id = id;}

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

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

        Person guest = (Person) obj;
        return id == guest.id
                && (firstName == guest.firstName 
                     || (firstName != null && firstName.equals(guest.getFirstName())))
                && (lastName == guest.lastName 
                     || (lastName != null && lastName .equals(guest.getLastName())));
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((firstName == null) ? 0 : firstName.hashCode());
        result = prime * result + id;
        result = prime * result
                + ((lastName == null) ? 0 : lastName.hashCode());
        return result;
    }
    
}
在覆写 equals时的常见错误

(1)  重载了equals方法而没有重写它

这是我见到的覆写equals方法最常见的错误。equasl的语法是public boolean equals(Object obj),但许多人无意间重载了equals方法 public boolean equals(Person obj)。这个错误因为静态绑定而非常难以察觉。

(2)  第二个错误是在覆写equals方法时没有为成员变量没有做null检查,最终在调用equals方法时导致 NullPointerException。正确的做法是:

firstname == guest.firstname || (firstname !null && firstname.equals(guest.firstname)));

(3)  只覆写equals()方法而没有覆写hashCode方法。你必须同时覆写equals和hashCode的方法,要不然这个对象就不能在HashMap中作为一个key,因为HashMap是依赖这两个方法的。

(4)      最后一个错误是在覆写equals方法的时候没有保持equals方法和compareTo()的一致性,这不是正常的要求,只是为了服从Set的约定避免重复。SortedSet 的实现例如TreeSet使用compareTo方法来比较两个对象,例如字符串。如果compareTo方法和equals方法没有保持一致,那么TreeSet就会允许重复,这样就损坏了Set不能重复的约定。想要学习更多可以查看Things to remember whileoverriding compareTo in Java

在写equals方法时的5个小提示

1)大多数的IDE,例如NetBeans, Eclipse 和 IntelliJ IDEA提供生成equals和hashCode方法的支持。在In Eclipse do the right click-> source-> generate hashCode() and equals().

2)如果你的类中有一些唯一的商业主键,那么在equals方法中比较这些主键字段就足够了而不需要比较所有的字段。例如“id”是每个Person唯一的,那么只需要比较id就可以鉴别两个Person是不是相同的。

3)在覆写hashCode方法时,要确保你使用的所有字段都是在equals方法里是相同的。

4)String和包装类例如Integer,Float和Double需要覆写equals方法而StringBuffer不需要。

5)只要有可能,要努力通过final使你的字段不可变。Equals方法在不可变字段上要比可变字段安全的多。

你可能感兴趣的:(java)