第50章

Java ArrayList

动态数组详解与实战应用 - 掌握ArrayList的使用和特性

学习目标

ArrayList简介

ArrayList是Java集合框架中最常用的动态数组实现,它基于数组实现,支持动态扩容,是List接口的一个重要实现类。ArrayList允许存储任何类型的对象(包括null),并且保持元素的插入顺序。

动态扩容

ArrayList会根据需要自动扩容,初始容量为10,每次扩容为原来的1.5倍。

有序集合

ArrayList保持元素的插入顺序,支持通过索引快速访问元素。

允许重复

ArrayList允许存储重复的元素,也可以存储null值。

非线程安全

ArrayList不是线程安全的,多线程环境下需要额外的同步措施。

ArrayList基础操作

创建ArrayList

ArrayList创建方式
import java.util.ArrayList;
import java.util.List;

// 方式1:默认构造函数(初始容量为10)
ArrayList<String> list1 = new ArrayList<>();

// 方式2:指定初始容量
ArrayList<String> list2 = new ArrayList<>(20);

// 方式3:从其他集合创建
List<String> sourceList = List.of("Java", "Python", "C++");
ArrayList<String> list3 = new ArrayList<>(sourceList);

// 方式4:使用接口引用(推荐)
List<String> list4 = new ArrayList<>();

添加元素

添加元素操作
List<String> languages = new ArrayList<>();

// 在末尾添加元素
languages.add("Java");
languages.add("Python");
languages.add("JavaScript");

// 在指定位置插入元素
languages.add(1, "C++");

// 添加多个元素
List<String> newLanguages = List.of("Go", "Rust");
languages.addAll(newLanguages);

// 在指定位置插入多个元素
languages.addAll(2, List.of("C#", "Swift"));

访问和修改元素

访问和修改操作
List<String> fruits = new ArrayList<>();
fruits.addAll(List.of("苹果", "香蕉", "橙子", "葡萄"));

// 通过索引获取元素
String first = fruits.get(0);  // 获取第一个元素
String last = fruits.get(fruits.size() - 1);  // 获取最后一个元素

// 获取元素索引
int index = fruits.indexOf("橙子");  // 返回2
int notFound = fruits.indexOf("西瓜");  // 返回-1

// 修改指定位置的元素
String oldFruit = fruits.set(1, "草莓");  // 将"香蕉"替换为"草莓"

// 检查是否包含元素
boolean hasApple = fruits.contains("苹果");  // true

删除元素

删除元素操作
List<String> numbers = new ArrayList<>();
numbers.addAll(List.of("1", "2", "3", "4", "5", "3"));

// 通过索引删除
String removed = numbers.remove(0);  // 删除第一个元素

// 通过对象删除(删除第一个匹配的)
boolean isRemoved = numbers.remove("3");  // 删除第一个"3"

// 批量删除
List<String> toRemove = List.of("2", "4");
numbers.removeAll(toRemove);

// 条件删除(Java 8+)
numbers.removeIf(n -> Integer.parseInt(n) > 3);

// 清空所有元素
numbers.clear();
💻 查看完整代码 - 在线IDE体验

ArrayList遍历方法

四种遍历方式
List<String> list = Arrays.asList("A", "B", "C", "D");

// 方式1:传统for循环
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// 方式2:增强for循环(推荐)
for (String item : list) {
    System.out.println(item);
}

// 方式3:迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    System.out.println(item);
    // 可以安全删除元素
    // iterator.remove();
}

// 方式4:Stream API(Java 8+)
list.stream().forEach(System.out::println);

性能特点与优化

时间复杂度分析

操作 时间复杂度 说明
添加到末尾 O(1) 摊销时间复杂度,偶尔需要扩容
插入到指定位置 O(n) 需要移动后续元素
删除末尾元素 O(1) 直接删除,无需移动元素
删除指定位置 O(n) 需要移动后续元素
随机访问 O(1) 基于数组索引,非常快
查找元素 O(n) 线性查找,需要遍历

扩容机制

ArrayList扩容规律
// ArrayList扩容机制
初始容量:10(默认构造函数)
扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1)
扩容倍数:1.5倍

// 示例:容量变化过程
10 → 15 → 22 → 33 → 49 → 73 → 109 → ...

性能优化建议

  • 预估数据量,使用带容量参数的构造函数避免频繁扩容
  • 批量操作使用addAll()而不是多次调用add()
  • 从尾部删除元素效率更高,避免从头部删除
  • 使用removeIf()进行条件删除,比传统循环更安全
  • 大量随机访问场景选择ArrayList,频繁插入删除选择LinkedList

ArrayList vs 其他集合

特性 ArrayList LinkedList Vector Array
随机访问 O(1) ✅ O(n) ❌ O(1) ✅ O(1) ✅
插入/删除(头部) O(n) ❌ O(1) ✅ O(n) ❌ O(n) ❌
插入/删除(尾部) O(1) ✅ O(1) ✅ O(1) ✅ 固定大小
线程安全 否 ❌ 否 ❌ 是 ✅ 否 ❌
内存占用 较少 ✅ 较多 ❌ 较少 ✅ 最少 ✅
动态大小 是 ✅ 是 ✅ 是 ✅ 否 ❌

实际应用场景

购物车功能

存储用户选择的商品,支持添加、删除、修改数量等操作。

学生成绩管理

管理学生成绩列表,支持查询、排序、统计等功能。

数据过滤转换

对数据进行过滤、映射、排序等操作,配合Stream API使用。

分页功能

实现数据分页显示,通过subList()方法获取指定页面数据。

注意事项

线程安全问题

ArrayList不是线程安全的。在多线程环境下,如果多个线程同时修改ArrayList,可能导致数据不一致或抛出异常。解决方案:

  • 使用Collections.synchronizedList()包装
  • 使用CopyOnWriteArrayList(读多写少场景)
  • 使用显式同步(synchronized关键字)

内存管理

ArrayList在删除元素时不会自动缩容,可能造成内存浪费。如果需要释放内存,可以调用trimToSize()方法。

迭代时修改

在使用增强for循环或迭代器遍历时,不能直接修改ArrayList结构(添加或删除元素),否则会抛出ConcurrentModificationException。应该使用迭代器的remove()方法或removeIf()。