effective_java之一:覆写 equals 和 hasCode 方法

时间:2022-10-22 16:19:33

提升下自己的表达及总结能力,也许若干年后可以去做一名合格的讲师~~~

一、覆写equals方法

为什么要覆写equals方法呢?覆写的原则是什么呢?原因以下:

有时候做到 两个实例对象在 逻辑上是否相等 时 就要覆写 equals 方法了。比如两个Person年龄、姓名、性别都相等时可以认为是一个人(但显示生活是:身份证ID相同的才是同一个人),这时就要覆写equals方法了:

        @override

public booleanequals(Object obj){

if(obj==null){return false ;} //---line 2 

if(!obj instanceof Person){return false ;}   //---line 3

Person this_person  = (Person) obj ; //--line 4

if(this.age = this_person.age&&this.name = this_person.name&&this.sex=this_persion.sex){return ture ;}

return false ;

}

        一定不要漏洞第三行 instanceof 判断,否则如果参数obj不是Person类型会抛出 类型匹配异常ClassCaseException.

好了,虾米是如何覆写equals方法,需要遵守的约定是什么?

1、一致性

line 2 如果参数对象是null空值要返回false.line 3 参数对象不是待比较对象的类型or待比较对象的子类型要返回false.

2、自反性

x.equals(y)==true 则 y.equals(x)也要返回ture.这点就不在此赘述了。

3、传递性

x.equals(y)==true , y.equals(z)==true ,则 x.equals(z)也要返回true.

默认的equals方法比较的是引用的内存地址是否相等。JDK源码如下:

 public boolean equals(Object obj) {
effective_java之一:覆写 equals 和 hasCode 方法
        return (this == obj);
effective_java之一:覆写 equals 和 hasCode 方法
    }
int 、float 等基本类型比较的是值,因为内部重写了equals方法。 String 首先比较的是引用的是否是同一块内存地址,是则返回true;不是比较内容是否相等。所以比较的也是值。付String类equals源码:  public boolean equals(Object anObject) {
1014        if (this == anObject) {
1015            return true;
1016        }
1017        if (anObject instanceof String) {
1018            String anotherString = (String)anObject;
1019            int n = count;
1020            if (n == anotherString.count) {
1021                char v1[] = value;
1022                char v2[] = anotherString.value;
1023                int i = offset;
1024                int j = anotherString.offset;
1025                while (n-- != 0) {
1026                    if (v1[i++] != v2[j++])
1027                        return false;
1028                }
1029                return true;
1030            }
1031        }
1032        return false;
1033    }
其他对象比较的是引用地址。

二、覆写hasCode方法

为什么要覆写hasCode方法,覆写原则又是什么呢?

当我们覆写了equals方法后,使用非has的集合时如List,没关系可以正常存取,因为List不关注待add的对象是否已经存在。但是在使用Set、Map等只能保存唯一的实例对象时,add对象时怎么判断该对象是否已存在呢?没错,就是通过hasCode进行判断的(插一曲:JDK源码为什么不用equals方法判断呢?我也不知道,也许是因为hasCode本身就是快速存取散列数据的原因吧)。

所以我们在重写hasCode时要遵循第一条约定:相同的对象,hasCode值要相等;不相同的对象的hasCode值尽量分不到不同的has桶中;hasCode不相等的两个对象,对象的equalse返回true.代码就是:

x.equals(y)==true 则 x.hasCode == y.hasCode ;

        x.hasCode != y.hasCode ,则 x.equals(y) == false ;

      如果只覆写equals而不覆写hasCode ,就会导致两个逻辑相等的对象的hasCode值不相等。这样Set\Map就会认为这是两个不同的对象,造成混乱。

      如果两个对象不相等,那么hasCode值尽量也不要相等。设计者设计hasCode的初衷就是为了更快的存取散列数据,所以 Set  Map等has集合比较和取对象都是根据hasCode的值。所以hasCode不等的对象会被分配到不同的has桶中。

那么如何来重写hasCode方法呢,原则又是什么呢?

针对对象中的每个域做如下转换:

1.byte、char、short、int 做 (int)val转换

2、float 做 Float.toBigInts(f)

3、long 、double、待补充


最后综合各域:

result = 31*result + c ;

做31*result的目的是让 字母相同但是排列顺序不同的字符串的has值不同。31是素数还是基数,如果是偶数,乘以2就是左移数据,容易造成溢出。个人理解是:31==2~5 - 1  最小的素基数。