Hi ! 哈希,Hash

本文深入探讨了哈希表的概念,包括哈希值的生成与作用,以及解决哈希冲突的开放寻址法和拉链法。详细阐述了HashMap的实现,如其基于数组和链表的数据结构,以及当链表长度达到一定阈值时转为红黑树的优化。此外,还解析了哈希表的扩容机制,指出当负载因子达到特定比例时触发扩容,并介绍了扩容过程中数组的双倍增长及重新哈希的过程。
摘要由CSDN通过智能技术生成

1、什么是哈希值?

对哈希表就是通过将关键值,也就是key通过一个散列函数加工处理之后得到一个值,这个值就是数据在数组中存放的位置,我们就可以根据这个值快速的找到我们想要的数据

键值对 --> entry
其他的某些语言可能叫做键值对;
在JDK起了一个高大上的名字:entry;
本质上面是一个东西

1.1、关于哈希值

哈希表就是根据key值来通过哈希函数计算得到一个值;
这个值就是用来确定这个Entry要存放在哈希表中的位置的;
实际上这个值就是一个下标值,来确定放在数组的哪个位置上;

1.2、value 当然和 HashCode 不一样

1、从名字上来讲,不是一个东西;
2、value 是键值对里面的;
3、HashCode 是 key 经过哈希函数然后得到的,目的是找到entry 也就是键值对在哈希表中应该存储的下标,也就是存储的位置

1.3、解决哈希冲突的办法

1.3.1、开放寻址法

当前的哈希表中的位置被占用了,找一个旁边的位置,把相关的元素放进去;

1.3.2、拉链法

使用了数组和链表结合的方式,在键值对也就是 entry 里面加一个指向下一个元素的指针,进行元素的存放:

  • 当拉链里面的键值对个数 >= 8 的时候,将会把链表转换成为树的结构;
  • 当拉链里面的键值对个数 <= 6 的时候,保持原来的链表的形式;
  • 拉链的键值对个数是 7 的时候,为了避免缓冲,避免频繁的进行链表和树之间的转换;

1.4 关于哈希表的扩容机制

1.4.1、为什么扩容?

当哈希表被占的位置比较多的时候,出现哈希冲突的概率也就变高了,所以很有必要进行扩容;
简单理解成为在开放寻址法中,里面的位置都被占用光了,没有存放的位置可以使用了,需要进行扩容;

1.4.2、怎么扩容?

有一个增长因子的概念,也叫作负载因子,简单点说就是已经被占的位置与总位置的一个百分比,比如一共十个位置,现在已经占了七个位置,就触发了扩容机制,因为它的增长因子是0.7,也就是达到了总位置的百分之七十就需要扩容。

  • 在HashMap中,当它当前的容量占总容量的百分之七十五的时候就需要扩容了。

而且这个扩容也不是简单的把数组扩大,而是新创建一个数组是原来的2倍,然后把原数组的所有Entry都重新Hash一遍放到新的数组;

  • Hash也就是把之前的数据通过新的哈希函数计算出新的位置来存放。

2、JDK 中HashMap 的相关实现知识

/**
 * 哈希表 或者叫做 散列表数据结构
 * 1、HashMap 的底层数据结构?
 *      底层是哈希表 / 散列表的数据结构
 * 2、哈希表是什么样子的数据结构?
 *      哈希表是一个数组和链表的结合体;
 *      数组:在查询方面效率比较高,在随机增删方面的效率比较低下;
 *      链表:在随机增删方面的效率比较低下,在查询方面的效率很低;
 *      哈希表将上面的两种数据结构进行结合,充分发挥各自的优点;
 * 3、HashMap集合底层的源代码
 *      public class HashMap{
 *          // 实际上是一个数组(一纬的数组)
 *          Node<K,V>[] table;
 *
 *          // 静态的内部类
 *          static class Node<K,V> {
 *              final int hash;     // 哈希值,是 key 的hashCode() 方法执行结果,哈希值通过了哈希函数的算法,转换成为了数组的下标
 *              final K key;        // 存储带 Map 集合中的 key
 *              V value;            // 存储到 Map 集合中的 value
 *              Node<K,V> next;     // 下一个节点的内存地址
 *          }
 *      }
 *
 *      哈希表 / 散列表:是一个一维数组,数组中的每一个元素是一个单向链表,数组和链表的结合体
 *
 * 4、需要掌握的是:
 *      map.put(k,v);
 *      v = map.get(k);
 *      上面的两个方法是需要掌握的;
 *
 *          map.put(k,v) 实现原理;
 *              1、将 k,v 封装到 Node 对象之中
 *              2、底层调用 key 的hashCode() 方法得到 hash 值,通过哈希函数/哈希算法将 hash 值转换为数组的下标,
 *                 下标的位置上面如果没有任何的元素,就将 Node 添加到这个位置上面,如果说下面对应的位置上面,有链表节点,
 *                 此时会拿着 key 和链表的每一个节点进行 equals ,如果所有的 equals 返回的都是 false ,这个节点会添加到链表的结尾
 *                 如果其中有一个 equals 返回了true ,那么将相等节点上面的 value 替换掉即可;
 *
 *          map.get(k) 实现原理:
 *              1、先调用 k 的hashCode 方法得到哈希值,通过哈希算法转换成为数组下标;
 *              2、通过数组下标快速定位到某个位置上面,如果这个位置上面什么也没有,返回 null ,如果在这个位置上面有一个单向链表(因为
 *                 产生了哈希冲突),会拿着参数 key 和单向链表上面的节点中的 key 进行 equals ,所有的 equals 方法返回 false,
 *                 get 返回 null ,只要有其中的一个节点返回的是 true ,说明这个时候已经找到了自己想要找到的元素,那么返回这个节点
 *                 的 value 即可;
 *
 * 5、为什么哈希表的随机增删以及查询的效率都是比较高的?
 *      因为随机增删是在链表上面操作的(选择了这个处理哈希冲突的方式,有了冲突直接形成一个链表);
 *      因为索引是在数组上面进行的,使用了哈希函数,将 key 转换成为了数组中的下标,查询时候,只需要扫描其中的一个即可;
 *      没有纯数组和纯链表的性能好,这是一个结合体,是一个中间的选择;
 *
 * 6、总结上面的内容:HashMap 集合中的 key ,会先后使用了两个方法,一个是 hashCode(),一个是equals 方法,需要对其进行重写;先是用了hashCode 方法进行了数组位置的寻找,使用 equals() 方法将节点装进去,装的时候,需要比较 value 中的值;
 *
 * 7、为什额需要重写 equals 方法?
 *      因为 equals 方法默认比较的是两个对象的地址,现在需要比较内容才能进行下一步的操作;
 *
 * 8、HashMap 集合中 key 的特点:
 *      无序,不可重复
 *          为什么无序?
 *              因为在加入的时候,不知道会加入到什么地方,随机的链表的添加
 *          为什么不可重复?
 *              因为加入的时候,重复的东西被覆盖掉了;
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值