一、HashMap是什么?先打个比方
如果你去过图书馆,一定见过这样的场景: 每本书都有一个唯一的“编号”,管理员会根据编号把书放到对应的书架格子里。 HashMap就像一个“超级图书馆”:
你存东西时(比如存一个“key=张三,value=18岁”),它会先给“张三”生成一个“编号”(专业叫哈希值),然后根据这个编号快速找到对应的“格子”存放。你取东西时,只要告诉它“张三”,它就能通过同样的“编号”规则,瞬间找到对应的“18岁”。
简单说:HashMap是一种用“key快速找value”的容器,速度极快,平均耗时接近“光速”(O(1))。
二、HashMap的核心原理:3步搞定存和取
我们用一个“示意图”(文字版)来拆解它的工作流程:
1. 初始化:先建一个“空书架”(数组)
HashMap刚创建时,会默认创建一个“空数组”(学名:桶数组,bucket array),默认长度是16(可以自己设置)。 这个数组就像一排书架格子,每个格子编号是0、1、2……15(共16个格子)。
2. 存数据:给key算“编号”,放进对应的格子里
当你执行 map.put("张三", 18) 时,HashMap会做3件事:
第一步:给“张三”算一个“哈希值” 通过Java自带的 hashCode() 方法(比如“张三”的哈希值可能是123456),再经过一次“扰动算法”(让哈希值更分散),得到一个最终的哈希值。第二步:算这个key该放在哪个格子里 用哈希值对数组长度取模,比如数组长度是16,123456 % 16 = 8,所以放在第8号格子里。第三步:处理“格子里已经有东西”的情况(重点!) 如果第8号格子已经有其他key(比如“李四”也算到了8号格子),就会出现“哈希冲突”(两个不同的key算出了相同的格子编号)。 这时候,HashMap会用两种方式处理冲突:
如果冲突的元素少(比如<=7个):用“链表”把它们串起来,新元素放在链表末尾(JDK1.8之前放在头部,1.8之后优化为尾部,减少性能损耗)。如果冲突的元素多(比如>=8个):链表会变成“红黑树”(一种平衡二叉树),让查找速度从链表的O(n)提升到树的O(log n)。
3. 取数据:用同样的规则,秒级定位value
当你执行 map.get("张三") 时:
先给“张三”算哈希值,再算格子编号(肯定还是8号格子)。去8号格子里找:如果是链表,就逐个对比key是否相等;如果是红黑树,就通过树的结构快速查找。找到对应的节点后,直接返回value(18岁)。
三、HashMap的“秘密武器”:扩容机制(为什么它不会满?)
假设书架(数组)长度是16,当格子里的元素越来越多,超过一个阈值(默认是16×0.75=12,这个值叫负载因子),HashMap会做两件事:
创建一个更大的数组(比如长度变成32)。把旧数组里的所有元素重新算一遍格子编号,放到新数组里(这个过程叫“重新哈希”)。
为什么要扩容? 如果不扩容,格子里的链表或红黑树会越来越长,查找速度会变慢。扩容后,数组长度变大,哈希值取模后的结果更分散,冲突减少,速度就保持高效了。
四、小白必懂的3个注意点
key可以是null,但只能有一个 HashMap允许key为null(其他Map比如TreeMap不允许),而且null的哈希值固定算到0号格子里。但只能存一个null key,否则会覆盖。
线程不安全,但速度快 如果多个线程同时改HashMap,可能会出问题(比如链表成环)。如果需要线程安全,建议用 ConcurrentHashMap(Java 8之后优化得很强)。
遍历顺序不稳定 存进去的顺序和取出来的顺序可能不一样,因为它是按哈希值分配格子的,不是按插入顺序存的。如果需要顺序,用 LinkedHashMap。
五、总结:HashMap为什么快?核心就3点
哈希值定位:通过哈希算法把key快速映射到数组的某个格子,省去了逐个查找的时间。冲突处理:用链表+红黑树平衡了“存储密度”和“查找速度”,冲突少用链表,冲突多用红黑树。自动扩容:通过负载因子动态调整数组大小,避免性能下降。
如果你还是有点懵,没关系!找个小例子动手试试:
import java.util.HashMap;
public class TestHashMap {
public static void main(String[] args) {
HashMap
map.put("张三", 18);
map.put("李四", 20);
System.out.println(map.get("张三")); // 输出18
}
}
试着往map里多存一些key,观察它们的存储位置(可以打印哈希值和格子编号),慢慢就理解啦!
六、课后小思考
如果两个不同的key算出了相同的哈希值(比如“abc”和“abd”),怎么办?为什么红黑树比链表查找快?可以在评论区留下你的想法哦~
希望这篇文章能让你对HashMap豁然开朗!如果觉得有用,别忘了点赞收藏~ 😊