Java-正确使用equals和hashCode方法

来源:互联网 发布:网络安全管理指导思想 编辑:程序博客网 时间:2024/05/18 03:43

基本摘抄自Java 中正确使用 hashCode 和 equals 方法
hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。

1.equals

equals要遵守的通用约定(equals方法实现了等价关系):
1)自反性:x.equals(x)一定返回true
2)对称性:x.equals(y)返回true当且仅当y.equals(x)
3)传递性:x.equals(y)且y.equals(z),则x.equals(z)为true
4)一致性:若x.equals(y)返回true,则不改变x,y时多次调用x.equals(y)都返回true
5)对于任意的非空引用值x,x.equals(null)一定返回false。
当重写完equals方法后,应该检查是否满足对称性、传递性、一致性。(自反性、null通常会自行满足)

1.1 equals重写默认实现

有的时候程序要求我们必须改变一些对象的默认实现。
来看看这个例子,让我们创建一个简单的类Person

public class Person {    private String id;    private String name;    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return "id:"+this.getId()+" name:"+this.getName();    }}

上面的Person类只是有一些非常基础的属性和getter、setter.现在来考虑一个你需要比较两个person的情形。

public class EqualTest {    public static void main(String[] args) {        Person p1 = new Person();        Person p2 = new Person();        p1.setId("1"); p1.setName("xwq");        p2.setId("1"); p2.setName("xwq");        System.out.println(p1.equals(p2));    }}

这里写图片描述
毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个person。真正的商业逻辑希望我们返回true。
为了达到这个目的,我们需要重写equals方法。

@Override    public boolean equals(Object o) {        if(o == null)   return false;        if(o == this)   return true;//提高效率        if(o.getClass() != this.getClass())            return false;        //或者使用instance关键字判断//      if(!(o instanceof Person)) //          return false;        Person p1 = (Person)o;        //如果元素是数组,可使用Arrays.deepEquals(array1,array2);进行判断        return this.getId().equals(p1.getId()) && this.getName().equals(p1.getName());    }

这里写图片描述
在上面的类中添加这个方法,EauqlsTest将会输出true。

1.2 编写equals开发方法 的建议

当equals参数不属于同一类时,且具有继承关系时,instanceof的检测结果将不满足对称性。
如:c是p的子类,如果在equals中用instanceof检测,那么:
  p.equals(c) 将返回true;
  c.equals(p) 将返回false或者抛出异常。
1、显示参数命名为otherobject,稍后强制转换为叫other的变量。
2、检测this==otherobject
3、检测this==null
4、检测getclass()!=otherobject.getclass()
  如果所有的子类都拥有统一的语义,就使用instanceof检测
  otherobject instanceof classname
5、将otherobject转换为相应类型的变量
6、比较所有的域
7、若在子类中重新定义了equals开发方法 ,则需在子类中包含调用super.equals(other)

2.hashCode

hashCode()是HashTable、HashMap和HashSet使用的。hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。

hash散列算法,使得在hash表中查找一个记录速度变O(1). 每个记录都有自己的hashcode,散列算法按照hashcode把记录放置在合适的位置. 在查找一个记录,首先先通过hashcode快速定位记录的位置.然后再通过equals来比较是否相等。如果hashcode没找到,则必定不equal,元素不存在于哈希表中;即使找到了,也只需执行hashcode相同的几个元素的equal,如果不equal,还是不存在哈希表中。

2.1 hashcode重写默认实现

来看看这个例子,

import java.util.HashSet;import java.util.Set;public class EqualTest {    public static void main(String[] args) {        Person p1 = new Person();        Person p2 = new Person();        p1.setId("1"); p1.setName("xwq");        p2.setId("1"); p2.setName("xwq");        Set<Person> s = new HashSet<Person>();        s.add(p1);        s.add(p2);        System.out.println(s);    }}

上面的程序输出的结果是两个。
这里写图片描述
如果两个employee对象equals返回true,Set中应该只存储一个对象才对,问题在哪里呢?
我们忘掉了第二个重要的方法hashCode(),因为hashCode()的默认实现是对象在内存中的内存地址,所以p1和p2都被插入到HashSet中。
如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。
这里写图片描述

需要注意记住的事情

  • 尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用人员id。
  • eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)
  • 任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。
  • 两者必须同时重写。

当使用ORM的时候特别要注意的

  • 如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。
  • 例如在我们的例子中,如果我们使用p1.id == p2.id则可能会出现这个问题,但是我们使用p1.getId() == p2.getId()就不会出现这个问题。
0 0