重写 hashCode 为什么还需要重写 equals方法

2023-11-18

为什么重写hashCode 还需要重写 equals方法

(或者两者反过来说)

众所周知,根据生成的哈希码将数据离散开来,可以是存取元素更快。调用 Object.hashCode() 生成哈希值;由于比可避免地会存在哈希值冲突的情况,因此当 hashCode 相同时,还需要再调用 equals 进行一次值的比较;但是,若 hashCode 不同,将直接判定 Object 不同,跳过 equals,这加快了冲突处理效率。

首先可以了解下,hashCode 是一个本地方法,其相关源码:

VM_ENTRY(joint, JVM_IHashCode(JNIEnv* env, jobject handle))
	JVMWrapper(“JVM_IHashCode”);
	return Handel == NULL ? 0 : ObjectSynchronizer :: FastHashCode
		(THREAD, JNIHandles :: resolve_non_null(handle));
VM_END

从代码上可以分析到hashCode 就是根据对象的地址进行相关计算得到 int 类型的数值。

mark = monitor->header();
assert(mark->is_neutral(), “invariant”);
hash = mark->hash();

intptr_t hash() const {
	return mask_bits(value() >> hash_shift, hash_mask);
}

关于哈希码的相关知识点

  • 解决冲突的方法:
  1. 开放式地址法

    • 线性探索再散列

      例:用线性探测再散列实现 14、5、21、16、17、15,在0 ~ 10的存储空间中的存放散列函数为H(key)=key%9。

      0 1 2 3 4 5 6 7 8 9 10
      14 5 15
      • H(14)=14%9=5

      • H(5)=5%9=5(冲突) 则(5+1)%11=6

        …(省略)

      • H(15)=15%9=6(冲突) 则(6+1)%11=7

    • 二次探索再散列

      遇上差不多,只是平方了

      d1 = H(key) di = (d1 + i2) % m i = 1,2,3…

    • 随机探索再散列

      d1 = H(key) di=(d1+R)%m R是给定的随机数

  2. 链地址法

    • 每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被 分配到同一个索引上的多个节点可以用这个单向链表进行存储.

      继上题条件

      将每个冲突的元素放置对应位置的链表后

    • 填充因子:存储的数据 / 存储空间长度 = 7 / 11 (越小冲突可能性越小,越大冲突可能性越大)

哪里大佬发现有问题的跟小弟说,小弟立马改


被问到重写hashCode后为什么还要重写equals

感觉这么问会好说一点,因为可以说即使两个对象的 hashCode 相等,但未必两者内容也相同,这时候需要重写 equals 来判断对象的内容是否相同。

关于 equals 相关知识点可以查看 【equals 与 == 有什么不同】

但是被问到重写 equals 重写后有什么必要重写 hashCode 该怎么回答

在此之前是比较懵的,因为当时还没彻底了解过 equals,认为

可以举个例子 Set 例子,我们编写一个 MyTest 类重写 equals 没重写 hashCode,new出三个对象,分别添加到Set中。

import java.util.*;

public class Main {
    public static void main(String[] args) {

        Set<MyTest> set = new HashSet<>();

        set.add(new MyTest(1,"baven"));
        set.add(new MyTest(1,"baven"));
        set.add(new MyTest(1,"baven"));

        System.out.println(set.size());		// 大小为 3
    }
}
class MyTest{

    int id;
    String name;

    MyTest(int id, String name){
        this.id = id;
        this.name = name;
    }

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

最终大小并不为1,而是为3。原因是因为,new出的对象地址都是不相同的,根据 equals 的特性,虽然比较得出内容相等但是弟子不同。

所以这时候有一条要求:

如果两个对象的 equals 的结果是相等的,则两个对象的 hashCode 的返回结果也必须相同。

注:是必须,而不是一定或是什么判断得出的语气,是必须!铁要求!

所以这时候,在重看Set的这个例子,如果重写了hashCode,按照要求得出哈希码也相等,那么Set的大小就会为 1。


在这里插入图片描述
祝早日new到对象

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

重写 hashCode 为什么还需要重写 equals方法 的相关文章

随机推荐