关于HashMap put元素的原理

本文深入解析HashMap集合如何通过计算key的hashCode,运用哈希算法确定数组下标,以及如何处理数组元素冲突,采用链表进行元素存储和更新,揭示HashMap在查询和插入操作上高效性的秘密。

HashMap集合put元素的原理:
(1)计算key的hashCode
(2)将key的hashCode作为计算因子,通过哈希算法计算HashMap的数组下标index
(3)如果index下标的数组元素为空,直接put(新增元素)
(4)如果index下标的数组元素不为空,调用key的equal方法,判断index位置的链表是否存在
(5)如果找到链表中某个元素与key的equals方法相等,则使用value更新
(6)如果未找到链表中某个元素与key的equals方法相等,则新增
一般面试的时候,经常会问到HashMap的原理。起初一直不理解,搞清楚HashMap的原理很重要吗?我就知道HashMap是线程不安全的。后来仔细看了下HashMap的底层实现原理,
原来HashMap既具有数组的快速查询,又兼具链表的快速插入特性。虽然不是线程安全的,但通过线程安全处理之后,无论是查询还是添加元素都很高效,用途广泛,因此掌握HashMap的底层原理的重要性不言而喻了。

转载于:https://www.cnblogs.com/beheaven/p/10729358.html

<think>我们正在讨论HashMapput方法实现原理。根据用户要求,我们需要详细讲解put元素的具体流程,包括哈希计算、冲突处理、链表转红黑树等关键步骤。同时,参考引用[1]中提到的处理哈希冲突的部分,但要以用户当前问题(put流程)为核心展开。 注意:用户的问题是关于HashMapput方法,因此我们需要完整描述从开始放入元素到最终存储的整个流程,包括初始化、哈希计算、索引定位、处理冲突、扩容等。 另外,根据系统指令,我们需要: 1. 使用$...$表示行内公式,例如计算索引位置时:$index = (n - 1) \& hash$ 2. 独立公式使用$$...$$,例如描述扩容阈值:$$threshold = capacity * loadFactor$$ 3. 使用中文回答 4. 生成相关问题 5. 在回答中引用的地方添加引用标识(如引用[1]中关于冲突处理的部分) 下面开始组织回答:</think>### HashMap put方法实现原理元素存储流程 HashMapput操作是核心方法,其实现流程如下(以Java 8源码为基础): --- #### 一、核心数据结构 ```java transient Node<K,V>[] table; // 哈希桶数组 static class Node<K,V> { final int hash; // 哈希值 final K key; V value; Node<K,V> next; // 链表指针 } ``` --- #### 二、put操作全流程 **1. 哈希值计算** ```java public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } ``` - 通过`hash(key)`计算扰动哈希值: `(h = key.hashCode()) ^ (h >>> 16)` 高位参运算,减少哈希冲突[^1] **2. 初始化哈希表** ```java final V putVal(int hash, K key, V value, ...){ Node<K,V>[] tab; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 首次扩容至16 } ``` - 初始容量16,负载因子0.75(默认) **3. 计算桶位置** ```java int index = (n - 1) & hash; // n为桶数组长度 ``` 通过位运算 $index = (n-1) \& hash$ 实现高效取模[^1] **4. 处理哈希冲突** ```java Node<K,V> p = tab[index]; if (p == null) { tab[index] = newNode(hash, key, value, null); // 直接插入 } else { // 处理已存在节点... } ``` --- #### 三、哈希冲突处理机制 当桶位置已有节点时: 1. **键值相同** ```java if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 标记为待更新节点 ``` 2. **红黑树节点** ```java else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); ``` 3. **链表遍历** ```java else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // 尾插法 if (binCount >= TREEIFY_THRESHOLD - 1) // 链表长度≥8 treeifyBin(tab, hash); // 转红黑树 break; } // 检查链表节点key是否相同 ... } } ``` --- #### 四、扩容机制(resize) 当元素数量超过阈值 $threshold = capacity \times loadFactor$ 时触发: ```java final Node<K,V>[] resize() { int newCap = oldCap << 1; // 容量翻倍 threshold = (int)(newCap * loadFactor); // 新阈值 // 重新计算节点位置:index = (newCap-1) & hash } ``` - **节点迁移策略**: 利用 $hash \& oldCap$ 判断高位比特: - 结果为0 → 保持原索引 - 结果为1 → 新位置 = 原索引 + oldCap $$\text{新索引} = \begin{cases} i & \text{if } (hash \& oldCap) = 0 \\ i + oldCap & \text{if } (hash \& oldCap) \neq 0 \end{cases}$$ --- #### 五、关键特性对比 | 操作 | Java 7 | Java 8 | |---------------|-------------------------|-------------------------| | **冲突处理** | 头插法(易死锁) | 尾插法 | | **数据结构** | 数组+链表 | 数组+链表/红黑树 | | **树化阈值** | - | 链表长度≥8 | | **退化阈值** | - | 树节点≤6时退化为链表 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值