Java ArrayList 完整详解
一、概述
ArrayList 是 java.util 包下基于动态数组实现的 List 集合,底层是 Object\[\] 数组,自动扩容,支持随机访问、增删改查。
- 线程不安全 ,多线程并发修改会抛
ConcurrentModificationException - 允许存放
null,元素可重复、有序(插入顺序不变) - 查询快、中间/头部删除插入慢(需要数组移位)
类关系
java
ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
RandomAccess 标记接口:支持下标快速随机访问,遍历优先用普通for循环。
二、底层原理
- 默认初始容量 :
DEFAULT_CAPACITY = 10 - 空构造器先赋值空数组,第一次add时才扩容到10,节省内存
- 扩容机制 :
- 新容量 = 旧容量 + 旧容量 / 2(1.5倍扩容)
- 若1.5倍仍不够,直接取所需最小容量
- 最大容量:
Integer.MAX_VALUE - 8
- 增删操作:数组复制
Arrays.copyOf(),移位消耗性能
三、常用构造方法
java
// 1. 空构造,初始数组为空,首次add扩容至10
ArrayList<String> list1 = new ArrayList<>();
// 2. 指定初始容量(推荐已知数据量时使用,减少扩容)
ArrayList<Integer> list2 = new ArrayList<>(20);
// 3. 传入集合,复制所有元素
List<Integer> temp = Arrays.asList(1,2,3);
ArrayList<Integer> list3 = new ArrayList<>(temp);
四、核心常用API
1. 添加元素
java
ArrayList<String> list = new ArrayList<>();
list.add("A"); // 尾部添加
list.add(1, "B"); // 指定下标插入(下标不能越界)
list.addAll(Arrays.asList("C","D")); // 批量添加集合
2. 获取元素
java
String s = list.get(0); // 根据下标取值,越界抛IndexOutOfBoundsException
int size = list.size(); // 获取集合元素个数
3. 修改元素
java
list.set(0, "AA"); // 替换指定下标元素,返回旧值
4. 删除元素
java
list.remove(0); // 按下标删除,返回被删元素
list.remove("B"); // 按对象删除,只删第一个匹配元素,返回boolean
list.removeAll(Arrays.asList("C","D")); // 删除交集元素
list.clear(); // 清空所有元素,数组保留容量
5. 判断与查找
java
list.contains("A"); // 是否包含元素
list.indexOf("A"); // 首个匹配下标,无则-1
list.lastIndexOf("A"); // 最后一个匹配下标
list.isEmpty(); // 是否为空集合
6. 转换数组
java
Object[] arr1 = list.toArray();
String[] arr2 = list.toArray(new String[0]); // 转指定类型数组
五、三种遍历方式
1. 普通for(推荐,随机访问最快)
java
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
2. 增强for循环(foreach,底层迭代器)
java
for (String str : list) {
System.out.println(str);
}
3. Iterator迭代器(遍历中安全删除)
java
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if ("A".equals(s)) {
it.remove(); // 安全删除,不会并发修改异常
}
}
❌ 禁止foreach里直接
list.remove(),会触发并发修改异常。
六、线程安全问题
ArrayList 非线程安全,多线程同时读写会报错或数据错乱。
解决方案
Vector:旧集合,方法全加synchronized,性能差,不推荐Collections.synchronizedList():包装成同步集合
java
List<String> safeList = Collections.synchronizedList(new ArrayList<>());
CopyOnWriteArrayList:读写分离,读不加锁,写复制新数组,读多写少场景首选
七、常用示例代码
java
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<>();
// 添加
nums.add(10);
nums.add(20);
nums.add(30);
nums.add(1, 15);
// 查询
System.out.println(nums.get(1)); // 15
System.out.println(nums.size()); // 4
// 修改
nums.set(0, 99);
// 删除
nums.remove(Integer.valueOf(30));
// 遍历
for (Integer num : nums) {
System.out.print(num + " ");
}
}
}
八、ArrayList vs LinkedList
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层 | 动态数组 | 双向链表 |
| 查询 | 快 O(1) | 慢 O(n) |
| 头尾增删 | 尾部快,头部慢 | 头尾极快 O(1) |
| 内存 | 连续数组,占用少 | 每个节点存前后指针,开销大 |
| 适用场景 | 大量查询、少量删除 | 频繁头部/中间插入删除 |
九、常见坑
- 下标越界 :
get(i)、add(i,obj)i >= size 抛异常 - foreach遍历中调用list.remove() → ConcurrentModificationException,要用迭代器删除
Arrays.asList()返回的集合不能add/remove,底层固定数组,需套new ArrayList<>()- 存储基础类型会自动装箱成包装类(int→Integer)
- 扩容会产生大量数组复制,大数据初始化建议指定容量