equals()方法和hashCode()的使用
1. 原生equals()方法的内容:
public boolean equals(Object obj) {
return (this == obj);
}
equals方法在其内部是调用了"==",所以说在不重写equals方法的情况下,equals方法是比较两个对象是否具有相同的引用,即是否指向了同一个内存地址。
而在业务系统中,有时候需要的并不是一种严格意义上的相等,而是业务上的对象相等。比如:如果两个对象中的id相等,那么就认为这两个对象是相等的。
2.重写equals()方法,需要遵守的通用约定
1>自反性
对于任意非null的引用值x,x.equals(x)必须返回true。
2>对称性
对于任意非null的引用值x、y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
3>一致性
对于任意非null的引用值x、y,只要equals方法的比较操作在对象中所用的信息没有发生改变,那么多次调用x.equals(y)应该 一致的返回true或false。
4>传递性
对于任意非null的引用值x、y、z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)必须返回true。
对于任意非null的引用值x,x.equals(null)必须返回false。
5> 非空性
对于任意null的引用值x,x.equals(null) 一定是false。
3.hashCode()
原生写法:
public native int hashCode();
hashCode是一个本地方法,他返回的是这个对象的内存地址。
这个方法返回对象的散列码,返回值是int类型的散列码。
对象的散列码是为了更好的支持基于哈希机制的Java集合类,例如 Hashtable, HashMap, HashSet 等。
重写hashCode方法时除了上述一致性约定,还有以下几点需要注意:
(1)返回的hash值是int型的,防止溢出。
(2)不同的对象返回的hash值应该尽量不同。(为了hashMap等集合的效率问题)
(3)《Java编程思想》中提到一种情况
“设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。
4. 重写equals()方法必须重写hashCode()方法
原因:hashCode()方法在Object规范中的通用约定第二条,相等的对象必须具有相等的散列码。
1>在应用运行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么多次调用该对象的equals方法应该始终如一的返回同一个整数。在同一个应用程序的多次执行过程中,每次执行equals方法所返回的整数可以不一致。
2>如果两个对象使用equals(Object)方法比较是相等的,那么调用这两个对象中的任意一个对象的hashCode方法都必须产生相同的一个整数结果。
3>如果两个对象使用equals(Object)方法比较是不相等的,那么调用这两个对象中的任意一个对象的hashCode方法,则不一定要产生不同的整数结果。如果给不同的对象产生不同的hash码,有可能提高散列表性能(比如往HashMap中添加数据时,具体添加到哪个桶中,就是根据(table.length - 1) & hash来计算的)。
如果一个类重写了equals方法但是没有重写hashCode方法,那么该类无法结合所有基于散列的集合(HashMap,HashSet)一起正常运作。
5.写法
31*N可以被编译器优化为左移5位后减1即31*N =(N<<5)-1,有较高的性能。使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits! 所以从效率上 它是2的5次减1,对计算机来说2的乘除操作只需要做位移操作,例如*32就是左移5位。 也就是说31对计算机的角度来说运算更快、切占内存不多不少,而且形成惯例,虚拟机甚至都专门对他做了优化。所以常用31做系数算hashcode
下一篇: LitsModer —— 开发日志(上)