一、什么是 List
在 Java 集合框架中,List 是一个有序集合,允许存储重复元素。
特点:
-
元素有顺序
-
允许重复
-
支持索引访问
示例:
java
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("Java");
System.out.println(list.get(0));
输出:
Java
说明:
List 可以重复
二、List 集合体系结构
Java List 主要实现类:
Collection
│
└── List
│
├── ArrayList
│
├── LinkedList
│
└── Vector
│
└── Stack
常用实现类:
| 类 | 特点 |
|---|---|
| ArrayList | 查询快 |
| LinkedList | 插入删除快 |
| Vector | 线程安全(过时) |
| Stack | 栈结构 |
日常开发中:
90% 使用 ArrayList
三、ArrayList 底层结构
ArrayList 本质:
动态数组
源码:
java
transient Object[] elementData;
结构:
elementData
│
├─ [0] Java
├─ [1] Python
├─ [2] C++
特点:
连续内存
优点:
随机访问快
四、ArrayList 扩容机制(重点)
默认容量:
10
源码:
java
private static final int DEFAULT_CAPACITY = 10;
初始化:
java
List list = new ArrayList();
第一次 add:
容量 = 10
扩容规则
扩容公式:
java
newCapacity = oldCapacity + oldCapacity >> 1
即:
1.5倍扩容
示例:
10 → 15 → 22 → 33 → 49
源码:
java
int newCapacity = oldCapacity + (oldCapacity >> 1);
扩容流程
1 创建新数组
2 复制旧数组数据
3 指向新数组
源码:
java
Arrays.copyOf(elementData, newCapacity);
五、ArrayList 时间复杂度
| 操作 | 时间复杂度 |
|---|---|
| get | O(1) |
| add(尾部) | O(1) |
| add(中间) | O(n) |
| remove | O(n) |
原因:
数组需要移动元素
示例:
[1,2,3,4]
删除 2
变成
[1,3,4]
需要移动:
n-1 次
六、LinkedList 底层结构
LinkedList 本质:
双向链表
源码:
java
Node<E> first;
Node<E> last;
节点结构:
java
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
结构:
null <- A <-> B <-> C -> null
每个节点包含:
prev
data
next
七、LinkedList 特点
优点:
插入删除快
缺点:
查询慢
时间复杂度:
| 操作 | 时间复杂度 |
|---|---|
| get | O(n) |
| add | O(1) |
| remove | O(1) |
八、LinkedList 查询优化
LinkedList 会判断:
index < size/2
如果:
index < size/2
从头遍历:
first
否则:
last
从尾遍历。
源码:
java
if (index < (size >> 1)) {
Node<E> x = first;
} else {
Node<E> x = last;
}
优化查询效率。
九、ArrayList vs LinkedList
| 对比 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 数组 | 双向链表 |
| 查询 | 快 | 慢 |
| 插入 | 慢 | 快 |
| 删除 | 慢 | 快 |
| 内存占用 | 小 | 大 |
选择建议:
查询多 → ArrayList
增删多 → LinkedList
但现实开发:
几乎都用 ArrayList
原因:
CPU缓存友好
十、Vector 原理
Vector 和 ArrayList 类似:
动态数组
区别:
线程安全
源码:
java
public synchronized boolean add(E e)
所有方法:
synchronized
缺点:
性能差
所以:
基本不用
十一、fail-fast 机制
Java 集合有一个重要机制:
fail-fast
意思:
快速失败
示例:
java
for(String s : list){
list.remove(s);
}
会抛异常:
ConcurrentModificationException
原因:
modCount 变化
源码:
java
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
十二、Iterator 迭代器原理
迭代器作用:
遍历集合
示例:
java
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
结构:
Iterator
│
hasNext()
next()
remove()
如果需要删除:
必须用:
it.remove();
不能:
list.remove()
否则触发:
fail-fast
十三、List 常用方法
java
add(E e)
add(int index,E e)
get(int index)
set(int index,E e)
remove(int index)
size()
clear()
示例:
java
list.add("Java");
list.add("Python");
list.remove(1);
list.set(0,"C++");
十四、List 遍历方式
1 for循环
java
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
适合:
ArrayList
2 增强for
java
for(String s:list){
System.out.println(s);
}
3 Iterator
java
Iterator<String> it = list.iterator();
适合:
删除元素
4 forEach
list.forEach(System.out::println);
十五、ArrayList 面试高频问题
1 ArrayList 底层结构
动态数组
2 ArrayList 默认容量
10
3 ArrayList 扩容多少倍
1.5倍
4 为什么 ArrayList 查询快
因为:
数组连续存储
可以:
随机访问
5 ArrayList 为什么线程不安全
原因:
多个线程同时修改 elementData
可能导致:
数据覆盖
6 ArrayList 和 Vector 区别
| ArrayList | Vector | |
|---|---|---|
| 线程安全 | 否 | 是 |
| 性能 | 高 | 低 |
| 是否推荐 | 是 | 否 |
十六、总结
Java List 核心结构:
List
│
├─ ArrayList
│ 动态数组
│
├─ LinkedList
│ 双向链表
│
└─ Vector
线程安全数组
核心知识点:
ArrayList扩容
LinkedList结构
fail-fast机制
Iterator原理
时间复杂度