目录
1.ArrayList 底层原理 & 源码解析
2.LinkedList 底层原理 & 源码解析
3.迭代器底层原理与并发修改异常
4.泛型全套知识点(基础、伪泛型、泛型类 / 方法 / 接口、通配符)
一、ArrayList 底层原理与源码详解
1.基础核心结论
- 底层:Object 数组 elementData存储元素
- 空参构造初始化:创建长度为 0 的空数组,首次 add 才扩容到默认容量 10
- size 变量两层含义:当前集合元素个数;下一个待存入元素索引
- 扩容规则
○单个 add 添加:扩容至原长度 1.5 倍(oldCapacity >> 1)
○addAll 批量添加:若 1.5 倍容量仍不够,直接扩容至「原有元素数量 + 新增元素总数」 - 扩容底层依靠Arrays.copyOf():创建新数组,复制全部旧元素
2. 空参构造源码流程
- new ArrayList ():elementData 赋值为长度 0 的空数组,size=0
- 第一次调用 add ()
○判断 size 与数组长度相等,执行 grow () 扩容
○旧容量 oldCap=0,进入 else 分支,取默认容量 10 与最小需求容量 1 的最大值
○创建长度为 10 的新数组,赋值给 elementData
○元素存入 size 索引,size++
3. 普通扩容流程(数组存满,添加单个元素)
- 数组已满 (size == 数组长度),触发 grow
- oldCap = 10,默认扩容增量 = 10>>1=5,预期新容量 15
- minCapacity = size+1=10+1=1
- 取预期容量 15 作为新长度,Arrays.copyOf 复制所有元素
- 新数组存入新增元素,size 自增
4. 批量 addAll 扩容特殊逻辑
一次性添加大量元素时,计算最少需要增加的容量(新增元素个数),和默认扩容增量对比,取更大值作为扩容增量,保证能放下所有数据。
5. ArrayList 核心变量
- transient Object\[\] elementData:底层存储数组
- int size:实际元素个数
- modCount:集合修改计数器(add/remove 都会自增,用于迭代器校验并发修改)
6. 补充 API
addAll (集合):批量将传入集合所有元素添加至当前 ArrayList
二、LinkedList 底层原理与源码详解
1.基础核心结论
- 底层:双向链表,无数组,不存在扩容机制
- 内部私有内部类 Node:链表节点
- 维护三个全局变量:size(节点总数)、first(头节点)、last(尾节点)
- 优势:头尾增删速度极快;中间位置增删需要遍历查找节点,效率低
- 劣势:随机查询效率低,必须遍历链表
2. Node 节点结构(三成员)
java
private static class Node<E> {
E item; // 当前存储元素
Node<E> prev; // 上一节点地址
Node<E> next; // 下一节点地址
}
3. 空参构造
new LinkedList ():size=0,first=null,last=null,无任何节点
4. add () 尾部添加流程(底层 linkLast ())
- 用局部变量 l 记录原尾节点 last
- 新建 Node:prev=l,item = 待添加元素,next=null
- 将新节点赋值给 last
- 判断原尾节点 l 是否为 null(集合为空)
○为空:first 也指向新节点(链表只有一个节点,头尾相同)
○不为空:原尾节点 l.next = 新节点,完成双向绑定 - size++,modCount++
5. LinkedList 独有 API(操作首尾)
addFirst () 头部添加 | addLast () 尾部添加(等价 add)
getFirst () 获取头元素 | getLast () 获取尾元素
removeFirst () 删除头 | removeLast () 删除尾
三、迭代器底层原理 & 并发修改异常
1.迭代器获取方式
list.iterator ():返回内部类 ITR 迭代器对象
2. ITR 迭代器核心成员变量
1.cursor:迭代器指针,默认初始值 0(指向待读取索引)
2.lastRet:上一次读取元素的索引,初始 - 1
3.expectedModCount:创建迭代器时记录的集合修改次数
3. 核心方法底层逻辑
hasNext()
判断cursor != size,不等代表还有未读取元素,返回 true;反之无元素
next () 执行流程
- 先执行 checkForModification () 校验并发修改
- 记录当前 cursor 到局部变量 i
- cursor 指针 + 1(后移)
- lastRet = i,返回底层数组 i 对应元素
NoSuchElementException 报错场景
cursor 等于 size 后继续调用 next,无元素可读取,抛出异常
4. 并发修改异常 ConcurrentModificationException
核心变量 modCount
集合每 add/remove 一次,modCount 自增,记录集合结构修改总次数。
触发原因
- 创建迭代器时,迭代器复制当前 modCount 存入 expectedModCount
- 遍历过程中,使用集合 add/remove 方法修改集合
- 调用 next () 时执行校验:if (modCount != expectedModCount) 直接抛异常
规避方案
遍历过程中,不要用集合对象增删元素;如需删除使用迭代器自身 remove () 方法。
四、泛型全套知识点
4.1 泛型基础概念
1.诞生版本:JDK5 引入
2.作用:编译阶段约束集合存储数据类型,统一元素类型
3.格式:类 / 接口 / 方法后加<类型>,仅支持引用类型,不能使用基本类型(int→Integer)
无泛型的弊端
1.集合默认存 Object,任意类型都能存入
2.取出元素需要强制类型转换,极易抛出 ClassCast 类型转换异常
3.类型错误运行期才暴露,难以排查
泛型带来两大好处
1.编译期校验类型,不符合直接报错,提前规避问题
2.取出元素无需强转,简化代码
4.2 Java 泛型本质:伪泛型(泛型擦除)
1.泛型仅存在于.java 源码编译阶段
2.编译为.class 字节码时,所有泛型标识全部擦除(泛型擦除)
3.底层实际存储仍为 Object,取出时编译器自动插入强制转换代码
4.设计目的:兼容 JDK5 之前无泛型的旧代码,无需大规模改造历史项目
4.3 泛型三大载体
1.泛型类
定义:类名后<E/T/K/V>,类内全局可使用该不确定类型
java
public class MyArrayList<E> {
Object[] arr = new Object[10];
public boolean add(E e){
arr[size++] = e;
return true;
}
public E get(int index){
return (E) arr[index];
}
}
使用:创建对象时确定泛型MyArrayList list = new MyArrayList<>();
2.泛型方法
修饰符后定义,仅当前方法可用该不确定类型,不受类泛型限制
适用场景:仅单个方法类型不确定,无需整个类泛型
java
java
// 工具类静态泛型方法示例
public static <E> void addAll(List<E> list, E... elements){
for(E e : elements) list.add(e);
}
可变参数E...:底层数组,可传入任意多个同类型参数
3.泛型接口
两种实现方式
1.实现类直接指定具体类型
2.实现类延续泛型,创建对象时再确定
4.4 泛型继承与通配符
关键规则:泛型不具备继承性,存储的数据具备继承性
示例:ArrayList 不能接收 ArrayList 对象
但ArrayList中可以 add (new Dog ())、add (new Cat ())
通配符?(代表任意未知类型)
1.? extends E:上界限定,只能是 E 或 E 的子类;只读,不能存数据
2.? super E:下界限定,只能是 E 或 E 的父类;可以存入 E 及其子类
3.单独?:无限制,任意类型
使用场景区分
1.类型全程固定:泛型类 / 泛型方法
2.类型不确定,但限定在某条继承链:通配符 extends/super
4.5 泛型细节注意点
1.泛型尖括号内只能写包装类,禁止 byte/short/int 等基本类型
2.指定泛型后,可存入当前类型、其子类对象
3.不写泛型则默认原始类型,底层 Object,失去类型校验
4.通配符和泛型方法区分:泛型方法可读写,单纯?通配符有读写限制
五、本章对比总结

泛型核心速记
1.编译检查,字节码擦除,伪泛型
2.三类:泛型类、泛型方法、泛型接口
3.泛型无继承,元素有继承
4.? extends 存子类,? super 可以存父类