四、覆写equal():
四大特性:①自反性; 判断是否引用同一个对象;
②对称性; A.equals(B)相等,B.equals(A)也相等;
③传递性; A.equals(B)相等,B.equals(C)相等,那么A.equals(C)也相等;
④一致性; 两对象不变的情况下,A.equals(B)一直相等或者一直都不相等;
步骤:①使用==操作符检查 参数是否为同一个对象的引用;
②使用instanceof操作符检查 参数是否为正确的类型;
③把参数转换为正确的类型; 因为参数必须为Object类型,所以要强转;
④对于该类中的每个关键域,检查参数中的域是否与该对象中对应的域相匹配;
⑤审阅是否满足4大特性。
注意:覆盖equals时总要覆盖hashCode; HashMap、HashSet和Hashtable。
不要企图让equals方法过于智能;
不能将equals声明中的Object对象修改为其他的类型。
问题一:为什么equals声明中的参数对象一定要为Object?
原理:因为如果equals方法的参数不是Object类型的话,则属于是重载了Object.equals()。
所以当重载了Object.equals()时,遇到了继承的情况就不会调用同一个equals()了 。
实例:
public class Test1 extends Test2 {
public Test1 equals(Test1 test1) {
System.out.println("haha");
return null;
}
public static void main(String[] args) {
Test1 test1 = new Test1();
Test2 test2 = new Test2();
test1.equals(test2); //Object中的equals() ;
test2.equals(test1); //Object中的equals() ;
test1.equals(test1); //Test1中的equals() ; 只有这里打印出"haha"。
}
}
问题二:HashCode的作用?
原理:优点:“空间换时间”原则,提高了查找速率。因为先查找对应的hash code从小范围获取地址,然后在从桶中查找key值从而查找对应元素。
缺点:不适合多个数据相同的情况。因为极端情况在只有一个或两个桶,那么查找的速率跟一般存储一样,但是却浪费了空间。
javaSE6 约定:
①对象没改变,返回统一hashCode。
②如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
③如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。
原理:机器相同,相同程序,运行环境相同的情况下。对象值域相同则生成的hashCode也是相同的。反则则不然。
所以equals()相等的情况下hashCode()肯定相等,但是hashCode()相等的情况下equals()不一定相等。
问题三:覆盖equals()要覆盖hashCode()的必要性?
hashCode可变性:自定类型和某些没有覆盖hashCode()的类,不同的机器hashCode会不同。
必要性:当用到Hash表的时候必要!使到Hash表里面的每个桶里面的对象相等。
①集合框架, 为什么要覆写集合中元素类型equals()呢,因为在java的集合框架中,key值是通过equals来判断两个对象是否相等的。
②自定义的类,如果没有实现HashCode()方法的话,new出来的自定义类的地址是不一样的,那么对应的HashCode()也是不一样的,所以在Set里面可以插入两个值域相同但地址不同的对象。当然此时
的equals方法也不相等。
(2)String类:覆写Object
equals实例:
(1)Object类:仅仅判断引用是否相同
public boolean equals(Object obj) {
return (this == obj);
}
public boolean equals(Object anObject) {
if (this == anObject) { //引用是否相等
return true;
}
if (anObject instanceof String) { //是否是同一类
String anotherString = (String) anObject;
int n = count;
if (n == anotherString.count) { //长度是否一致
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) { //逐个字符判断是否相等
if (v1[i++] != v2[j++]) //基本类型按照ASCII码值判断相等。
return false;
}
return true;
}
}
return false;
}
hashCode实例:
(1)Object类:hashCode()是本地方法,不同机器hashCode()不同
public native int hashCode();
(2)String类:解释一下这个程序(String的API中写到):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)
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;
}