核心概念
-
数组
(图片来源网络,侵删)- 是什么:数组是 Java 中的一种数据结构,用于存储固定大小的相同类型的元素。
- 本质:它在内存中分配一块连续的空间,一旦创建,其大小就不能改变。
- 声明方式:
数据类型[] 数组名 = new 数据类型[大小];或数据类型[] 数组名 = {元素1, 元素2, ...};
-
ArrayList
- 是什么:
ArrayList是 Java 集合框架 (java.util) 中的一个类,它实现了List接口,它内部也是使用一个数组来存储数据,但提供了动态扩容的能力。 - 本质:它是一个对象,可以看作是对数组的封装,当元素数量超过其内部数组的容量时,它会自动创建一个更大的新数组,并将旧数组的内容复制过去。
- 声明方式:
ArrayList<数据类型> 列表名 = new ArrayList<数据类型>();(推荐使用泛型)
- 是什么:
详细对比表格
| 特性 | 数组 | ArrayList |
|---|---|---|
| 大小 | 固定,创建时必须指定大小,之后无法改变。 | 动态,可以随时添加或删除元素,大小会自动调整。 |
| 性能 | 访问 (get/set): 极快,基于索引,直接通过内存地址计算,时间复杂度为 O(1)。 增删 (add/remove): 慢,在中间或开头增删需要移动大量元素,时间复杂度为 O(n)。 |
访问 (get/set): 快,同样基于索引,时间复杂度为 O(1)。 增删 (add/remove): 中间/开头: 慢,需要移动元素,O(n)。 末尾: 平均很快,如果容量足够,直接添加,O(1),如果需要扩容,则涉及复制数组,成本较高,但分摊后仍是 O(1)。 |
| 数据类型 | 可以存储基本数据类型 (如 int, char) 和对象引用。 |
只能存储对象引用,如果要存储基本数据类型,必须使用其包装类 (如 Integer, Character)。 |
| 功能/方法 | 功能非常有限,只有 length 属性和一些 java.lang.reflect 中的方法。 |
功能非常丰富,提供了大量方法,如 add(), remove(), size(), isEmpty(), contains(), iterator() 等。 |
| 泛型支持 | 支持,但类型擦除后数组在运行时仍保留类型信息。 | 完全支持泛型,是类型安全的,可以避免在运行时出现 ClassCastException。 |
| 内存分配 | 在堆上分配一块连续的内存空间。 | 对象本身在堆上,其内部维护的数组也是连续的,但整体不保证与其他对象内存连续。 |
| 代码可读性 | 简单直接。 | 方法调用更符合面向对象思想,代码更清晰、易读。 |
| 继承关系 | 是 Java 中的第一类对象,但 Object 类中没有 add() 或 remove() 这类方法。 |
继承自 AbstractList,实现了 List, RandomAccess, Cloneable, Serializable 等接口,这意味着它支持随机访问、克隆和序列化。 |
代码示例对比
声明和初始化
数组
// 声明并初始化一个大小为 3 的 String 数组
String[] names = new String[3];
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";
// 声明并初始化一个 int 数组
int[] scores = {90, 85, 88};
ArrayList
import java.util.ArrayList;
// 声明并初始化一个 ArrayList,用于存储 String 对象
ArrayList<String> nameList = new ArrayList<>();
nameList.add("Alice");
nameList.add("Bob");
nameList.add("Charlie");
// 初始化时指定初始容量(可选)
ArrayList<Integer> scoreList = new ArrayList<>(10); // 初始容量为 10
添加和删除元素
数组

// 数组大小固定,无法直接添加或删除元素。 // 模拟添加:必须创建一个新数组,复制旧数组内容,再将新元素放入。 String[] tempNames = new String[names.length + 1]; System.arraycopy(names, 0, tempNames, 0, names.length); tempNames[names.length] = "David"; names = tempNames; // 引用指向新数组 // 模拟删除:同样需要创建一个新数组,并跳过要删除的元素。 String[] newNames = new String[names.length - 1]; int index = 1; // 假设要删除 "Bob" System.arraycopy(names, 0, newNames, 0, index); System.arraycopy(names, index + 1, newNames, index, names.length - index - 1); names = newNames;
ArrayList
// 添加元素到末尾
nameList.add("David"); // 非常简单
// 在指定位置插入元素
nameList.add(1, "Eve"); // 在索引 1 的位置插入 "Eve"
// 删除指定元素
nameList.remove("Alice"); // 删除第一个值为 "Alice" 的元素
// 删除指定位置的元素
nameList.remove(0); // 删除索引为 0 的元素
获取大小和遍历
数组
// 获取大小
int size = names.length;
// 遍历
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
// 增强 for 循环 (推荐)
for (String name : names) {
System.out.println(name);
}
ArrayList
// 获取大小
int size = nameList.size();
// 遍历
for (int i = 0; i < nameList.size(); i++) {
System.out.println(nameList.get(i));
}
// 增强for循环 (推荐)
for (String name : nameList) {
System.out.println(name);
}
// 使用迭代器 (更安全,尤其在遍历时需要删除元素时)
Iterator<String> iterator = nameList.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
// ...
}
如何选择?何时使用?
这是一个关键问题,选择合适的数据结构能让你的代码更高效、更健壮。

使用数组 的场景:
- 数据大小固定且已知:当你明确知道要存储多少个元素时,数组是最佳选择,一年的12个月、一周的7天。
- 追求极致的性能:在性能要求极高的场景下(如游戏开发、高频交易),并且操作主要是基于索引的访问,数组的
O(1)访问速度优势明显。 - 存储基本数据类型:如果需要存储大量基本数据类型(如
int,double),使用数组可以避免ArrayList自动装箱带来的性能开销和内存占用。 - 作为其他数据结构或API的参数:很多Java底层API(如
main方法的参数、System.arraycopy)直接使用数组作为参数。
示例:
// 保存一个月的天数,固定为31天 int[] daysInMonth = new int[31]; // 假设不考虑不同月份 // 保存图像的像素数据,大小固定 int[] pixels = new int[width * height];
使用 ArrayList 的场景:
- 数据数量不确定或需要频繁变化:这是
ArrayList最核心的应用场景,从数据库查询用户列表、读取文件中的所有行、用户动态添加购物车商品等。 - 需要丰富的操作方法:当你需要频繁地进行增、删、查、判断是否包含等操作时,
ArrayList提供的丰富方法能让代码更简洁、可读性更高。 - 需要使用集合框架的其他功能:如果你需要将列表进行排序 (
Collections.sort())、打乱 (Collections.shuffle()) 或者使用for-each循环,ArrayList是List的标准实现,能无缝集成。 - 绝大多数日常业务开发:在99%的日常应用开发中,当你需要一个“列表”时,首选
ArrayList,它的灵活性和易用性远超数组。
示例:
// 保存用户的动态评论列表,数量不确定
ArrayList<String> comments = new ArrayList<>();
// ... 从数据库或用户输入中不断添加评论
comments.add("第一个评论");
comments.add("第二个评论");
// ...
| 数组 | ArrayList | |
|---|---|---|
| 核心优势 | 固定大小、高性能随机访问 | 动态大小、功能丰富、使用方便 |
| 核心劣势 | 大小固定、功能单一 | 只能存对象、增删非末尾元素慢 |
| 一句话总结 | 当你知道确切数量且追求速度时用数组。 | 当数量不确定或需要频繁增删时用ArrayList。 |
记住这个简单的原则:优先考虑 ArrayList。 只有在 ArrayList 无法满足你的特定需求时(如性能瓶颈、固定大小、存储基本类型),才回退到使用数组。
