一篇吃透 ArrayList:动态数组、扩容机制、遍历删除全搞定
一、 ArrayList 的核心定位
ArrayList 是 Java 集合框架中List接口的可变数组实现,对比我们初学的普通数组:
普通数组:长度固定,一旦创建就不能修改,增删元素需要手动新建数组复制
ArrayList:长度动态可变,底层自动维护数组扩容 / 缩容,提供了丰富的操作方法(增删改查)
注意:ArrayList 只能存储引用类型(如 String、Integer),不能直接存基本类型(int、double),需要用对应的包装类
二、ArrayList 基础操作
举个例子:
import java.util.ArrayList;
import java.util.List;
public class ArrayListBasic {
public static void main(String[] args) {
// 1. 创建ArrayList:指定泛型(String),限定只能存字符串类型
List<String> fruitList = new ArrayList<>();
// 2. 新增元素:add()方法(尾部添加)
fruitList.add("苹果");
fruitList.add("香蕉");
fruitList.add("橙子");
System.out.println("添加元素后:" + fruitList); // 输出:[苹果, 香蕉, 橙子]
// 3. 指定位置新增元素:add(索引, 元素)
fruitList.add(1, "草莓"); // 在索引1的位置插入草莓
System.out.println("指定位置添加后:" + fruitList); // 输出:[苹果, 草莓, 香蕉, 橙子]
// 4. 获取元素:get(索引)
String firstFruit = fruitList.get(0);
System.out.println("索引0的元素:" + firstFruit); // 输出:苹果
// 5. 修改元素:set(索引, 新值)
fruitList.set(2, "蓝莓"); // 把索引2的香蕉改成蓝莓
System.out.println("修改后:" + fruitList); // 输出:[苹果, 草莓, 蓝莓, 橙子]
// 6. 删除元素:两种方式
fruitList.remove(3); // 按索引删除(删除橙子)
// fruitList.remove("草莓"); // 按元素值删除(删除草莓)
System.out.println("删除后:" + fruitList); // 输出:[苹果, 草莓, 蓝莓]
// 7. 遍历ArrayList:三种常用方式
System.out.println("\n=== 遍历方式1:普通for循环(可操作索引)==");
for (int i = 0; i < fruitList.size(); i++) {
System.out.println("索引" + i + ":" + fruitList.get(i));
}
System.out.println("\n=== 遍历方式2:增强for循环(简洁)==");
for (String fruit : fruitList) {
System.out.println(fruit);
}
System.out.println("\n=== 遍历方式3:forEach Lambda==");
fruitList.forEach(fruit -> System.out.println(fruit));
// 8. 其他常用方法
System.out.println("\n集合大小:" + fruitList.size()); // 输出:3
System.out.println("是否包含苹果:" + fruitList.contains("苹果")); // 输出:true
System.out.println("苹果的索引:" + fruitList.indexOf("苹果")); // 输出:0
fruitList.clear(); // 清空集合
System.out.println("清空后:" + fruitList); // 输出:[]
System.out.println("是否为空:" + fruitList.isEmpty()); // 输出:true
}
}
三、 ArrayList 的核心特性
1. 动态扩容机制
ArrayList 底层是一个Object[]数组,默认初始容量是 10。当添加元素导致数组满了时,会自动扩容:
扩容规则:新容量 = 原容量 * 1.5
本质:新建一个更大的数组,把原数组的元素复制过去
用代码直观感受扩容(重点理解 “动态”):
import java.util.ArrayList;
public class ArrayListExpand {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
// 连续添加11个元素(超过默认初始容量10)
for (int i = 1; i <= 11; i++) {
list.add(i);
System.out.println("添加第" + i + "个元素后,集合大小:" + list.size());
}
// 输出会看到集合大小从1到11,说明自动扩容了,不用手动处理
}
}
2. 泛型的作用(避免类型错误)
如果创建时不指定泛型,ArrayList 会变成 “万能容器”,容易出现类型转换异常,我们一定要养成指定泛型的习惯:
import java.util.ArrayList;
public class ArrayListGeneric {
public static void main(String[] args) {
// 错误示范:不指定泛型,能存任意类型
ArrayList badList = new ArrayList();
badList.add("字符串");
badList.add(123); // 存整数
// 取元素时需要强制类型转换,容易报错
String s = (String) badList.get(1); // 运行时报错:Integer不能转String
// 正确示范:指定泛型<Integer>,只能存整数
ArrayList<Integer> goodList = new ArrayList<>();
goodList.add(123);
// goodList.add("字符串"); // 编译时就报错,提前规避问题
int num = goodList.get(0); // 无需强制转换
}
}
3. ArrayList vs 普通数组(互转示例)
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListVsArray {
public static void main(String[] args) {
// 1. ArrayList转普通数组
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
// toArray()方法:注意指定数组类型
String[] arr = list.toArray(new String[0]);
System.out.println("ArrayList转数组:" + Arrays.toString(arr)); // 输出:[A, B]
// 2. 普通数组转ArrayList
String[] arr2 = {"X", "Y", "Z"};
ArrayList<String> list2 = new ArrayList<>(Arrays.asList(arr2));
System.out.println("数组转ArrayList:" + list2); // 输出:[X, Y, Z]
}
}
四、常见易错点
1. 遍历删除元素的坑
增强 for 循环中直接删除元素会抛出ConcurrentModificationException,正确方式用迭代器或普通 for 循环(倒序):
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListRemoveError {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
// 错误方式:增强for循环删除
/*
for (String s : list) {
if (s.equals("b")) {
list.remove(s); // 抛出ConcurrentModificationException
}
}
*/
// 正确方式1:迭代器删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("b")) {
it.remove(); // 用迭代器的remove方法
}
}
System.out.println("迭代器删除后:" + list); // 输出:[a, c]
// 正确方式2:普通for循环(倒序)
ArrayList<String> list2 = new ArrayList<>();
list2.add("1");
list2.add("2");
list2.add("3");
for (int i = list2.size() - 1; i >= 0; i--) {
if (list2.get(i).equals("2")) {
list2.remove(i);
}
}
System.out.println("倒序删除后:" + list2); // 输出:[1, 3]
}
}
2. 索引越界异常
常犯:获取 / 删除元素时索引超出集合大小,比如集合只有 3 个元素(索引 0-2),却去 get (3):
ArrayList<String> list = new ArrayList<>();
list.add("a");
// list.get(1); // 报错:索引1超出范围(当前大小1)
// 规避:操作索引前先判断大小
if (list.size() > 1) {
System.out.println(list.get(1));
} else {
System.out.println("索引不存在");
}
总结
核心特性:ArrayList 是动态可变数组,底层自动扩容,相比普通数组更灵活,但增删中间元素效率较低(需要移动元素)。
基础使用:掌握add/get/set/remove/size/contains等核心方法,遍历优先用增强 for 或 Lambda。
避坑要点:指定泛型避免类型错误,遍历删除用迭代器 / 倒序 for 循环,操作索引前先检查集合大小。
更多推荐




所有评论(0)