哈希表由浅入深

哈希表由浅入深🔥

一、前置理解

哈希表

哈希表(也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。
哈希表底层就是数组。

hashCode

hashCode 称为 哈希码 也叫 散列码。是一个 int 值, 用来表示一个对象。
默认是通过地址转换的,一般重写让它是通过将该对象的某些信息进行转换而生成的。
hashCode方法 主要是为了给诸如HashMap这样的哈希表使用

哈希算法

简单的理解就是把 hashcode 转成下标的规则方式。

二、哈希表案例

举例: 我存储了一个学校的学生对象。现在有个需求, 我要查找姓名叫张三的学生。

数组解决

使用数组存储学生,然后从第一个元素遍历。 判断每一个学生的名字等不等于张三。
弊端 如果学生比较多。每次查询需要从第一个元素遍历,直到找到元素,效率太低了

哈希表解决

添加元素的时候,每个对象的 hashcode 通过算法转成下标。该学生对象保存在该下标。
我要找张三的话, 不管多少元素,直接把张三的 hashcode 转成下标, 通过下标一次查找就找到该元素了。

比如现在我使用的算法就是 (hashcode 对 数组长度取模),为了容易理解, 真正的算法肯定不是这样的。

在这里插入图片描述
弊端 可能会出现哈希冲突。

三、什么是哈希冲突

哈希冲突就是两个不同的元素,通过哈希函数得出的实际存储地址相同了。也叫哈希碰撞。

举个例子

数组长度是 10,张三对象的 hashcode 通过算法得到下标为 3, 李四对象的 hashcode 通过算法得到下标也是 3,这就是哈希冲突。

在这里插入图片描述

疑问:好的算法会哈希冲突吗

由于通过哈希函数产生的哈希值是有限的,拿 Java举例 hashcode的取值范围为 -2^31 到 2^31-1
而数据(对象)理论上是无穷的。
稍微思考一下就可以发现,既然数据是不定长的,但是哈希值是定长的,哈希值是有限集,而数据可以是无限多的。
所以“碰撞”是必然会发生的,一个成熟的哈希算法会有更好的防碰撞性。 但是还是会出现哈希冲突

四、解决哈希冲突

JDK1.7 实现方式

哈希冲突的解决方案有很多种,这里讲其中一种:链地址法
JDK 1.7 即之前版本 HashMap 使用的就是这种方式

在这里插入图片描述
当出现冲突时,在冲突的地址上生成一个链表,将冲突的元素的 key,通过equals进行比较。
这时候就算张三和李四冲突了。equals 比较他们不是同一个人,李四放在链表的第二个位置即可。是同一个人就不会添加重复元素张三了。
查找张三和李四的时候 还是通过 哈希算法 得到索引,再拿索引中链表的元素一个一个比较, 效率仍然很高

JDK1.8 实现方式

JDK1.7 中,如果链表过长的话,查询效率会降低,
JDK1.8 之后,又添加了红黑树,如果链表过长了会自动把该链表转成红黑树,从而提高效率

在这里插入图片描述

五、为什么必须重写 hashcode 和 equals

为什么重写 hashcode

hashcode 方式是Object 中的本地方法,返回的是对象地址转成的 int 值。
因为要保证相同的对象hashcode相同,所以需要重写。

1. 未重写hashcode

未重写hashcode

    public static void main(String[] args) {
        Student student = new Student("小明", 18);
        Student student2 = new Student("小红", 20);
        Student student3 = new Student("小红", 20);
        System.out.println(student.hashCode());
        System.out.println(student2.hashCode());
        System.out.println(student3.hashCode());
    }
1735600054
21685669
2133927002

在我们看来 student2student3 是同一个元素, 但是 hashcode 值却不同。
问题:
这样的话,使用 hashMap 保存数据就会导致添加的数据在数组中重复,因为他是通过 hashcode 确定索引位置的。

2. 重写hashcode

因为要保证相同的对象 hashcode 相同,所以必须要重写。
可以使用 IDEA 快速重写,大概重写规则就是和属性关联,属性相同的对象,hashcode相同。

	public static void main(String[] args) {
        Student student = new Student("小明", 18);
        Student student2 = new Student("小红", 20);
        Student student3 = new Student("小红", 20);
        System.out.println(student.hashCode());
        System.out.println(student2.hashCode());
        System.out.println(student3.hashCode());
    }
23458772
23653826
23653826

为什么重写equals

因为需要使用 equals 判断对象是否相等,避免一个链表上重复添加相同对象。

结论

重写 hashcode 保证数组中相同元素不重复
重写 equals 保证链表中相同元素不重复

  • equals相等,hashCode一定相等
  • equals不等,hashcode不一定不等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值