在
Java
语言中,
equals()
和
hashCode()
两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个。在多数情况下,这两个函数是不用考虑的,直接使用它们的默认设计就可以了。但是在一些情况下,这两个函数最好是自己设计,才能确保整个程序的正常运行。最常见的是当一个对象被加入集合对象(
collection object
)时,这两个函数必须自己设计。更
细化的定义是:如果你想将一个对象
A
放入另一个
Collection Object B
里,或者使用这个对象
A
为查找一个元对象在
Collection Object B
里位置的
key
,并支持是否容纳,删除
Collection Object B
里的元对象这样的操作,那么,
equals()
和
hashCode()
函数必须开发者自己定义。其他情
况下,这两个函数是不需要定义的。
equals():
它是用于进行两个对象的比较的,是对象内容的
比较,当然也能用于进行对象参阅值的比较。什么是对象参阅值的比较?就
是两个参阅变量的值得比较,我们
都知道参阅变量的值其实就是一个数字,这个数字可以看成是鉴别不同对象的代号。两个对象参阅值的比较,就是两个数字的比较,两个代号的比较。这种比较是默
认的对象比较方式,在
Object
这个对象中,这种方式就已经设计好了。所以你也不用自己来重写,浪费不必要的时间。
对象内容的比较才是设计
equals()
的真正目的,
Java
语言对
equals()
的要求如下,这些要求是必须遵循的。否则,你就不该浪费时间:
对称性:如果
x.equals(y)
返回是
“true”
,那么
y.equals(x)
也应该返回是
“true”
。
反射性:
x.equals(x)
必须返回是
“true”
。
类推性:如果
x.equals(y)
返回是
“true”
,而且
y.equals(z)
返回是
“true”
,那么
z.equals(x)
也应该返回是
“true”
。
还有一致性:如果
x.equals(y)
返回是
“true”
,只要
x
和
y
内容一直不变,不管你重复
x.equals(y)
多少次,返回都是
“true”
。
任何情况下,
x.equals(null)
,永远返回是
“false”
;
x.equals(
和
x
不同类型的对象
)
永远返回是
“false”
。
hashCode():
这个函数返回的就是一个用来进行 hash 操作的整型代号,请不要把这个代号和前面所说的参阅变量所代表的代号弄混了。后者不仅仅是个代号还具有在内存中才查找对 象的位置的功能。 hashCode() 所返回的值是用来分类对象在一些特定的 Collection 对象中的位置。这些对象是 HashMap, Hashtable, HashSet ,等等。这个函数和上面的 equals() 函数必须自己设计,用来协助 HashMap, Hashtable, HashSet ,等等对自己所收集的大量对象进行搜寻和定位。
这些 Collection 对象究竟如何工作的,想象每个元对象 hashCode 是一个箱子的 编码,按照编码,每个元对象就是根据 hashCode() 提供的代号归入相应的箱子里。所有的箱子加起来就是一个 HashSet , HashMap ,或 Hashtable 对象,我们需要寻找一个元对象时,先看它的代码,就是 hashCode() 返回的整型值,这样我们找到它所在的箱子,然后在箱子里,每 个元对象都拿出来一个个和我们要找的对象进行对比,如果两个对象的内容相等,我们的搜寻也就结束。这种操作需要两个重要的信息,一是对象的 hashCode() ,还有一个是对象内容对比的结果。
hashCode() 的返回值和 equals() 的关系如下:
这个函数返回的就是一个用来进行 hash 操作的整型代号,请不要把这个代号和前面所说的参阅变量所代表的代号弄混了。后者不仅仅是个代号还具有在内存中才查找对 象的位置的功能。 hashCode() 所返回的值是用来分类对象在一些特定的 Collection 对象中的位置。这些对象是 HashMap, Hashtable, HashSet ,等等。这个函数和上面的 equals() 函数必须自己设计,用来协助 HashMap, Hashtable, HashSet ,等等对自己所收集的大量对象进行搜寻和定位。
这些 Collection 对象究竟如何工作的,想象每个元对象 hashCode 是一个箱子的 编码,按照编码,每个元对象就是根据 hashCode() 提供的代号归入相应的箱子里。所有的箱子加起来就是一个 HashSet , HashMap ,或 Hashtable 对象,我们需要寻找一个元对象时,先看它的代码,就是 hashCode() 返回的整型值,这样我们找到它所在的箱子,然后在箱子里,每 个元对象都拿出来一个个和我们要找的对象进行对比,如果两个对象的内容相等,我们的搜寻也就结束。这种操作需要两个重要的信息,一是对象的 hashCode() ,还有一个是对象内容对比的结果。
hashCode() 的返回值和 equals() 的关系如下:
如果
x.equals(y)
返回
“true”
,那么
x
和
y
的
hashCode()
必须相等。
如果
x.equals(y)
返回
“false”
,那么
x
和
y
的
hashCode()
有可能相等,也有可能不等。
为什么这两个规则是这样的,原因其实很简单,拿 HashSet 来说吧, HashSet 可以拥有一个或更多的箱子,在同一个箱子中可以有一个 或更多的独特元对象( HashSet 所容纳的必须是独特的元对象)。这个例子说明一个元对象可以和其他不同的元对象拥有相同的 hashCode 。但是一个 元对象只能和拥有同样内容的元对象相等。所以这两个规则必须成立。 ( 在实际的某个集合对象如 HashSet set.contains(object o); 时,是先通过 hashcode() 找到 “ 箱子 ” ,在根据 equals 判断对象内容 是否相等,从而判断集合对象是否包含某个元对象)
设计这两个函数所要注意到的:
如果你设计的对象类型并不使用于 Collection 对象,那么没有必要自己再设计这两个函数的处理方式。这是正确的面向对象设计方法,任何用户一时用不到的功能,就先不要设计,以免给日后功能扩展带来麻烦。
如果你在设计时想别出心裁,不遵守以上的两套规则,那么劝你还是不要做这样想入非非的事。我还没有遇到过哪一个开发者和我说设计这两个函数要违背前面说的两个规则,我碰到这些违反规则的情况时,都是作为设计错误处理。
当一个对象类型作为
Collection
对象的元对象时,这个对象应该拥有自己处理
equals()
,和
/
或处理
hashCode()
的设计,而且要遵守前面所说
的两种原则。
equals()
先要查
null
和是否是同一类型。查同一类型是为了避免出现
ClassCastException
这样的异常给丢出来。查
null
是为了避免出现
NullPointerException
这样的异常给丢出来。
如果你的对象里面容纳的数据过多,那么这两个函数
equals()
和
hashCode()
将会变得效率低。如果对象中拥有无法
serialized
的数据,
equals()
有可能在操作中出现错误。想象
一个对象
x
,它的一个整型数据是
transient
型(不能被
serialize
成二进制数据流)。然而
equals()
和
hashCode()
都有依靠
这个整型数据,那么,这个对象在
serialization
之前和之后,是否一样?答案是不一样。因为
serialization
之前的整型数据是有效的
数据,在
serialization
之后,这个整型数据的值并没有存储下来,再重新由二进制数据流转换成对象后,两者(对象在
serialization
之前和之后)的状态已经不同了。这也是要注意的。
知道以上这些能够帮助你:
1. 进行更好的设计和开发。
2. 进行更好的测试案例开发。
3. 在面试过程中让面试者对你的学识渊博感到满意。
知道以上这些能够帮助你:
1. 进行更好的设计和开发。
2. 进行更好的测试案例开发。
3. 在面试过程中让面试者对你的学识渊博感到满意。
在
Hibernate
中,
POJO
类要重写
hashcode()
方法和
equals()
方法。为什么呢?
1
,重点是
equals
,重写
hashCode
只是技术要求(为了提高效率)
2
,为什么要重写
equals
呢,因为在
java
的集合框架中,是通过
equals
来判断两个对象是否相等的
3
,在
hibernate
中,经常使用
set
集合来保存相关对象,而
set
集合是不允许重复的,但是下面的程序,判断一下运行结果:
Set user = new HashSet();
user.add(new Address("http://hi.baidu.com/yangwen_yw"));
user.add(new Address("http://hi.baidu.com/yangwen_yw"));
System.out.println(user.size());
user.add(new Address("http://hi.baidu.com/yangwen_yw"));
user.add(new Address("http://hi.baidu.com/yangwen_yw"));
System.out.println(user.size());
上面程序的运行结果取决于
Address
类是否重写了
equals
方法。
如果没有重写,默认
equals
是比较地址,那么这两个
address
对象不一样,输出
2
,意味着
hibernate
会认为这是两个对象,再接下来的持久化过程中可能会出错。
如果重写了
equals
,比如按照主键(
address
空间地址)比较,那么这两个对象是一样的,输出
1