Java集合框架解析:从基础到底层源码
Java集合框架解析:从基础到底层源码
一、集合体系
1.1 两大核心接口深度解析
Collection 单列集合
-
List 系列
ArrayList
:动态数组实现,初始容量10,扩容策略为 原容量的1.5倍// JDK17 扩容源码片段 int newCapacity = oldCapacity + (oldCapacity >> 1);
LinkedList
:双向链表实现,首尾操作时间复杂度O(1)// 节点结构源码 private static class Node<E> {E item;Node<E> next;Node<E> prev; }
Vector
:线程安全版ArrayList(方法级synchronized锁)
-
Set 系列
HashSet
:基于HashMap实现(值存储在Key)TreeSet
:红黑树实现有序存储,时间复杂度O(logn)
Map 双列集合(深度扩展)
实现类 | 数据结构 | 线程安全方案 |
---|---|---|
HashMap | 数组+链表/红黑树(JDK8+) | ConcurrentHashMap分段锁 |
LinkedHashMap | 链表维护插入/访问顺序 | Collections.synchronizedMap |
TreeMap | 红黑树 | 无内置方案 |
HashMap扩容机制:
- 默认初始容量16,负载因子0.75
- 扩容阈值 = 容量 * 负载因子
- 链表长度≥8且数组长度≥64时转红黑树
二、Collection核心机制详解
2.1 迭代器遍历的陷阱与突破
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));// 错误示例:触发ConcurrentModificationException
Iterator<String> it = list.iterator();
while(it.hasNext()) {String s = it.next();if(s.equals("B")) {list.remove(s); // 错误!使用集合的remove方法}
}// 正确写法:使用迭代器的remove
Iterator<String> it = list.iterator();
while(it.hasNext()) {String s = it.next();if(s.equals("B")) {it.remove(); // ✅ 安全删除}
}
源码级原理:
modCount
机制:集合修改次数计数器- 迭代器创建时记录expectedModCount
- 每次操作前校验modCount == expectedModCount
2.2 遍历方式性能对比
遍历方式 | 时间复杂度 | 适用场景 |
---|---|---|
普通for循环 | O(n) | 需要索引操作的场景 |
迭代器 | O(n) | 遍历中需要删除元素 |
增强for | O(n) | 简单遍历(语法糖) |
forEach+Lambda | O(n) | 函数式编程风格 |
三、List集合深度扩展
3.1 ListIterator 的威力
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");ListIterator<String> lit = list.listIterator();
while(lit.hasNext()) {String s = lit.next();if(s.equals("B")) {lit.add("C"); // ✅ 安全添加元素lit.set("D"); // ✅ 修改当前元素}
}
// 结果:[A, D, C]
3.2 时间复杂度对比表
操作 | ArrayList | LinkedList |
---|---|---|
get(int index) | O(1) | O(n) |
add(E element) | O(1) 均摊 | O(1) |
add(int index, E) | O(n) | O(n) |
remove(int index) | O(n) | O(n) |
四、并发集合与Java8新特性
4.1 线程安全方案对比
实现方式 | 锁粒度 | 性能 | 适用场景 |
---|---|---|---|
Vector | 方法级锁 | 差 | 遗留系统兼容 |
Collections.synchronizedList | 对象锁 | 中等 | 低并发场景 |
CopyOnWriteArrayList | 写时复制 | 读快写慢 | 读多写少场景 |
4.2 Stream API实战
List<Employee> employees = ...;// 统计研发部平均工资
double avgSalary = employees.stream().filter(e -> "研发部".equals(e.getDept())).mapToDouble(Employee::getSalary).average().orElse(0);// 分组统计部门人数
Map<String, Long> deptCount = employees.stream().collect(Collectors.groupingBy(Employee::getDept, Collectors.counting()));
五、高频面试题深度剖析
5.1 HashMap为什么线程不安全?
- 数据覆盖问题:多线程put时可能覆盖已有键值对
- 扩容死循环:JDK7头插法导致链表成环(JDK8改用尾插法)
- 可见性问题:未使用volatile修饰size等字段
5.2 ArrayList与LinkedList的选择依据
- 查询为主:选ArrayList(CPU缓存友好)
- 频繁增删:首尾操作选LinkedList,中间操作两者都差
- 内存敏感:ArrayList更节约空间(无节点指针开销)
六、终极总结:集合框架选用指南
- 单线程环境
- 快速查询:ArrayList/HashMap
- 频繁增删:LinkedList
- 需要排序:TreeSet/TreeMap
- 高并发场景
- 读多写少:CopyOnWriteArrayList
- 写多读少:ConcurrentHashMap
- 严格一致性:Collections.synchronized系列
- 函数式编程
- 使用Stream API进行链式处理
- 优先选择不可变集合(Guava/Java9+)
彩蛋知识点:
Java9新增工厂方法创建不可变集合:List<String> list = List.of("A", "B", "C"); Set<Integer> set = Set.of(1, 2, 3); Map<String, Integer> map = Map.of("A", 1, "B", 2);
通过全面理解集合框架的底层实现与设计哲学,我们可以编写出更高效、更健壮的Java应用程序。建议结合IDE的源码调试功能(如IDEA的View->Tool Windows->Structure),深入体会各集合类的实现细节。