HashMap的put方法,1.7和1.8有何不同?

本文详细解析HashMap的内部机制,包括数组初始化、索引计算、节点管理(链表/红黑树转换)、插入策略(1.7与1.8差异)、以及1.8中优化的扩容策略。
  1. HashMap是懒惰创建数组的,首次使用才创建数组;
  2. 计算索引(桶下标);
  3. 如果桶下标还人占用,创建Node节点占位返回;
  4. 如果桶下标已经被人占用
    a. 已经是TreeNode节点,走红黑树的添加或更新逻辑;
    b. 是普通的Node节点,走链表的添加或更新逻辑,如果链表长度超过树化阈值,走树化逻辑;
  5. 返回前检查容量是否超过阈值,一旦超过进行扩容(元素放进去再扩容)
  6. 不同:
    a. 链表插入节点时,1.7是头插法,1.8是尾插法
    b. 1.7是大于等于阈值且没有空位时才扩容,而1.8是大于阈值数组容量为16时,数组个数大于12,即13个元素时就要扩容)就扩容;
    c. 1.8在扩容计算Node索引时,会优化(即hash值 & 原先容量 == 0的元素留在原来位置,否则新位置 = 旧位置+原先容量)。
### HashMap 在 JDK1.7 JDK1.8 之间的实现差异 #### 数据结构 在 JDK1.7 中,`HashMap` 的底层数据结构是由数组加链表组成的。当发生哈希冲突时,所有的键值对会被存放在同一个桶中形成一条单向链表[^1]。 而在 JDK1.8 中,除了保留原有的数组加链表的数据结构外,还引入了红黑树的概念。如果某个桶中的链表长度超过了一个阈值(默认为 8),并且当前 `HashMap` 的大小超过了一定界限,则该链表会转换成红黑树,从而减少查找时间复杂度从 O(n) 到平均情况下的 O(log n)[^2]。 #### 初始化过程 对于初始化部分,在 JDK1.7 中,`table` 数组是在声明时被初始化为空数组,并且首次插入元素之前调用 `inflateTable()` 方法完成实际分配工作[^3]。然而到了 JDK1.8 版本里,“懒加载”的概念得到了应用——即只有当真正需要存储第一个键值对时才会触发真正的初始化操作;另外值得注意的是,默认情况下初始容量调整为了最接近指定容量的一个 2 的幂次方数值[^4]。 #### 扩容机制 关于扩容方面也有明显改变: - **JDK1.7**: 使用头插法来进行迁移旧有节点到新位置的过程当中可能出现循环链表的情况,进而导致死循环问题的发生概率增加。而且它的判断依据较为严格,需满足两个前提条件才能执行扩容动作:一是达到负载因子限制;二是目标索引处已有其他映射项存在。 - **JDK1.8**: 改进了这一点采用了尾插法避免上述风险的同时简化逻辑只依赖单一条件即可触发改动 —— 即每当新增后的总数量超出临界点便会立即启动扩展流程。此外它利用位运算技巧快速定位每一个条目应该迁移到的新地址[(e.hash & oldCap)==0][^3]。 #### Hash 计算方式 两代产品间另一个重要差别体现在如计算散列码之上: - **JDK1.7** 定义了一种相对复杂的扰动函数用来进一步打乱原始hashCode分布特性以期获得更加均匀的结果集[^4]: ```java static final int hash(int h){ h ^= (h >>> 20) ^ (h >>>12); return h^(h >>> 7) ^ (h >>> 4); } ``` - **JDK1.8** 对此进行了优化精简版本如下所示不仅减少了不必要的移位次数同时也兼顾效率与效果平衡考虑特别针对null关键字做了特殊处理直接返回零值作为对应hashcode: ```java static final int hash(Object key){ int h; return (key == null) ? 0 : (h = key.hashCode())^(h >>> 16); } ``` #### Null 键的支持 无论是哪个版本都允许最多只有一个Null Key 存在于整个Map实例之中不过具体表现形式略有出入: - 在早期设计(JDK1.7)里面如果遇到这样的特殊情况则专门开辟首个槽位留给它们单独使用而不参与常规路径匹配流程. - 后续更新至最新标准后(NKD1.8),尽管依旧保持相同行为模式但内部实现细节有所调整现在即使NULL也可以像普通对象那样经历完整的HASHING步骤只不过最终得出固定常量ZERO而已因此理论上讲二者并无本质区别仅限表达手法上的转变罢了[^3]. ```python # 示例代码展示两种不同版本下 NULL KEY 插入情形对比模拟演示片段 map_17.put(None,"value") # JDK1.7 方式可能直接放置 index=0 处理 map_18.put(None,"another value") # JDK1.8 经过 HASH 函数作用同样指向 index=0 结果一致 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值