【java-core-collections】集合框架深度解析

集合框架深度解析

一、List 集合

1.1 ArrayList

底层数据结构: Object\[\] 数组

java 复制代码
// 扩容机制
private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 * 1.5
    int newCapacity = ArraysSupport.newLength(oldCapacity,
        minCapacity - oldCapacity, oldCapacity >> 1);
    return Arrays.copyOf(elementData, newCapacity);
}

扩容流程:

复制代码
初始: 空数组 {} (懒初始化)
首次 add: 默认容量 10
第 11 次 add: 10 → 15 (×1.5)
第 16 次 add: 15 → 22 (×1.5)

时间复杂度:

操作 复杂度 原因
get(index) O(1) 数组随机访问
add(e) 尾部 O(1) 均摊 偶尔扩容 O(n)
add(index, e) O(n) 需要移动元素
remove(index) O(n) 需要移动元素

1.2 ArrayList vs LinkedList

特性 ArrayList LinkedList
底层结构 数组 双向链表
随机访问 O(1) O(n)
头部插入 O(n) O(1)
内存占用 紧凑 每节点额外2个指针
缓存友好性 好(连续内存) 差(分散内存)

结论: 99% 场景用 ArrayList,除非频繁头部插入。

1.3 ArrayList 线程安全问题

java 复制代码
// ConcurrentModificationException
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String s : list) {
    if ("b".equals(s)) list.remove(s); // 抛异常!
}

// 正确方式1: Iterator.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if ("b".equals(it.next())) it.remove();
}

// 正确方式2: CopyOnWriteArrayList
List<String> safeList = new CopyOnWriteArrayList<>(list);

二、Map 集合

2.1 HashMap 原理

数据结构:数组 + 链表 + 红黑树(JDK 8+)

复制代码
table[]
┌────┐
│ 0  │ → null
├────┤
│ 1  │ → Node(K1,V1) → Node(K2,V2) → Node(K3,V3)
├────┤
│ 2  │ → TreeNode (红黑树)
├────┤
│ 3  │ → null
├────┤
│... │
└────┘

put 流程:

复制代码
1. 计算 hash = (h = key.hashCode()) ^ (h >>> 16)  // 扰动函数
2. 计算索引 = (n - 1) & hash                        // 等价 hash % n
3. 桶为空 → 直接放入
4. 桶非空:
   - key 相同 → 覆盖 value
   - 是 TreeNode → 红黑树插入
   - 是链表 → 尾插法,长度 ≥ 8 时转红黑树
5. 检查容量 > threshold → 扩容

2.2 HashMap 扩容机制

java 复制代码
// 核心参数
static final int DEFAULT_INITIAL_CAPACITY = 16;    // 默认容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;    // 负载因子
static final int TREEIFY_THRESHOLD = 8;            // 链表→红黑树
static final int UNTREEIFY_THRESHOLD = 6;           // 红黑树→链表
static final int MIN_TREEIFY_CAPACITY = 64;         // 树化最小容量

// 扩容条件
size > capacity * loadFactor  →  capacity × 2

为什么容量是 2 的幂?

  • (n - 1) & hash 等价于 hash % n,位运算更快
  • 扩容时,元素要么在原位置,要么在原位置+旧容量,无需重新计算 hash

2.3 JDK 7 vs JDK 8 HashMap

特性 JDK 7 JDK 8
链表插入 头插法(多线程成环) 尾插法
链表过长 始终链表 转 TreeNode(红黑树)
hash 计算 9 次扰动 1 次扰动
扩容 重新计算所有位置 原位置 or 原位置+旧容量

2.4 HashMap 为何线程不安全

JDK 7: 并发扩容时头插法导致链表成环 → 死循环

JDK 8: 并发 put 时数据覆盖

java 复制代码
// 两个线程同时判断桶为空,都执行写入
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
// 后一个覆盖前一个

2.5 ConcurrentHashMap(JDK 8)

java 复制代码
// 初始化(CAS)
if (tab == null || tab.length == 0)
    tab = initTable();  // CAS 保证只有一个线程初始化

// put 操作
Node<K,V> f; int n, i;
if (tab == null || (n = tab.length) == 0)
    tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    // 桶为空:CAS 写入
    if (casTabAt(tab, i, null, new Node<>(hash, key, value)))
        break;
} else {
    // 桶非空:synchronized 头节点
    synchronized (f) {
        // 遍历链表/红黑树插入
    }
}

三、Set 集合

3.1 HashSet

java 复制代码
// HashSet 底层就是 HashMap
public class HashSet<E> {
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();

    public boolean add(E e) {
        return map.put(e, PRESENT) == null;
    }
}

3.2 TreeSet

  • 底层 TreeMap(红黑树)
  • 元素有序(自然排序或 Comparator)
  • 添加/删除/查询 O(log n)

四、Queue 集合

4.1 PriorityQueue

java 复制代码
// 底层是最小堆(数组实现)
// offer: 上浮(sift up)  O(log n)
// poll:  下沉(sift down) O(log n)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(3); pq.offer(1); pq.offer(2);
pq.poll(); // 1(最小元素)

4.2 阻塞队列

队列 特点 适用场景
ArrayBlockingQueue 有界数组 线程池生产者消费者
LinkedBlockingQueue 可选有界链表 Executors 默认
SynchronousQueue 不存储元素 直接传递任务
PriorityBlockingQueue 无界优先级 优先级任务
DelayQueue 延迟取出 定时任务
java 复制代码
// 生产者-消费者模式
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);

// 生产者
queue.put("任务");  // 满时阻塞

// 消费者
String task = queue.take();  // 空时阻塞

五、面试高频题

Q1: HashMap 的底层数据结构?

考点 :HashMap 原理
难度 :⭐⭐⭐
答案:JDK 8 是数组 + 链表 + 红黑树。数组默认容量 16,负载因子 0.75。hash 冲突时形成链表,链表长度 ≥ 8 且数组长度 ≥ 64 转为红黑树,红黑树节点 ≤ 6 退回链表。

Q2: HashMap 的 hash 方法为什么用扰动函数?

考点 :hash 优化
难度 :⭐⭐⭐
答案(h ^ (h >>> 16)) 让高 16 位也参与索引计算,减少碰撞。因为容量通常较小,只有低位参与索引,高位不同但低位相同的 key 会冲突。

Q3: HashMap 和 ConcurrentHashMap 的区别?

考点 :并发容器
难度 :⭐⭐⭐
答案:HashMap 线程不安全;ConcurrentHashMap 线程安全。JDK 8 CHM 使用 CAS + synchronized(锁头节点),粒度细到桶级别,并发度等于数组长度。

Q4: ArrayList 的扩容机制?

考点 :动态数组
难度 :⭐⭐
答案:默认初始容量 10(首次 add 时),每次扩容为原来的 1.5 倍。使用 Arrays.copyOf 复制旧数组到新数组。

Q5: HashMap 为什么用红黑树而不是 AVL 树?

考点 :数据结构选择
难度 :⭐⭐⭐⭐
答案:红黑树插入/删除时旋转次数更少(最多 3 次 vs AVL 的 O(log n) 次),综合增删查性能更好。HashMap 场景增删频繁,红黑树更合适。

Q6: fail-fast 和 fail-safe 的区别?

考点 :迭代器机制
难度 :⭐⭐⭐
答案:fail-fast(ArrayList/HashMap):遍历时修改抛 ConcurrentModificationException,通过 modCount 检测。fail-safe(CopyOnWriteArrayList/ConcurrentHashMap):遍历副本或弱一致性,允许并发修改。

Q7: Comparable 和 Comparator 的区别?

考点 :排序接口
难度 :⭐⭐
答案:Comparable 在类内部实现 compareTo(),定义自然排序。Comparator 是外部比较器,实现 compare(),可以定义多种排序策略。Comparator 更灵活,符合开闭原则。

Q8: CopyOnWriteArrayList 的原理和适用场景?

考点 :并发容器
难度 :⭐⭐⭐
答案:写入时复制整个数组。读无锁,写加锁并复制。适合读多写极少(事件监听器列表、配置列表)。不适用写频繁场景(每次 O(n) 复制)。迭代器遍历的是快照。

Q9: HashMap 的负载因子为什么是 0.75?

考点 :HashMap 参数
难度 :⭐⭐⭐⭐
答案:时间和空间的折中。0.5 太浪费空间;1.0 碰撞太多。0.75 时泊松分布计算,每个桶 8 个以上元素的概率 < 千万分之一。也是 0.75 浮点数在二进制中精确表示。

Q10: ConcurrentHashMap 能完全替代 Hashtable 吗?

考点 :并发容器
难度 :⭐⭐⭐
答案:基本可以。ConcurrentHashMap 性能远优于 Hashtable(后者每个方法都 synchronized)。但 Hashtable 支持 null key/value,CHM 不支持。Hashtable 已被标记过时。

学习路径

  1. 基础:ArrayList、LinkedList、HashMap 使用
  2. 原理:HashMap 扩容、红黑树转换、hash 计算
  3. 并发:ConcurrentHashMap 实现、CopyOnWriteArrayList
  4. 应用:合理选择集合类型、性能优化
相关推荐
阿米亚波1 分钟前
【C++ STL】std::unordered_multiset
开发语言·数据结构·c++·笔记·stl
丙氨酸長鏈7 分钟前
[Bukkit插件开发]手持发射器箭矢机枪 教学文档 面向Python/C#开发者入门Java与Bukkit API
java·python·c#
Nontee8 分钟前
设计模式:模板方法与策略,从“每个字都认识“到能说清它们在干嘛
java·数据库·设计模式
我是唐青枫10 分钟前
Java ReentrantLock 实战详解:比 synchronized 更灵活的可重入锁
java·开发语言
QH1392923188021 分钟前
# R&S ZNB43 ZNA43 ZNA67矢量网络分析仪
开发语言·网络·php
晨曦中的暮雨1 小时前
Virtual Thread 优化 Spring AI 阻塞式 LLM I/O:对照压测报告
java·并发·个人项目
脚踏实地皮皮晨1 小时前
003003001_Grid控件
开发语言·windows·算法·c#·visual studio
十年磨剑走天涯1 小时前
C++ STL 容器操作复杂度速查表
开发语言·c++
always_TT1 小时前
【Python requirements.txt 依赖管理】
开发语言·python
15Moonlight1 小时前
C++进阶(09):特殊类设计
开发语言·c++