Java 重写equals()、hashCode()
java中的equals,==
在java中,比较两个基本类型的值是否相等,只需要==就可以完成任务(除了float、double),对于常用的引用类型String类型,java重写了equals、hashCode方法,可以让我们简单的用equals方法就能比较两个字符串是否相等。但是对于我们自己定义的类型,怎么能用equals方法就能判断出两个对象是否相等呢?这时候就要自己同时实现equals,hashCode方法了。
如何重写equals方法
在重写equals方法时,一定要使得所写的equals方法满足以下5种情况,才能保证程序的健壮性:
- 自反性 :A.equals(A)要返回true。
- 对称性:如果A.equals(B)返回true, 则B.equals(A)也要返回true.
- 传递性:如果A.equals(B)为true, B.equals(C)为true, 则A.equals(C)也要为true. 说白了就是 A = B , B = C , 那么A = C.
- 一致性:只要A,B对象的状态没有改变,A.equals(B)必须始终返回true.
- A.equals(null) 要返回false.
写一个例子来满足以上的5种情况。
比如我们有一个Dog类,有color、age、name三个属性
Class Dog{
private String color;
private String name;
private int age;
}
重写时遵循以下三步:
1.判断是否等于自身
if(obj==this)
return true;
2.使用instanceof运算符判断other是否为Dog类型的对象
if(!obj instanceof Dog)
return false;
3.比较Dog类中你自定义的属性,name,age,color
Dog o = (Dog)obj;
return o.name.equals(name)&&o.age.equals(age)&&o.color.equals(color);
上面的三步也是 < Effective Java>中推荐的步骤,基本可保证万无一失。
重写hashCode()方法
在重写了equals方法后,那么一定要记得重写hashCode方法,
<Effective Java>中给出了一个能最大程度上避免哈希冲突的写法,但我个人认为对于一般的应用来说没有必要搞的这么麻烦.如果你的应用中HashSet中需要存放上万上百万个对象时,那你应该严格遵循书中给定的方法.如果是写一个中小型的应用,那么下面的原则就已经足够使用了:
@Override
public int hashCode() {
int result = 17;
result = result * 31 + name.hashCode();
result = result * 31 + age;
return result;
}
其中int result = 17你也可以改成20, 50等等都可以.看到这里我突然有些好奇,想看一下String类中的hashCode()方法是如何实现的.查文档知:
"Returns a hash code for this string. The hash code for a String object is computed as s[0]31^(n-1) + s[1]31^(n-2) + ... + s[n-1]
using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.)"
/*返回哈希码,String的哈希码计算方式为s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*/
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
对每个字符的ASCII码计算n - 1次方然后再进行加和,可见Sun对hashCode的实现是很严谨的. 这样能最大程度避免2个不同的String会出现相同的hashCode的情况。
为什么要重写hashCode()
那为什么在重写equals方法时都要重写hashCode方法呢:
首先equals与hashcode间的关系是这样的:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
自我的理解:
如此设计是为了提高程序的效率才实现了hashcode方法。先进行hashcode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的,一个很好的例子就是在集合中的使用;
我们都知道java中的List集合是有序的,是可以重复的,而set集合是无序的,是不能重复的,那么怎么能保证不能被放入重复的元素呢,但靠equals方法一样比较的话,如果原来集合中以后又10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,欧码噶的,这个效率可想而知,因此hashcode就应遇而生了,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,只要就大大减少了equals的使用次数,执行效率就大大提高了。
继续上面的话题,为什么必须要重写hashcode方法,其实简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。