概述
HashMap 提供了高效的键值对存储和检索方式,适用于需要快速访问和管理映射关系的多种场景,如数据库操作、缓存、索引等。它是现代编程中不可或缺的数据结构之一。
hashmap 数据结构组成:
1、 hash表:
哈希表(Hash Table),也称为散列表,是一种数据结构,用于存储键值对(key-value pairs)
哈希表的核心思想是利用哈希函数将键转换成唯一的索引,然后将该索引与值存储在数组中,以获取高效的查询。
2 、单链表:
每个节点除了包含存储的数据,还包含指向后一个节点的引用,hash冲突时挂载元素。
3、 红黑树:
红黑树(Red-Black Tree)是一种自平衡的二叉搜索树(Binary Search Tree),其设计思想是为了在有序集合(或映射)的基础上,提供高效的数据存储和检索,并且在插入和删除操作时保持树的平衡, 在某keY,hash冲突的元素大于等于8时由链表转成。
3.1 如何理解红黑的概念:
红黑树的 "红黑" 是指每个节点都被赋予了一个红色或黑色的标记,这些标记遵循一定的规则和性质,用以维护树的平衡和性能。红黑树的设计目标是保持树的高度平衡,以确保各种操作(插入、删除、查找等)在最坏情况下也能够在对数时间内完成。
规则: 根节点是黑色的,每个叶子节点(NIL 节点)都是黑色的,每个红色节点的两个子节点都是黑色的,从任一节点到其每个叶子的路径都包含相同数量的黑色节点,没有连续的红色节点,等等。
hashmap 核心成员变量:
- table(Node数组): 用于存储键值对的数组,是
HashMap存储数据的主要容器。每个数组元素对应一个桶,可以存储一个或多个键值对。 - threshold(阈值): 表示数组的容量阈值,当存储的键值对数量超过阈值时,会触发扩容操作。通常为容量与负载因子的乘积。
- loadFactor(负载因子): 表示哈希表的负载因子,用于确定何时进行扩容。负载因子是存储容量与实际键值对数量之间的比率,例如,0.75 表示当存储的键值对数量达到容量的 75% 时,进行扩容。
- size(大小): 表示当前存储的键值对数量。
- modCount(修改次数): 表示
HashMap的结构修改次数,用于支持迭代器的快速失败机制。 - threshold(阈值): 当数组容量超过此值时,数组将被重新分配。
- loadFactor(负载因子): 当存储的键值对数量达到数组容量的负载因子时,进行扩容。
hashmap 的hash算法:
具体来说,JDK 8 中的 HashMap 采用了以下的散列算法:
- 获取原始哈希值: 首先,会调用键的
hashCode()方法获取键的原始哈希值。 - 扰动函数(Spread Function): JDK 8 的
HashMap会使用一个扰动函数(spread function)来对原始哈希值进行处理,以便更好地分散哈希值的分布。这个扰动函数的实现会将原始哈希值进行一些位操作,使得高位的影响能够影响到低位,从而更好地减少哈希冲突。
hashmap的rehash优化:
JDK 1.8 中引入了“树化分裂”机制,它在 rehash 过程中不会一次性地将链表全部转换为红黑树,而是分批次进行。具体来说,当链表长度超过阈值(默认为8)时,会将链表的前 8 个节点直接保留在链表中,而剩余的节点会被放置到新桶中。这样做的目的是为了保持链表的局部性,减少树化过程中的性能开销。
这个“树化分裂”的机制在 resize 方法中得到应用,当进行 rehash 时,会对链表进行分裂,一部分节点继续保留在链表中,而另一部分节点则被放置到新桶中的树中。