首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

为啥重写 equals 和 hashCode 方法(一)

2012-12-26 
为什么重写 equals 和 hashCode 方法(一)Object、equals()、hashCode()众所周知,Object是所有类的父类。但,我

为什么重写 equals 和 hashCode 方法(一)

Object、equals()、hashCode()

众所周知,Object是所有类的父类。但,我们在实际开发中自定义自己类时,往往需要重写Object中equals和hashCode方法。为什么呢?首先看看Object的API吧。

Object类中原始写法是:

public boolean equals(Object obj) {

??? ????????? return (this == obj);

?}

可见,原始equals比较的是2个对象的“内存地址”。但,我们往往是需要判断的是“逻辑上的内容”是否相等,如:StringIntegerMath...等等,时而我们关心是逻辑内容上是否相等,而不关心是否指向同一对象,所以所要重写。

再者,尽管Object是一个具体的类,但是设计它主要是为了扩展。它所要的非final方法(equals hashCode toString clonefinalize)都有通用约定(general contract),因为它们被设计成要被覆盖(override)的。任何一个类,它在覆盖这些方法的时候,都有责任遵守这些通用的约定;如果不能做到这一点,其它依赖这些约定的类(例如HashMapHashSet)就无法结合该类一起正常运行。

?????? JDKAPI上重写equals约定如下:

自反性:

对于任何非空引用值 x,x.equals(x) 都应返回 true。

对称性:

对于任何非空引用值 x 和 y,当且仅当y.equals(x) 返回 true 时,x.equals(y)才应返回 true。

传递性:

对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。

一致性:

对于任何非空引用值 x 和 y,多次调用x.equals(y) 始终返回 true 或始终返回false,前提是对象上 equals 比较中所用的信息没有被修改。

对于任何非空引用值 x,x.equals(null) 都应返回 false

同时,API规定“当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码“所以也要重写hashCode方法。

??? public native int hashCode();

说明是一个本地方法,它的实现是根据本地机器相关的,方法返回的是对象的地址值。时而重写hashCode一是为了遵守API约定,二是重点提高对象比较时效率。

因为,在java集合对象中比较对象是这样的,如HashSet中是不可以放入重复对象的,那么在HashSet中又是怎样判定元素是否重复的呢?让我们看看源代码(首先要知道HashSet内部实际是通过HashMap封装的):

public boolean add(Object o) {//HashSet的add方法

??? ?????? return map.put(o, PRESENT)==null;

??? }

?

public Object put(Object key, Objectvalue) {//HashMap的put方法

??????? Object k =maskNull(key);

??????? int hash = hash(k);

??????? int i = indexFor(hash, table.length);

???? ???for (Entry e = table[i]; e != null; e = e.next) {

??????????? if (e.hash == hash && eq(k, e.key)) {//从这里可见先比较hashcode

???????????????Object oldValue = e.value;

???????????????e.value = value;

???????????????e.recordAccess(this);

??????????????? return oldValue;

??????????? }

??????? }

??????? modCount++;

??????? addEntry(hash, k, value, i);

??????? return null;

??? }

?

所以在java的集合中,判断两个对象是否相等的规则是:
1,判断两个对象的hashCode是否相等
????? 如果不相等,认为两个对象也不相等,完毕
????? 如果相等,转入2
2,判断两个对象用equals运算是否相等
????? 如果不相等,认为两个对象也不相等
????? 如果相等,认为两个对象相等

?

为什么是两条准则,难道用第一条不行吗?不行,因为 hashCode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。

?

?

?

class Student{

??? String name;

??? int age;

???

??? Student(String name,int age){

?????? this.name=name;

?????? this.age=age;

??? }

//没有重写equalshashCode

}

?

/**

?* @author ydj

?* @version Apr 28, 2010 3:12:42 PM

?*/

public class OverEqualsHashcodeTest {

?

??? public static void main(String []args){

?????? Set<Student> set=new HashSet<Student>();

??????

?????? Student stu1=new Student("ydj",26);

?????? Student stu2=new Student("ydj",26);

?????? set.add(stu1);

?????? set.add(stu2);

??????

?????? System.out.println("set.size():"+set.size());

??? }

}

结果是:2.(这个无须解释)

?

?

2.现在重写equals方法如下:

??? public boolean equals(Object obj){

?????? System.out.println("--------equals()-----------:"+obj);

?????? if(obj==null){

?????????? return false;

?????? }

?????? if(!(obj instanceof Student)){

?????????? return false;

?????? }else {

?????????? Student oth=(Student)obj;

?????????? return this.age==oth.age&&this.name==oth.name;

?????? }

//???? return true;

?? }

结果是:2.(为什么依然是2呢?!为什么连equals方法都没调用呢)

分析:这就是为什么要重写hashCode的原因(相等对象必须具有相等的哈希码)。因为现在的hashCode依然返回各自对象的地址,就是说明此时的hashCode肯定不相等,故根本不会调用equals()。

?

3.重写hashCode方法如下:

public int hashCode(){

??? ??? ? int res=17;

?????? ? res=31*res+age;

?????? ? res=31*res+name.hashCode();

?????? ?

?????? ? return res;

?? }

结果是:1.

?---->继续第二页

热点排行