HashCode方法和Equals实现

当我们的使用任何Java的集合进行去重的时候,比如Set集合,比如JDK1.8的stream.distinct,Jvm都需要对不同的数据类型进行相等判断,如果我们往集合存入的是基本数据类型,那么很容易就可以判断相等,但是如果我们传入的是一个对象,那么需要重写相应类的equals方法和hashcode方法。

在JVM当中,比较两个对象的时候,是先调用相应类的hashCode方法计算两个对象的HashCode码,如果两个对象的HashCode码能够得出相同的两个值,那么再调用equals方法进行相等判断,如果hashCode码相同并且equals方法返回True,那么JVM就认为这两个对象是相等的。

然而我们应该如何去设计类的equals和hashCode方法呢?

一、equals方法

《Effective Java》中讲到,Java对Object.equals方法有以下必须要遵守的约定:

equals方法必须要满足以下几个等价关系

1.自反性:对于非null的x,x.equals(x)必须返回true

2.对称性:对于非null的x和y,当y.equals(x)返回true时,x.equals(y)必须返回true

3.传递性:对于非null的x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)必须返回true

4.一致性:对于非null的x和y,只要equals和x和y没有被修改,多次调用equals方法和一次调用的结果相同

在《Effective Java》中,作者针对每一条规则各列出了错误的equals写法,最后,给出了正确的equals写法:下面是Person类的equals方法 

@Override
    public boolean equals(Object obj){
        if (obj == this){
            return true;
        }
        if (!(obj instanceof Person)){
            return false;
        }
        Person person = (Person)obj;
        return person.age.equals(this.age)&&person.name.equals(this.name)&&person.sex.equals(this.sex);
    }

注意点:

(1)传入的参数必须要是Object类,才可以覆盖Object类的equals方法

(2)使用instanceof关键字判断对象是否为同族类,如果不是同族类向下转型会失败

(3)判断完成之后将传入的Object向下转型为当前类,以便调用相应类的get方法

(4)满足相等条件的返回true

二、hashCode方法

hashCode方法同样有以下三条需要遵守的约定:

1.对于同一个对象,多次调用hashCode方法应该始终返回同一个整数

2.如果两个对象equals判断相等,那么hashCode方法产生的整数也必须要相同

3.如果equals方法比较不相等,那么hashCode方法产生的整数也可以相等

@Override
    public int hashCode() {
        int n=31;
        n = n * 31 + this.name.hashCode();
        n = n * 31 + this.age.hashCode();
        n = n * 31 + this.sex.hashCode();
        return n;
    }

这里我name、age和sex属性都是String对象,所以计算的是他们的hashCode码。

 

对于每一个equals方法涉及的属性f

如果是boolean类型,计算f?1:0就可以了。

如果是byte、char、short或者int类型,则计算(int)f就可以。

如果是long类型,则计算(int)(f^(f>>>32))。

如果是double类型,则计算Double.doubleToLongBits(f),然后按照long类型处理就可以了。

如果是对象引用或者数组,则可以使用f.hashCode()。

 

在JVM层面,n*31会自动被优化成(n<<5)-i,所以这样计算性能很高。也可以乘以32或者33或者其他数,但习惯使用奇素数,Lombok自动生成的hashCode方法是使用的n*59。

如果一个类是不变类,并且计算hashCode码的开销也比较大,那么可以考虑将hashCode码缓存在对象内部,计算一次享用终生。

private volatile int hashCode;

@Override
public int hashCode() {
    int n = hashCode;
    if(n == 0){
        n = 31;
        n = n = n * 31 + this.name.hashCode();
        n = n * 31 + this.age.hashCode();
        n = n * 31 + this.sex.hashCode();
        hashCode = n;
    }
    return n;
}

以上是hashCode和equals方法的设计原理,实际使用中,使用lombok自动生成的hashCode方法和equals就可以满足日常使用需求,附上找到的一篇lombok生成的hashCode和equals方法反编译:

https://blog.csdn.net/sunct/article/details/88741574

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值