第五章:集合异界
知识具象化场景
陆小柒踏入由集合框架构成的异世界,天空漂浮着不同数据结构的岛屿:
- HashMap森林:键值对组成的发光蘑菇,哈希碰撞化作缠绕树干的藤蔓,当负载因子超过阈值时整棵树会分裂成红黑树
- LinkedList峡谷:由节点锁链构成的悬崖,每个悬崖平台都有
prev
和next
指针构成的吊桥 - ConcurrentHashMap堡垒:由分段锁铸就的钢铁城墙,CAS操作化作闪电在城墙间跳跃
- PriorityQueue火山:堆结构岩浆不断喷发,Comparator比较器控制着喷发优先级
- TreeMap古树:红黑树枝干维持着严格的平衡,旋转操作时枝干会发出机械齿轮的转动声
实战代码谜题
任务: 修复被破坏的哈希森林平衡
// 导致哈希冲突风暴的诅咒代码
public class HashMapCurse {
public static void main(String[] args) {
Map<Key, String> map = new HashMap<>();
for (int i = 0; i < 1000; i++) {
map.put(new Key(i), "value"+i);
}
}
static class Key {
int id;
Key(int id) {
this.id = id; }
@Override public int hashCode() {
return 1; } // 故意制造哈希冲突
}
}
正确解法:
- 实现合理的hashCode方法
- 重写equals方法保证一致性
- 采用Guava的
Hashing
优化分布
@Override
public int hashCode() {
return Hashing.murmur3_32().hashInt(id).hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return id == key.id;
}
特效: 修正后哈希森林的藤蔓自动重组为红黑树,碰撞区域发出金光
原理剖析(角色对话)
集合精灵(手持由Entry节点构成的法杖):
“看这HashMap的扰动函数!(法杖挥动出现哈希值光影)JDK8通过高位异或打破哈希碰撞攻击,当链表长度超过8(展示发光数字8符文),树化结界就会启动!”
陆小柒(触摸ConcurrentHashMap的城墙):
“这些分段锁的闪电有什么特别之处?”
精灵(城墙突然透明化露出CAS操作火花):
“在JDK8之后,这里改用synchronized+CAS+volatile的三重结界(浮现Node
的val修饰符),锁粒度从段级细化到桶级!”
红黑树守卫(从古树中现身):
“记住红黑树的五大法则:①根节点黑 ②叶节点黑 ③红节点子必黑 ④黑高相同 ⑤新插节点必红!”(每说一条,古树的对应部位就亮起红光)
陷阱关卡
危机: 并发修改引发的迭代器风暴
List<String> list = new ArrayList<>(Arrays.asList("剑","盾","药"));
for (String item : list) {
if ("药".equals(item)) {
list.remove(item); // 触发ConcurrentModificationException雪崩
}
}
破局步骤:
- 发现foreach语法糖背后的Iterator检测机制
- 改用Iterator的remove方法
- 或使用CopyOnWriteArrayList防护结界
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if ("药".equals(item)) {
it.remove(); // 安全移除
}
}
特效: 修正后迭代器化作金色飞龙,安全地修改集合结构
性能优化挑战
任务: 平息LinkedList的随机访问海啸
原始代码:
// 遍历十万元素的链表(时间复杂度O(n²))
List<Integer> list = new LinkedList<>();
for (int i = 0; i < 100000; i++) {
list.add(i); }
long sum = 0;
for (int i = 0; i < list.size(); i++) {
// 使用get(i)随机访问
sum += list.get(i);
}
优化方案:
- 改用Iterator顺序访问
- 转换为ArrayList临时副本
- 使用
ListIterator
双向遍历
// 优化后时间复杂度O(n)
long sum = 0;
for (Integer num : list) {
sum += num;
}
// 或使用流式处理
sum = list.stream().mapToInt(Integer::intValue).sum();
特效: 优化后遍历过程化作银色流光,性能监控显示耗时从15秒降至3毫秒
本章技术总结
核心概念 | 现实映射 | 奇幻隐喻 |
---|---|---|
哈希冲突 | 不同key相同hashcode | 藤蔓缠绕 |
负载因子 | 扩容阈值 | 树化结界触发点 |
CAS操作 | 无锁并发 | 城墙闪电 |
快速失败机制 | 并发修改检测 | 结界警报 |
红黑树 | 平衡二叉搜索树 | 机械古树 |
章末彩蛋: 当陆小柒离开集合异界时,发现一块刻着Record
的神秘石碑正在形成——暗示Java 16的记录类(Record)即将带来新的数据结构革命…