- 核心概念与定义
- 主要区别对比表
- 详细特性分析
- 代码示例
- 如何选择:何时用数组,何时用
ArrayList?
核心概念与定义
数组
- 定义:数组是一个固定长度的、用来存储相同类型数据的数据结构。
- 特点:
- 长度固定:一旦创建,其大小就不能改变。
- 类型统一:只能存储一种数据类型(
int[]只能存int,String[]只能存String)。 - 底层连续:在内存中是连续存储的,这使得通过索引访问元素非常快(时间复杂度为 O(1))。
- 性能高:由于内存连续,访问速度极快,也基本没有额外的方法调用开销。
ArrayList
- 定义:
ArrayList是 Java 集合框架 (java.util.ArrayList) 中的一个类,它实现了List接口,它本质上是一个动态数组。 - 特点:
- 长度可变:可以根据需要动态地增加或减少元素的大小。
- 类型安全(泛型):通过泛型(
ArrayList<String>)可以确保只能存储指定类型的数据,避免了类型转换的错误。 - 底层是数组:
ArrayList的内部实现就是一个数组,当元素数量超过当前数组容量时,它会创建一个更大的新数组(通常是原容量的 1.5 倍),然后将旧数组的元素复制到新数组中,这个过程被称为“扩容”(Resizing)。 - 提供丰富方法:封装了大量便捷的方法,如
add(),remove(),size(),get()等,使用起来非常方便。
主要区别对比表
| 特性 | 数组 | ArrayList |
|---|---|---|
| 长度 | 固定,创建后大小不可变。 | 可变,可以动态添加或删除元素。 |
| 类型 | 可以存储基本类型(int, char 等)和对象。 |
只能存储对象,如果要存基本类型,必须使用其包装类(如 Integer, Character)。 |
| 功能/方法 | 功能非常有限,只有 length 属性和一些 java.lang.reflect 方法。 |
功能非常丰富,提供了 add(), remove(), get(), size(), contains() 等大量方法。 |
| 性能 | 访问(读/写)速度快,直接通过索引计算内存地址。 | 访问速度也很快,但比原生数组稍慢,因为有方法调用开销。添加/删除元素在中间位置较慢,因为可能需要移动大量元素。 |
| 声明与初始化 | int[] arr = new int[10];String[] names = {"A", "B"}; |
ArrayList<String> list = new ArrayList<>();list.add("A"); list.add("B"); |
| 可以直接存基本类型和对象。 | 只能存对象,存基本类型时用自动装箱。 | |
| 多维 | 支持多维数组,如 int[][] matrix。 |
不直接支持“真正的”多维数组,但可以通过 ArrayList<ArrayList<Integer>> 来模拟。 |
| 父类/接口 | 是 Java 的第一类对象,有 Object 作为隐式父类。 |
实现 List 接口,继承自 AbstractList。 |
详细特性分析
数组的详细分析
优点:

(图片来源网络,侵删)
- 性能极致:对于已知大小的数据集,数组是最高效的选择,内存访问是直接的,没有额外的对象和方法调用。
- 内存紧凑:在内存中是连续的,没有额外的指针或对象头开销。
缺点:
- 长度固定:这是最大的缺点,如果不知道要存多少数据,数组的大小很难确定,定义太大浪费内存,定义太小又会导致程序出错(
ArrayIndexOutOfBoundsException)。 - 功能匮乏:没有内置的方法来排序、搜索、添加或删除元素,所有这些操作都需要自己手动实现,非常繁琐。
ArrayList 的详细分析
优点:
- 动态扩容:解决了数组长度固定的问题,使用起来非常灵活。
- 功能强大:提供了丰富的 API,极大简化了开发。
list.remove(0)可以轻松移除第一个元素,而在数组中这需要手动移动所有后续元素。 - 代码可读性高:
list.add("item")比arr[index++] = "item"更直观。
缺点:
- 性能开销:
- 扩容成本:当
ArrayList频繁添加元素导致多次扩容时,复制数组会带来一定的性能开销。 - 中间插入/删除慢:由于底层数组的连续性,在中间位置插入或删除一个元素,平均需要移动一半的元素,时间复杂度为 O(n)。
- 扩容成本:当
- 空间开销:为了支持动态扩容,
ArrayList内部通常会预留一部分容量(capacity),这可能导致比数组占用更多的内存。 - 不能存基本类型:虽然可以通过自动装箱(
int->Integer)解决,但会带来额外的对象创建和内存开销,并且可能产生NullPointerException。
代码示例
数组示例
public class ArrayExample {
public static void main(String[] args) {
// 1. 声明并初始化一个固定长度的整型数组
int[] numbers = new int[5]; // 长度为 5
// 2. 赋值
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;
// 3. 访问元素
System.out.println("第一个元素是: " + numbers[0]); // 输出: 第一个元素是: 10
// 4. 获取数组长度
System.out.println("数组长度是: " + numbers.length); // 输出: 数组长度是: 5
// 5. 遍历数组
System.out.println("遍历数组:");
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " "); // 输出: 10 20 30 40 50
}
System.out.println();
// 6. 数组的缺点:长度固定,尝试添加第6个元素会抛出异常
// numbers[5] = 60; // 编译不通过,数组索引越界
}
}
ArrayList 示例
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
// 1. 声明并初始化一个 ArrayList (只能存储 String 对象)
List<String> names = new ArrayList<>(); // 使用接口 List 声明是更好的编程习惯
// 2. 添加元素 (长度可变)
names.add("Alice");
names.add("Bob");
names.add("Charlie");
System.out.println("初始列表: " + names); // 输出: [Alice, Bob, Charlie]
// 3. 在指定位置添加元素
names.add(1, "David");
System.out.println("在索引1处添加David后: " + names); // 输出: [Alice, David, Bob, Charlie]
// 4. 获取元素
System.out.println("索引为1的元素是: " + names.get(1)); // 输出: David
// 5. 获取列表大小 (注意不是 length)
System.out.println("列表大小是: " + names.size()); // 输出: 4
// 6. 移除元素
names.remove("Alice"); // 移除第一个匹配的元素
System.out.println("移除Alice后: " + names); // 输出: [David, Bob, Charlie]
names.remove(0); // 移除指定索引的元素
System.out.println("移除索引0后: " + names); // 输出: [Bob, Charlie]
// 7. 检查元素是否存在
System.out.println("列表中是否包含 'Bob'? " + names.contains("Bob")); // 输出: true
// 8. 遍历 ArrayList (多种方式)
System.out.println("使用 for-each 遍历:");
for (String name : names) {
System.out.print(name + " "); // 输出: Bob Charlie
}
System.out.println();
}
}
如何选择:何时用数组,何时用 ArrayList?
这是一个非常常见且重要的问题,遵循以下原则可以做出正确的选择:

(图片来源网络,侵删)
优先选择 ArrayList 的情况:
- 不确定数据量大小:如果你不知道最终需要存储多少个元素,或者元素数量会动态变化,
ArrayList是不二之选。 - 需要频繁的增删操作:特别是对列表头或中间进行增删,
ArrayList提供了简洁的 API,而数组操作非常复杂。 - 需要丰富的集合操作:如果你需要排序、查找、清空列表、检查元素是否存在等,直接使用
ArrayList的方法即可,无需自己实现。 - 代码简洁性和可读性优先:
ArrayList的方法调用(如add,remove)比手动操作数组索引更直观、更不易出错。
在 90% 的日常开发场景中,都应该优先使用 ArrayList。
选择数组的情况:
- 性能是绝对优先级:在处理大规模数据(如游戏引擎、科学计算、高频交易)时,对内存访问速度要求极致,数组的直接内存访问是
ArrayList无法比拟的。 - 数据量固定且已知:如果你明确知道数据量不会改变(一年中的12个月,一周中的7天),使用数组更高效,因为它没有
ArrayList的扩容和对象开销。 - 作为
ArrayList或其他集合的底层实现:ArrayList本身就是用数组实现的,当你需要自己实现高性能数据结构时,数组是基础。 - 与 JNI 或其他本地代码交互:在调用本地 C/C++ 代码时,通常需要传递原始的数组指针,Java 数组可以方便地做到这一点。
只有在性能极其敏感、数据量固定且明确知道大小的场景下,才考虑使用原生数组。
希望这个详细的对比能帮助你彻底理解 Java 数组和 ArrayList 的区别与用法!

(图片来源网络,侵删)
