ArrayList核心逻辑

ArrayList 是Java 集合框架中最常用的动态数组实现类。它在 java.util.ArrayList 包下,继承自 AbstractList,实现了 List 接口。

核心特点:底层基于数组实现,容量可以自动扩容,支持快速随机访问

一、核心特点

  • 动态扩容 :初始容量默认是 10,存满后会自动扩容为原来的 1.5 倍
  • 有序可重复 :元素存入顺序和取出顺序一致,允许存储重复元素、null 值;
  • 随机访问快:通过索引访问元素的时间复杂度是 O (1),效率极高;
  • 增删慢:中间插入 / 删除元素需要移动后续元素,时间复杂度 O (n);
  • 非线程安全 :多线程环境下不建议使用(推荐 CopyOnWriteArrayList)。

二、基础使用

1. 创建ArrayList

java 复制代码
import java.util.ArrayList;

// 推荐:指定泛型(约束存储的元素类型)
ArrayList<String> list = new ArrayList<>(); 

2. 常用方法

java 复制代码
// 1. 添加元素
list.add("Java");
list.add("Python");
list.add(1, "C++"); // 在索引 1 位置插入元素

// 2. 获取元素
String s = list.get(0); // 获取索引 0 的元素

// 3. 修改元素
list.set(1, "Go"); // 把索引 1 的元素改为 Go

// 4. 删除元素
list.remove(0); // 根据索引删除
list.remove("Java"); // 根据元素删除

// 5. 其他常用方法
list.size(); // 获取元素个数
list.isEmpty(); // 判断是否为空
list.contains("Python"); // 判断是否包含某个元素
list.clear(); // 清空集合

3. 遍历 ArrayList

java 复制代码
// 方式1:for 循环(通过索引,推荐随机访问)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// 方式2:增强 for 循环(最常用)
for (String str : list) {
    System.out.println(str);
}

// 方式3:迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

三、ArrayList底层原理

1. 底层数据结构

Object 数组 transient Object[] elementData;

2. 初始容量

  • JDK 1.7:默认初始容量 10;
  • JDK 1.8+:懒加载,第一次添加元素时才初始化容量为 10;

3. 扩容机制

当元素个数达到数组容量时,自动扩容:新容量 = 旧容量 + 旧容量 >> 1(等价于 1.5 倍);扩容后会用 Arrays.copyOf() 复制原数组元素。

四、如何保证 ArrayList 的线程安全

ArrayList 本身是非线程安全的,多线程同时读写会出现数据错乱、并发修改异常**(ConcurrentModificationException)**。

Java 提供了 3 种标准方案保证线程安全:

1. Collections.synchronizedList ()(简单包装)

这是最快实现线程安全的方式,对 ArrayList 加锁包装。

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

// 把普通 ArrayList 变成线程安全
List<String> safeList = Collections.synchronizedList(new ArrayList<>());

特点:

  • 简单、一行代码搞定
  • 粗粒度锁,并发性能一般
  • 遍历时仍需要手动加锁

遍历注意: 必须手动加锁,否则仍会报错。

java 复制代码
synchronized (safeList) {  // 必须加锁
    for (String s : safeList) {
        System.out.println(s);
    }
}

2. 使用 Vector(古老、不推荐)

Vector 是 Java 早期的线程安全集合,所有方法都加 synchronized

特点:

  • 线程安全,但性能差
  • 几乎被淘汰,不要在新项目使用

3. CopyOnWriteArrayList(高并发首选)

Java 官方推荐的线程安全 List ,属于 java.util.concurrent 并发包。

java 复制代码
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

// 最推荐的线程安全 List
List<String> safeList = new CopyOnWriteArrayList<>();

核心原理:

  • 写时复制:添加 / 删除时,复制一个新数组操作
  • 读不加锁,写加锁
  • 适合:读多写少 的高并发场景

优点:

  • 完全线程安全
  • 遍历不需要加锁
  • 高并发下性能远好于 synchronizedList

4. 三种方案对比

方案 线程安全 性能 适用场景
ArrayList ❌ 不安全 最好 单线程
Collections.synchronizedList ✅ 安全 一般 简单并发、低并发
Vector ✅ 安全 最差 老项目、不推荐
CopyOnWriteArrayList ✅ 安全 读高并发优秀 读多写少、高并发

五、ArrayList 与 普通数组 的区别

特性 普通数组 ArrayList
容量 固定不可变 动态自动扩容
存储类型 基本类型 + 引用类型 只能存引用类型
功能 简单存取 提供丰富的增删改查方法
使用场景 固定长度数据 长度不确定、频繁操作

注意:ArrayList 不能存 int、char 等基本类型,只能存包装类 Integer、Character

六、使用注意事项

1. 指定初始容量

如果能预估元素数量,创建时指定容量,减少扩容开销。

2. 线程不安全

多线程下不要直接使用,可使用:

  • 日常开发、低并发使用 Collections.synchronizedList()
  • 高并发、读多写少使用 CopyOnWriteArrayList

3. 遍历删除问题

在 ArrayList 遍历过程中删除元素,只有迭代器(Iterator)的删除方式是完全安全、不会报错的。

直接用 for/增强for 删除都会出问题:

  • 普通 for:漏删、下标越界
  • 增强 for:ConcurrentModificationException 并发修改异常

用迭代器删除ArrayList元素关键点

  • 必须用 it.remove() ,不能用 list.remove()
  • 必须先调用 it.next() ,再调用 remove()
  • 不会抛异常,不会漏删,最安全

不能用 list.remove ()的原因:

  • 遍历过程中,直接调用集合的删除方法会修改集合结构,导致迭代器检测到 "被修改",直接抛出异常。

迭代器删除原理:

  • ① 迭代器会记录集合修改次数
  • ② 只有迭代器自己的 remove () 会同步修改次数,不会触发异常
  • ③ 外部调用 list.remove() 会导致修改次数不匹配 → 报错
相关推荐
叶子野格5 小时前
《C语言学习:编程例题》B
c语言·开发语言·c++·学习
cen__y5 小时前
Linux13(数据库)
linux·服务器·c语言·开发语言·数据库
亦暖筑序5 小时前
Spring AI Alibaba 1.1.2 实战:5种多Agent编排模式完全指南
java·spring boot·ai编程
happyprince5 小时前
05-Hugging Face Transformers 缓存系统深度分析
java·spring·缓存
人还是要有梦想的5 小时前
QT数据库乱码、QT qml import导入库报错、ui界面分层设计
开发语言·qt·ui
吃好睡好便好5 小时前
创建上三角矩阵和下三角矩阵
开发语言·学习·线性代数·matlab·矩阵
苕皮蓝牙土豆6 小时前
【Qt工业上位机实战】从零打造高性能串口监控终端
开发语言·qt
大数据三康6 小时前
Java静态常量与静态导入:计算圆面积
java·开发语言
郝学胜-神的一滴6 小时前
Qt 高级开发014 :信号槽connect函数精讲
开发语言·c++·qt·开源软件·用户界面