HashMap 的put方法流程?

1 个回答

  • 小菜鸟
    二八佳人体似酥,腰间仗剑斩凡夫。
    `HashMap` 的 `put()` 方法在 JDK 8 中的执行流程可以总结为以下几个步骤: ### 1. **计算 hash 值** - 根据传入的 `key` 计算它的 `hashCode()`,然后对哈希值进行进一步优化(即 `hash()` 方法),将高 16 位和低 16 位异或,得到最终的 `hash` 值。 - 通过 `(n - 1) & hash` 计算出该元素在数组中的索引 `i`,`n` 是当前 `HashMap` 数组的长度。 ### 2. **检查数组是否为空** - 如果数组还没有被初始化(`table == null`),调用 `resize()` 方法进行初始化,数组的初始长度为 16。 ### 3. **检查是否有哈希冲突** - 如果数组对应位置(索引 `i`)为空,即 `table[i] == null`,则直接在该位置插入新的键值对。 - 如果数组对应位置不为空,说明发生了哈希冲突。 ### 4. **处理哈希冲突** - 如果发生了哈希冲突,`HashMap` 会首先检查当前桶中的第一个节点是否与插入的 `key` 相等: - 如果相等,直接覆盖原有的 `value`。 - 如果不相等,则继续进行冲突处理。 ### 5. **红黑树或链表处理** - 如果冲突位置上的节点是红黑树结构,调用红黑树的插入方法,将新的键值对插入到红黑树中。 - 如果冲突位置上的节点是链表: - 遍历链表,如果找到了相同的 `key`,直接覆盖原有的 `value`。 - 如果没有相同的 `key`,则在链表末尾插入新的节点。 ### 6. **链表转换为红黑树** - 在插入新节点后,`HashMap` 会检查链表的长度: - 如果链表长度超过 8 且数组容量大于等于 64,则将链表转换为红黑树,以提高查找性能。 - 如果链表长度超过 8 但数组容量小于 64,则先执行扩容操作,而不是转换为红黑树。 ### 7. **扩容** - 当 `HashMap` 中的元素个数超过临界值(即 `size >= threshold`,通常是 `capacity * loadFactor`),则会调用 `resize()` 方法扩容,新的数组长度是原来的两倍,并重新计算每个元素在新数组中的位置。 ### 简要代码示例 以下是 `HashMap` 中 `put()` 方法的简要实现: ```java public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; // Step 1: 如果数组为空,进行初始化 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // Step 2: 计算索引并插入元素 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; // Step 3: 处理哈希冲突 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); // Step 4: 判断链表是否转换为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // Step 5: 覆盖值 V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } ``` ### 总结: `HashMap` 的 `put()` 方法的核心流程可以总结为:计算哈希值、找到数组的索引位置、处理冲突、根据链表长度决定是否转换为红黑树、必要时扩容。
    发布于 2024-09-03 12:46 •IP属地 辽宁
    举报
    评论
    收藏