为什么要重写对象的hashCode方法和equals方法

1、为什么要重写equals方法

参考Java中使用==进行比较,比较的是什么内容?

2、为什么要重写hashCode方法

判断2个对象是否相等我们常用的是调用对象的equals方法,所以重写了equals方法不就够用了吗?为啥还要重写hashCode方法?原因是在某些地方,判断2个对象相等是hashCode和equals一起判断的,如HashMap。

简单了解下HashMap的存储方式:

(1)HashMap底层是以数组方式进行存储的。

(2)将key-value键值对作为数组的一个元素进行存储。

(3)key-value都是Map.Entry中的属性。其中将key的值进行hash之后进行存储,即每一个key都是计算hash值,然后再存储。每一个hash值对应一个数组下标,数组下标是根据hash值和数组长度计算得来的。

(4)由于不同的key值可能具有相同的hash值,即一个数组的某个位置出现两个相同的元素,对于这种情况,HashMap采用链表的形式进行存储。即使用链地址法来解决冲突问题。

所以,在HashMap中,如果key的对象没有重写hashCode的话,2个我们认为是一样的对象因为hash计算出来的值是不一样的,所以在HashMap中他会被人为是2个不同的key。

3、hashCode方法和equals方法默认实现是什么?

Java对象默认是继承Object的,所以不重写默认使用的是Object对象的hashCode方法和equals方法,查看Object对象源码:

public native int hashCode();

hashCode有native标识,他是一个jni的方法,所以他的实现是外部实现,可能不同的操作系统会有不同的实现,具体不深入。

public boolean equals(Object obj) {
    return (this == obj);
}

equals默认直接对比对象的内存地址。

4、那么如果不重写hashCode方法和equals方法会怎么样呢?我们这里举个简单的代码例子。

Book对象代码(我们认为在业务上,只要书名一致就认为对象是相等的,不管价格跟出售平台)

@Data
@ToString
public class Book {

    /**
     * 书名
     */
    private String name;

    /**
     * 价格
     */
    private String price;

    /**
     * 出售平台
     */
    private String seller;

    public Book(String name, String price, String seller) {
        this.name = name;
        this.price = price;
        this.seller = seller;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Objects.equals(name, book.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

}

测试代码(我们简单的使用map的key为书,value为作者来进行测试)

public class Test {

    public static void main(String[] args) {
        Book book1 = new Book("Java编程思想","100","淘宝");
        Book book2 = new Book("Java编程思想","110","京东");
        System.out.println("equals()\t\t\t\t\t"+book1.equals(book2));

        System.out.println("book1.getName().hashCode()\t"+book1.getName().hashCode());
        System.out.println("book2.getName().hashCode()\t"+book2.getName().hashCode());
        System.out.println("book1.hashCode()\t\t\t"+book1.hashCode());
        System.out.println("book2.hashCode()\t\t\t"+book2.hashCode());

        Map<Book, String> map = new HashMap<>();
        System.out.println("\nmap为 书名->作家 映射 执行map.put(book1, \"Bruce Eckel\");");
        map.put(book1, "Bruce Eckel");
        System.out.println("map.get(book1)\t\t\t\t"+map.get(book1));
        System.out.println("map.get(book2)\t\t\t\t"+map.get(book2));
    }

}

hashCode和equals都重写的执行结果

equals()					true
book1.getName().hashCode()	480343597
book2.getName().hashCode()	480343597
book1.hashCode()			480343628
book2.hashCode()			480343628

map为 书名->作家 映射 执行map.put(book1, "Bruce Eckel");
map.get(book1)				Bruce Eckel
map.get(book2)				Bruce Eckel

注释掉Book对象中的hashCode方法,即只重写了equals方法的执行结果

equals()					true
book1.getName().hashCode()	480343597
book2.getName().hashCode()	480343597
book1.hashCode()			752848266
book2.hashCode()			815033865

map为 书名->作家 映射 执行map.put(book1, "Bruce Eckel");
map.get(book1)				Bruce Eckel
map.get(book2)				null

注释掉Book对象中的equals方法,即只重写了hashCode方法的执行结果

equals()					false
book1.getName().hashCode()	480343597
book2.getName().hashCode()	480343597
book1.hashCode()			480343628
book2.hashCode()			480343628

map为 书名->作家 映射 执行map.put(book1, "Bruce Eckel");
map.get(book1)				Bruce Eckel
map.get(book2)				null

注释掉Book对象中的hashCode和equals方法的执行结果

equals()					false
book1.getName().hashCode()	480343597
book2.getName().hashCode()	480343597
book1.hashCode()			1337752440
book2.hashCode()			1337503432

map为 书名->作家 映射 执行map.put(book1, "Bruce Eckel");
map.get(book1)				Bruce Eckel
map.get(book2)				null

参考资料:

https://blog.csdn.net/linjiaen20/article/details/82762933

https://developer.51cto.com/art/201908/601406.htm

支付宝搜索:344355 领取随机红包

如果文章对您有帮助,欢迎给作者打赏