你真的懂hashCode和equals吗???

前言

今天还在感概说,可能三年后我的简历和刚毕业那会没啥区别,因为现在已经毕业快一年了,上次重写简历除多了两个项目经验外没啥区别,而简历上面的技术我还需要好久才能掌握。


一、关于hashCode和equals我们知道下面几点
  • hashCode相同的两个对象不一定相同
  • equals相同的两个对象,hashCode一定相同
  • 重写equals一定要重写hashCode

其它的

  • == 比较的是两个对象的内存地址值
  • equals比较的是两个对象里面的值是否相同,没有重写的equals和==一样

二、hashCode的由来

我们知道hashCode是Object的方法,但是我们点进去可以看到它是一个native方法,也就是不由我们Java去实现,底层是C写的,我们理解默认的hashCode是基于内存地址生成的

public native int hashCode();

三、如何重写hashCode

重写hashCode的基本规则如下:

  • 在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
  • 当两个对象通过equals()方法比较返回true时,则两个对象的hashCode()方法返回相等的值。
  • 对象用作equals()方法比较标准的Field,都应该用来计算hashCode值。

我们根据每一个属性的hashCode,然后加上一定的规则计算出新的hashCode

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
    result = prime * result + ((getFromUserId() == null) ? 0 : getFromUserId().hashCode());
    result = prime * result + ((getFromUserName() == null) ? 0 : getFromUserName().hashCode());
    result = prime * result + ((getToUserId() == null) ? 0 : getToUserId().hashCode());
    result = prime * result + ((getContent() == null) ? 0 : getContent().hashCode());
    result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
    result = prime * result + ((getUnReadFlag() == null) ? 0 : getUnReadFlag().hashCode());
    return result;
}

四、如何重写equals
  1. 直接判断两个对象的地址值(用 ==)
  2. 判断要比较的对象是否为null
  3. 判断两个对象是否是一个类(比较getClass)
  4. 调用对象的每个属性的equals进行比较,考虑到对象的属性可能为null,所以我们要先判断是否为null
@Override
public boolean equals(Object that) {
    if (this == that) {
        return true;
    }
    if (that == null) {
        return false;
    }
    if (getClass() != that.getClass()) {
        return false;
    }
    MsgInfo other = (MsgInfo) that;
    return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
        && (this.getFromUserId() == null ? other.getFromUserId() == null : this.getFromUserId().equals(other.getFromUserId()))
        && (this.getFromUserName() == null ? other.getFromUserName() == null : this.getFromUserName().equals(other.getFromUserName()))
        && (this.getToUserId() == null ? other.getToUserId() == null : this.getToUserId().equals(other.getToUserId()))
        && (this.getContent() == null ? other.getContent() == null : this.getContent().equals(other.getContent()))
        && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
        && (this.getUnReadFlag() == null ? other.getUnReadFlag() == null : this.getUnReadFlag().equals(other.getUnReadFlag()));
}

注:上面的hashCode和equals都是IDEA自动生成的


五、何时需要重写equals和hashCode

我们都知道当我们在使用到HashMap这样的特殊容器的时候需要,但具体为什么需要呢?

有一个面试题是这样的:

Q: 为什么String这样的包装数据类型更适合作为hashMap的key

A:因为String这样的包装数据类型重写的hashCode和equals


事实上我们也基本上是使用String作为hashMap的key,考虑下如果我们使用自定义的类作为key会发生什么呢?

import java.util.HashMap;
import java.util.Map;

public class Test {

    private String name;

    private Integer age;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public static Map<Test,Object> map = new HashMap<>();

    static {
        Test test = new Test();
        test.setName("小道仙");
        test.setAge(18);
        map.put(test,"哇哈哈");
    }
}

思考一下你有什么办法可以获取这个map里面唯一的数据呢?(除去便利,你可以使用 map.get() 方法吗?)

public static void main(String[] args) {
    Test test = new Test();
    test.setName("小道仙");
    test.setAge(18);
    System.out.println(map.get(test));
}

上面的这种方式打印出来的是null,在我们没有重写hashCode和equals之前,我们重写new的对象和之前设置进去的对象它们的hashCode和equals永不相同。

所以当我们使用自定义对象作为hashKey的时候就一定要重写hashCode和equals。


六、为什么重写了equals就一定要重写hashCode

因为的hashMap等类似的容器,每次查找都需要进行对比,如果直接使用equals当然可以,但是代价太大了,所以策略是先对比hashCode,hashCode一样再对比equals。

而不重写hashCode,默认的生成和内存地址有关,永不相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值