杰瑞科技汇

数组与ArrayList有何本质区别?

核心概念

  1. 数组

    数组与ArrayList有何本质区别?-图1
    (图片来源网络,侵删)
    • 是什么:数组是 Java 中的一种数据结构,用于存储固定大小相同类型的元素。
    • 本质:它在内存中分配一块连续的空间,一旦创建,其大小就不能改变。
    • 声明方式数据类型[] 数组名 = new 数据类型[大小];数据类型[] 数组名 = {元素1, 元素2, ...};
  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

添加和删除元素

数组

数组与ArrayList有何本质区别?-图2
(图片来源网络,侵删)
// 数组大小固定,无法直接添加或删除元素。
// 模拟添加:必须创建一个新数组,复制旧数组内容,再将新元素放入。
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();
    // ...
}

如何选择?何时使用?

这是一个关键问题,选择合适的数据结构能让你的代码更高效、更健壮。

数组与ArrayList有何本质区别?-图3
(图片来源网络,侵删)

使用数组 的场景:

  1. 数据大小固定且已知:当你明确知道要存储多少个元素时,数组是最佳选择,一年的12个月、一周的7天。
  2. 追求极致的性能:在性能要求极高的场景下(如游戏开发、高频交易),并且操作主要是基于索引的访问,数组的 O(1) 访问速度优势明显。
  3. 存储基本数据类型:如果需要存储大量基本数据类型(如 int, double),使用数组可以避免 ArrayList 自动装箱带来的性能开销和内存占用。
  4. 作为其他数据结构或API的参数:很多Java底层API(如 main 方法的参数、System.arraycopy)直接使用数组作为参数。

示例:

// 保存一个月的天数,固定为31天
int[] daysInMonth = new int[31]; // 假设不考虑不同月份
// 保存图像的像素数据,大小固定
int[] pixels = new int[width * height];

使用 ArrayList 的场景:

  1. 数据数量不确定或需要频繁变化:这是 ArrayList 最核心的应用场景,从数据库查询用户列表、读取文件中的所有行、用户动态添加购物车商品等。
  2. 需要丰富的操作方法:当你需要频繁地进行增、删、查、判断是否包含等操作时,ArrayList 提供的丰富方法能让代码更简洁、可读性更高。
  3. 需要使用集合框架的其他功能:如果你需要将列表进行排序 (Collections.sort())、打乱 (Collections.shuffle()) 或者使用 for-each 循环,ArrayListList 的标准实现,能无缝集成。
  4. 绝大多数日常业务开发:在99%的日常应用开发中,当你需要一个“列表”时,首选 ArrayList,它的灵活性和易用性远超数组。

示例:

// 保存用户的动态评论列表,数量不确定
ArrayList<String> comments = new ArrayList<>();
// ... 从数据库或用户输入中不断添加评论
comments.add("第一个评论");
comments.add("第二个评论");
// ...

数组 ArrayList
核心优势 固定大小、高性能随机访问 动态大小、功能丰富、使用方便
核心劣势 大小固定、功能单一 只能存对象、增删非末尾元素慢
一句话总结 当你知道确切数量且追求速度时用数组。 当数量不确定或需要频繁增删时用ArrayList。

记住这个简单的原则:优先考虑 ArrayList 只有在 ArrayList 无法满足你的特定需求时(如性能瓶颈、固定大小、存储基本类型),才回退到使用数组。

分享:
扫描分享到社交APP
上一篇
下一篇