数据结构-ArrayList
在 Java 集合框架中,ArrayList 是 List 接口最常用的实现类之一,因其查询效率高、使用灵活的特点,广泛应用于日常开发。本文将从 ArrayList 的核心特点、常用方法、遍历方式到使用注意事项,进行系统性梳理,帮助快速复习并掌握其核心用法。
一、ArrayList 核心特点
ArrayList 本质是动态数组(底层基于数组实现),相比普通数组,它能自动扩容以存储更多元素,同时保留了数组 “按索引访问快” 的优势,具体特点如下:
- 存储结构:底层基于数组,元素在内存中连续存储,通过索引(
index
,从 0 开始)快速定位元素。 - 查询高效:获取指定索引元素(
get(int index)
)的时间复杂度为 O(1),适合频繁查询的场景。 - 增删低效:
- 尾部添加元素(
add(Object e)
)效率较高(无扩容时为 O (1)); - 中间 / 头部增删元素(
add(int index, E e)
、remove(int index)
)需移动后续元素,时间复杂度为 O(n),不适合频繁增删的场景。
- 尾部添加元素(
- 线程不安全:非同步设计,多线程环境下直接使用可能出现数据安全问题(如需线程安全,可使用
Collections.synchronizedList(new ArrayList<>())
或替代类CopyOnWriteArrayList
)。 - 支持泛型:可通过泛型(如
<String>
、<Integer>
)限定存储元素的类型,避免类型转换异常,提升代码安全性。 - 自动扩容:初始容量默认 10,当元素数量超过当前容量时,会自动扩容为原来的 1.5 倍(通过
Arrays.copyOf()
复制原数组实现)。
二、ArrayList 常用方法
ArrayList 提供了丰富的方法用于操作元素,以下是开发中最常用的方法,均附完整示例代码,直接复制即可运行。
1. 基础操作:添加、获取、删除
(1)添加元素
add(E e)
:向集合尾部添加元素;add(int index, E e)
:向集合指定索引位置添加元素(原位置及后续元素后移)。
import java.util.ArrayList;public class ArrayListAddDemo {public static void main(String[] args) {// 泛型限定:集合仅存储 String 类型元素ArrayList<String> list = new ArrayList<>();// 1. 尾部添加元素list.add("Java");list.add("Python");System.out.println("尾部添加后:" + list); // 输出:[Java, Python]// 2. 指定索引添加(索引 1)list.add(1, "Golang");System.out.println("指定索引添加后:" + list); // 输出:[Java, Golang, Python]}
}
(2)获取元素与集合大小
get(int index)
:获取指定索引的元素(索引从 0 开始,越界会抛IndexOutOfBoundsException
);size()
:返回集合中元素的实际个数(区别于数组的 “容量”)。
public class ArrayListGetSizeDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("北京");list.add("上海");list.add("广州");// 获取索引 1 的元素String city = list.get(1);System.out.println("索引 1 的元素:" + city); // 输出:上海// 获取集合大小int size = list.size();System.out.println("集合元素个数:" + size); // 输出:3}
}
(3)删除元素
remove(int index)
:删除指定索引的元素,返回被删除的元素,后续元素前移;remove(Object o)
:删除集合中第一次出现的指定元素,返回boolean
(true
表示删除成功,false
表示元素不存在)。
public class ArrayListRemoveDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("苹果");list.add("香蕉");list.add("香蕉");list.add("橙子");// 1. 按索引删除(删除索引 1 的元素)String removedByIndex = list.remove(1);System.out.println("按索引删除的元素:" + removedByIndex); // 输出:香蕉System.out.println("删除后集合:" + list); // 输出:[苹果, 香蕉, 橙子]// 2. 按元素删除(删除第一次出现的“香蕉”)boolean isRemoved = list.remove("香蕉");System.out.println("是否删除成功:" + isRemoved); // 输出:trueSystem.out.println("最终集合:" + list); // 输出:[苹果, 橙子]}
}
2. 进阶操作:修改、判断、清空
(1)修改元素
set(int index, E e)
:用新元素替换指定索引的旧元素,返回被替换的旧元素。
public class ArrayListSetDemo {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(10);list.add(20);list.add(30);// 替换索引 1 的元素(20 → 25)Integer oldVal = list.set(1, 25);System.out.println("被替换的旧值:" + oldVal); // 输出:20System.out.println("修改后集合:" + list); // 输出:[10, 25, 30]}
}
(2)判断元素与集合空否
contains(Object o)
:判断集合是否包含指定元素,返回boolean
;isEmpty()
:判断集合是否为空(元素个数为 0),返回boolean
。
public class ArrayListJudgeDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("猫");list.add("狗");// 1. 判断是否包含“狗”boolean hasDog = list.contains("狗");System.out.println("是否包含狗:" + hasDog); // 输出:true// 2. 判断集合是否为空boolean isEmpty = list.isEmpty();System.out.println("集合是否为空:" + isEmpty); // 输出:false// 清空集合后再判断list.clear();System.out.println("清空后集合是否为空:" + list.isEmpty()); // 输出:true}
}
(3)清空集合
clear()
:删除集合中所有元素,集合变为空(但对象本身仍存在)。
public class ArrayListClearDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("A");list.add("B");System.out.println("清空前:" + list); // 输出:[A, B]// 清空集合list.clear();System.out.println("清空后:" + list); // 输出:[]System.out.println("清空后大小:" + list.size()); // 输出:0}
}
3. ArrayList 三种遍历方式
遍历是集合的核心操作,ArrayList 支持三种常用遍历方式,需根据场景选择:
(1)普通 for 循环(适合需索引的场景)
通过 size()
获取长度,get(int index)
获取指定索引元素,可手动控制遍历顺序。
public class ArrayListForDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("周一");list.add("周二");list.add("周三");// 普通 for 循环遍历for (int i = 0; i < list.size(); i++) {System.out.println("索引 " + i + ":" + list.get(i));}// 输出:// 索引 0:周一// 索引 1:周二// 索引 2:周三}
}
(2)增强 for 循环(foreach,简洁高效)
无需关注索引,直接遍历每个元素,代码简洁,是日常开发的首选。
public class ArrayListForEachDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Spring");list.add("MyBatis");list.add("SpringBoot");// 增强 for 循环遍历for (String framework : list) {System.out.println("框架:" + framework);}// 输出:// 框架:Spring// 框架:MyBatis// 框架:SpringBoot}
}
(3)迭代器(Iterator,支持遍历中删除元素)
通过 iterator()
获取迭代器,用 hasNext()
判断是否有下一个元素,next()
获取元素。注意:遍历中删除元素需用 iterator.remove()
,而非 list.remove()
,否则会抛 ConcurrentModificationException
。
import java.util.ArrayList;
import java.util.Iterator;public class ArrayListIteratorDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("红色");list.add("绿色");list.add("蓝色");// 迭代器遍历Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String color = iterator.next();// 遍历中删除“绿色”if ("绿色".equals(color)) {iterator.remove(); // 必须用迭代器的 remove() 方法}System.out.println("颜色:" + color);}System.out.println("删除后集合:" + list); // 输出:[红色, 蓝色]}
}
三、ArrayList 使用注意事项
- 索引越界问题:
get(int index)
、remove(int index)
等方法的索引需在[0, size()-1]
范围内,否则会抛IndexOutOfBoundsException
,使用前需确认索引合法性。 - 泛型与空元素:
- 泛型仅支持引用类型(如
Integer
,而非int
); - ArrayList 允许存储
null
元素(但不建议,可能导致后续equals()
判断空指针)。
- 泛型仅支持引用类型(如
- 线程安全问题:ArrayList 非线程安全,多线程同时读写时,需通过
Collections.synchronizedList()
包装,或使用线程安全的CopyOnWriteArrayList
。 - 扩容性能优化:若已知集合元素数量,创建 ArrayList 时可指定初始容量(如
new ArrayList<>(100)
),减少自动扩容次数(扩容需复制数组,消耗性能)。 - 与 LinkedList 的区别:ArrayList 适合查询多、增删少的场景;LinkedList(链表实现)适合增删多、查询少的场景,需根据业务选择。
四、总结
ArrayList 作为 Java 中最常用的集合类,核心是 “动态数组” 的实现,优势在于快速查询,劣势在于中间增删效率低。掌握其核心方法(添加、获取、删除、修改)和三种遍历方式,结合使用注意事项(索引、线程安全、扩容优化),就能在开发中灵活应用。