【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. 应用:合理选择集合类型、性能优化
相关推荐
“AI国潮设计-小江”3 小时前
AIGC实战:Python+SDXL批量生成英歌舞IP立牌,解锁猪猪蛋糕“反差萌”流量密码(附完整API源码)
开发语言·人工智能·python·prompt·aigc
Sirens.3 小时前
网络通信基础:IP、端口、协议分层与数据封装
java·网络·网络协议·tcp/ip·java-ee·1024程序员节
H.莓飛3 小时前
【数据结构】排序_OJ题(二):划分与选择
linux·服务器·c语言·开发语言·数据结构·visual studio
CRMEB系统商城3 小时前
服装连锁的导购分销与企微私域
java·大数据·开发语言·人工智能·小程序·php·1024程序员节
若亦_3 小时前
Java泛型入门:类型安全与上界约束
java·1024程序员节
凉茶钱3 小时前
stack、queue、priority_queue、容器适配器完整梳理
开发语言·c++·queue·1024程序员节·stack
编程的一拳超人3 小时前
【元宝搜索开发者征文大赛】四语实战:Java/Python/Go/Node.js 构建 AI 搜索应用全攻略
java·python·golang·node.js·agent
wuminyu3 小时前
JVM规范学习总结-Java源码经过javac编译后产物
java·linux·c语言·jvm·c++
ly76894 小时前
Spring Security 过滤器链定制:认证、CORS 与 CSRF 的先后顺序
java·spring boot·spring·spring security·csrf·cors·过滤器链
云和数据.ChenGuang4 小时前
Python 菜鸟入门:字典(dict)类型详解与 CRUD 实战案例
开发语言·人工智能·python