【面试】HashMap ConcurrentHashMap 深度解析(面试版)

📅 发布时间:2026/7/5 6:54:08 👁️ 浏览次数:
【面试】HashMap  ConcurrentHashMap 深度解析(面试版)
文章目录一、HashMap非线程安全日常开发最常用1. 核心定义2. 底层结构JDK 1.8 是关键3. 核心原理put方法流程面试必问4. 核心特性面试高频二、ConcurrentHashMap线程安全并发场景专用1. 核心定义2. 底层实现JDK 1.7 vs 1.83. JDK 1.8 核心原理面试核心1线程安全保障2put方法核心逻辑简化版4. 核心特性面试高频三、HashMap vs ConcurrentHashMap 核心对比面试必背总结一、HashMap非线程安全日常开发最常用1. 核心定义HashMap是基于哈希表实现的Map接口实现类存储键值对Key-Value允许Key和Value为nullKey只能有1个nullValue可以多个无序插入顺序≠遍历顺序非线程安全多线程操作会出问题。2. 底层结构JDK 1.8 是关键JDK 1.7数组 链表拉链法解决哈希冲突JDK 1.8数组 链表 红黑树优化哈希冲突提升查询效率当链表长度 8 且数组长度 ≥ 64 时链表转红黑树当红黑树节点数 6 时转回链表。3. 核心原理put方法流程面试必问// 简化版put流程帮你理解核心逻辑publicVput(Kkey,Vvalue){// 1. 计算Key的哈希值扰动函数减少哈希冲突inthashhash(key);// 2. 计算数组下标hash (数组长度-1)等价于取模效率更高intindexhash(table.length-1);// 3. 检查下标位置是否有元素if(table[index]null){// 3.1 无元素直接新建节点放入数组table[index]newNode(hash,key,value,null);}else{// 3.2 有元素处理哈希冲突NodeK,Vptable[index];if(p.hashhash(p.keykey||key.equals(p.key))){// 3.2.1 Key相同覆盖Valuep.valuevalue;}else{// 3.2.2 Key不同遍历链表/红黑树for(intbinCount0;;binCount){if(p.nextnull){// 链表末尾新增节点p.nextnewNode(hash,key,value,null);// 检查是否转红黑树if(binCount7)treeifyBin(table,hash);break;}// 遍历中找到相同Key覆盖Valueif(p.next.hashhash(p.next.keykey||key.equals(p.next.key))){p.next.valuevalue;break;}pp.next;}}}// 4. 检查是否需要扩容数组元素个数 阈值if(sizethreshold)resize();returnnull;}4. 核心特性面试高频扩容机制默认初始容量16负载因子0.75阈值16*0.7512当元素数超过阈值时容量翻倍2倍重新计算所有元素的下标rehash线程安全问题多线程put可能导致链表成环JDK1.7、数据丢失JDK1.8禁止在多线程环境下直接使用为什么数组长度是2的幂哈希值 (长度-1) 等价于 哈希值 % 长度且位运算效率更高同时减少哈希冲突哈希分布更均匀。二、ConcurrentHashMap线程安全并发场景专用1. 核心定义ConcurrentHashMap是HashMap的线程安全版本专为高并发场景设计JDK 1.7和1.8的实现差异极大面试重点问1.8不允许Key或Value为null避免与并发场景下的null返回值混淆。2. 底层实现JDK 1.7 vs 1.8版本核心结构线程安全实现方式效率JDK 1.7分段数组Segment 链表分段锁ReentrantLock锁住单个Segment中等锁粒度大JDK 1.8数组 链表 红黑树CAS synchronized锁住单个节点更高锁粒度小3. JDK 1.8 核心原理面试核心1线程安全保障CAS对数组节点的新增/修改操作用CAS无锁算法保证原子性避免加锁synchronized仅当节点已存在时对单个链表/红黑树节点加synchronized锁而非整个数组锁粒度极小并发效率大幅提升volatile数组节点用volatile修饰保证多线程间的可见性。2put方法核心逻辑简化版publicVput(Kkey,Vvalue){returnputVal(key,value,false);}finalVputVal(Kkey,Vvalue,booleanonlyIfAbsent){// 禁止null键值与HashMap的核心区别之一if(keynull||valuenull)thrownewNullPointerException();inthashspread(key.hashCode());// 哈希值优化intbinCount0;for(NodeK,V[]tabtable;;){NodeK,Vf;intn,i,fh;if(tabnull||(ntab.length)0)tabinitTable();// 初始化数组CAS保证线程安全elseif((ftabAt(tab,i(n-1)hash))null){// 节点为空CAS插入无需加锁if(casTabAt(tab,i,null,newNodeK,V(hash,key,value,null)))break;}elseif((fhf.hash)MOVED)tabhelpTransfer(tab,f);// 扩容时的协助机制else{VoldValnull;// 节点存在对当前节点加synchronized锁锁粒度极小synchronized(f){// 再次检查节点是否被修改防止并发if(tabAt(tab,i)f){// 遍历链表/红黑树逻辑类似HashMap但保证线程安全if(fh0){binCount1;for(NodeK,Vef;;binCount){Kek;if(e.hashhash((eke.key)key||(ek!nullkey.equals(ek)))){oldVale.val;if(!onlyIfAbsent)e.valvalue;break;}NodeK,Vprede;if((ee.next)null){pred.nextnewNodeK,V(hash,key,value,null);break;}}}elseif(finstanceofTreeBin){// 红黑树处理逻辑NodeK,Vp;binCount2;if((p((TreeBinK,V)f).putTreeVal(hash,key,value))!null){oldValp.val;if(!onlyIfAbsent)p.valvalue;}}}}// 检查是否转红黑树if(binCount!0){if(binCountTREEIFY_THRESHOLD)treeifyBin(tab,i);if(oldVal!null)returnoldVal;break;}}}addCount(1L,binCount);// 统计元素数触发扩容returnnull;}4. 核心特性面试高频并发度高JDK1.8锁粒度从“分段”降到“单个节点”并发下性能接近HashMap扩容优化支持多线程协助扩容transfer方法避免单线程扩容耗时与Hashtable的区别Hashtable是对整个表加synchronized锁锁粒度极大效率低ConcurrentHashMap是精细化锁效率远高于Hashtable不支持null键值因为并发场景下null无法区分“键不存在”和“值为null”容易引发逻辑错误。三、HashMap vs ConcurrentHashMap 核心对比面试必背维度HashMapConcurrentHashMap线程安全非线程安全线程安全null键值允许Key/Value为null禁止Key/Value为null锁机制无锁JDK1.7分段锁JDK1.8CASsynchronized性能高无锁高精细化锁并发下优于Hashtable适用场景单线程/无并发的场景多线程高并发场景总结HashMap单线程场景首选底层是数组链表红黑树非线程安全允许null键值扩容为2倍容量哈希冲突用拉链法红黑树优化ConcurrentHashMap并发场景首选JDK1.8用CASsynchronized实现精细化锁禁止null键值性能远优于Hashtable面试高频问法HashMap的扩容机制、JDK1.8的优化点、ConcurrentHashMap的线程安全实现JDK1.7 vs 1.8、为什么ConcurrentHashMap不允许null键值。