HashMap详解(一). put方法详解

本文详细解析了Java8中HashMap的put方法实现原理。包括如何通过hash计算数组下标、处理hash碰撞、链表与红黑树转换的条件及过程、数据扩容策略等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA8

1.HashMap.put 方法实现了Map.put(K key,V value)的方法。直接调用内部方法

205339_xwUM_2341924.png

大致思路是这样的:

  1. 对key的hashCode()进行hash后计算数组下标index;
  2. 如果当前数组table为null,进行resize()初始化;
  3. 如果没碰撞直接放到对应下标的bucket里;
  4. 如果碰撞了,且节点已经存在,就替换掉 value;
  5. 如果碰撞后发现为树结构,挂载到树上。
  6. 如果碰撞后为链表,添加到链表尾,并判断链表如果过长(大于等于TREEIFY_THRESHOLD,默认8),就把链表转换成树结构;
  7. 数据 put 后,如果数据量超过threshold,就要resize。

下面是putVal方法解析

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0) //首次放入元素时,分配table空间-默认size=16
            n = (tab = resize()).length;

        if ((p = tab[i = (n - 1) & hash]) == null) // 算出新node在table中的位置,若对应位置为null,新建一个node并放入对应位置.
        // 注意:  (n - 1) & hash 求余操作 等价于 hash%n(只有n为2的幂时才成立,见下图)
            tab[i] = newNode(hash, key, value, null);
        else {                                      //在table对应位置有node时
            Node<K,V> e; K k;
            if (p.hash == hash &&                   // key一样 (hash值相同,且key 一样,相同实例或者满足Object.equals方法)
                ((k = p.key) == key || (key != null && key.equals(k)))) // 不满足此条件则发生hash碰撞
                e = p;
            else if (p instanceof TreeNode)                // hash碰撞的情况下,用链表解决,链表大于8时,改为红黑树. 当node为treeNode时,putTreeVal->红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {                                                                               //hash
                for (int binCount = 0; ; ++binCount) {                          //用for循环将链表指针后移,将新node在链表加在尾部
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 当链表长度大于8时,转成红黑树
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&                                               
                        ((k = e.key) == key || (key != null && key.equals(k))))         
                        break;                                                          
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)  //当node中旧的值null或者onlyIfAbsent==false时,将新的value替换原来的value.
                    e.value = value;
                afterNodeAccess(e); //新node塞入table后做的做的事情,在HashMap中是一个空方法(LinkedHashMap中有有使用, move node to last)
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)  //新的size大于阈值(默认0.75*table.length)时,扩容.
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 

235450_YihJ_2341924.png

总结.

1.第一次put操作时,才为table分配内存空间,

2.当发生hash碰撞时,先链表,链表长度大于8时,转为红黑树.

 

转载于:https://my.oschina.net/u/2341924/blog/1560149

<think>我们正在讨论JavaHashMapputAll()方法。根据引用[1]和引用[2],putAll()方法用于将个Map中的所有映射复制到另个Map中。引用[1]中给出了个示例:将HashMap中的所有键值对复制到另HashMap中。引用[2]提到,如果目标Map中已经存在某个键,则其对应的值会被新Map中的值替换。引用[3]则解释了put方法中哈希计算的细节,但这个问题主要关注putAll。因此,我们可以这样组织回答:1.方法作用:将指定Map中的所有映射复制到当前Map中。2.方法声明:voidputAll(Map<?extendsK,?extendsV>m)3.注意事项:如果当前Map中已经包含相同的键,则这些键对应的值将被指定Map中的值覆盖。4.示例代码:展示如何使用putAll方法。同时,我们需要按照用户要求,在回答中自然地添加引用标识。另外,根据系统级指令,我们需要生成相关问题。注意:用户提供的引用[1]中有个示例代码,我们可以参考它,但需要确保代码正确并符合Java语法。我们将编写个示例,并解释代码步骤。最后,生成相关问题。</think>### Java HashMap putAll() 方法详解 `putAll()` 是 Java `HashMap` 类中的个核心方法,用于将个 Map 中的所有键值对复制到当前 HashMap 中。其方法签名为: ```java void putAll(Map<? extends K, ? extends V> m) ``` #### 📌 核心特性 1. **批量复制** 将参数 Map `m` 中的所有映射关系复制到目标 HashMap 中[^1]。 2. **键值覆盖机制** - 若目标 HashMap 已存在相同的键,则用新值覆盖旧值[^2] - 若键不存在,则直接插入新键值对 - 示例:目标 Map 有 `{1: "A"}`, 源 Map 有 `{1: "B"}`, 执行后变为 `{1: "B"}` 3. **空值处理** 允许复制包含 `null` 键或 `null` 值的映射(符合 HashMap 规范)。 #### 📝 使用示例 ```java import java.util.HashMap; import java.util.Map; public class PutAllExample { public static void main(String[] args) { // 创建源Map Map<Integer, String> sourceMap = new HashMap<>(); sourceMap.put(10, "Java"); sourceMap.put(20, "Python"); sourceMap.put(30, "C++"); // 创建目标Map Map<Integer, String> targetMap = new HashMap<>(); targetMap.put(40, "PHP"); targetMap.put(20, "Rust"); // 重复键 System.out.println("复制前targetMap: " + targetMap); // {20=Rust, 40=PHP} // 执行复制操作 targetMap.putAll(sourceMap); System.out.println("复制后targetMap: " + targetMap); // 输出: {20=Python, 40=PHP, 10=Java, 30=C++} // 注意:键20的值被覆盖 } } ``` #### ⚠️ 注意事项 1. **哈希冲突处理** 底层通过 `hash(key) & (capacity-1)` 计算索引[^3],putAll() 会触发自动扩容(当元素数 > 容量×负载因子时)。 2. **并发问题** 非线程安全!多线程环境下需使用 `ConcurrentHashMap` 或手动同步。 3. **性能影响** 时间复杂度为 $O(n)$(n 为源 Map 大小),大容量 Map 复制可能影响性能。 4. **浅拷贝性质** 仅复制键值对的引用,不克隆对象本身(修改复制后的对象会影响源 Map 中的对象)。 #### 💡 典型应用场景 - 合并多个 Map 的数据源 - 配置参数的批量更新 - 实现 Map 的快速复制(替代手动遍历 put) --- ### 相关问题 1. **`putAll()` 和逐个调用 `put()` 方法有何性能差异?** 2. **当源 Map 与目标 Map 的泛型类型不兼容时会发生什么?** 3. **如何实现深拷贝的 Map 复制而非浅拷贝?** 4. **`putAll()` 执行过程中触发扩容的机制是怎样的?** 5. **在多线程环境下使用 `putAll()` 会导致哪些并发问题?如何解决?** [^1]: 引用自 HashMap putAll 示例 [^2]: 引用自 Java HashMap put() 方法解析 [^3]: 引用自 HashMap 哈希冲突处理机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值