作为Java集合框架中最常用的动态数组实现,ArrayList在日常开发中扮演着重要角色。本文将深入剖析ArrayList的核心操作,通过三个典型场景的实战演练,带你掌握字符串存储、对象管理以及集合修改等关键技能。
ArrayList底层基于数组实现,具有以下核心特点:
与普通数组相比,ArrayList的优势在于:
在开始实战前,请确保:
推荐项目配置:
java复制// pom.xml依赖(Maven项目)
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
给定字符串数组["aaa", "bbb", "aaa", "aaa", "ccc", "bbb"],需要:
java复制import java.util.ArrayList;
public class StringListDemo {
public static void main(String[] args) {
// 1. 创建ArrayList实例(推荐使用接口类型声明)
ArrayList<String> strList = new ArrayList<>();
// 2. 添加元素(保持原始顺序)
strList.add("aaa");
strList.add("bbb");
strList.add("aaa");
strList.add("aaa");
strList.add("ccc");
strList.add("bbb");
// 3. 索引遍历
for(int i=0; i<strList.size(); i++) {
System.out.printf("索引%d:%s%n", i, strList.get(i));
}
}
}
泛型声明:
<String>限定集合元素类型动态扩容机制:
java复制// ArrayList扩容核心逻辑(JDK源码节选)
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
遍历方式对比:
| 遍历方式 | 语法 | 适用场景 | 性能 |
|---|---|---|---|
| 索引循环 | for(int i=0; i<list.size(); i++) |
需要索引操作 | O(1) |
| 增强for | for(String s : list) |
简单遍历 | O(1) |
| 迭代器 | Iterator<String> it = list.iterator() |
需要删除元素 | O(1) |
NullPointerException:
new ArrayList<>()IndexOutOfBoundsException:
i <= list.size()i < list.size()类型转换异常:
ArrayList list = new ArrayList(); String s = (String)list.get(0);提示:调试时可调用
System.out.println(list)直接输出完整集合内容
java复制public class Teacher {
// 1. 私有属性(封装)
private String name;
private String major;
// 2. 构造方法
public Teacher(String name, String major) {
this.name = name;
this.major = major;
}
// 3. Getter/Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMajor() { return major; }
public void setMajor(String major) { this.major = major; }
// 4. 可选toString()
@Override
public String toString() {
return String.format("姓名:%s,专业:%s", name, major);
}
}
java复制public class TeacherManagement {
public static void main(String[] args) {
// 1. 初始化集合
ArrayList<Teacher> teachers = new ArrayList<>();
// 2. 添加教师对象
teachers.add(new Teacher("赵老师", "javase"));
teachers.add(new Teacher("钱老师", "javaee"));
teachers.add(new Teacher("孙老师", "php"));
teachers.add(new Teacher("李老师", "python"));
// 3. 遍历输出
for(Teacher t : teachers) {
System.out.printf("姓名:%s,专业:%s%n",
t.getName(), t.getMajor());
}
// 4. 使用Stream API(Java8+)
teachers.stream()
.forEach(t -> System.out.println(t));
}
}
code复制堆内存结构示例:
ArrayList对象
│
├── elementData: Object[10]
│ ├── [0]: Teacher@1001 (name="赵老师", major="javase")
│ ├── [1]: Teacher@1002 (name="钱老师", major="javaee")
│ ├── ...
│ └── [9]: null
├── size: 4
└── modCount: 4
关键特性:
java复制public class EmployeeSystem {
public static void main(String[] args) {
// 初始化员工数据
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("张三", 3000));
employees.add(new Employee("李四", 3500));
employees.add(new Employee("王五", 4000));
employees.add(new Employee("赵六", 4500));
employees.add(new Employee("田七", 5000));
// 1. 修改操作(王五→王小五)
for(int i=0; i<employees.size(); i++) {
Employee emp = employees.get(i);
if("王五".equals(emp.getName())) {
emp.setName("王小五");
// employees.set(i, emp); // 非必须,对象引用未变
}
}
// 2. 删除操作(赵六)
Iterator<Employee> it = employees.iterator();
while(it.hasNext()) {
if("赵六".equals(it.next().getName())) {
it.remove(); // 安全删除
}
}
// 3. 条件更新(田七加薪)
employees.stream()
.filter(e -> "田七".equals(e.getName()))
.forEach(e -> e.setSalary(e.getSalary() + 500));
}
}
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| add(E e) | O(1) | 尾部添加最快 |
| add(int index, E e) | O(n) | 需要移动元素 |
| get(int index) | O(1) | 随机访问快 |
| remove(int index) | O(n) | 需要移动元素 |
| contains(Object o) | O(n) | 需要遍历查找 |
问题复现:
java复制// 错误示例:增强for循环中删除
for(Employee emp : employees) {
if(emp.getName().equals("赵六")) {
employees.remove(emp); // 抛出ConcurrentModificationException
}
}
解决方案:
推荐写法:
java复制// 方法1:迭代器删除
Iterator<Employee> it = employees.iterator();
while(it.hasNext()) {
if(it.next().getName().equals("赵六")) {
it.remove();
}
}
// 方法2:Stream过滤(Java8+)
List<Employee> filtered = employees.stream()
.filter(e -> !e.getName().equals("赵六"))
.collect(Collectors.toList());
初始化容量:
java复制// 已知元素数量时指定初始容量
ArrayList<String> list = new ArrayList<>(1000);
批量操作:
java复制// 使用addAll替代循环add
list.addAll(Arrays.asList("a", "b", "c"));
遍历选择:
值对象模式:
防御性拷贝:
java复制// 返回集合的不可变视图
public List<Employee> getEmployees() {
return Collections.unmodifiableList(employees);
}
浅拷贝问题:
java复制// 错误示例:clone()方法只做浅拷贝
ArrayList<Employee> copy = (ArrayList<Employee>) employees.clone();
// 正确做法:深拷贝
ArrayList<Employee> deepCopy = new ArrayList<>();
employees.forEach(e -> deepCopy.add(new Employee(e)));
equals实现规范:
java复制@Override
public boolean equals(Object o) {
if(this == o) return true;
if(!(o instanceof Employee)) return false;
Employee e = (Employee) o;
return Objects.equals(name, e.name) &&
Double.compare(salary, e.salary) == 0;
}
多线程安全方案:
java复制// 方案1:使用Collections工具类
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 方案2:使用CopyOnWriteArrayList
List<String> cowList = new CopyOnWriteArrayList<>();
在实际项目开发中,建议根据业务场景选择最适合的集合类型。对于读多写少的场景,可以考虑使用CopyOnWriteArrayList;对于需要频繁随机访问的场景,ArrayList仍然是首选。