杰瑞科技汇

Java数组和ArrayList到底该用哪个?

  1. 核心概念与定义
  2. 主要区别对比 (一张图看懂)
  3. 详细特性分析
  4. 代码示例
  5. 如何选择:何时用数组,何时用 ArrayList

核心概念与定义

数组

数组是 Java 中一种最基础的数据结构,它是一个固定长度的、用来存储同一种类型数据的容器。

Java数组和ArrayList到底该用哪个?-图1
(图片来源网络,侵删)
  • 特点
    • 长度固定:一旦创建,其大小就不能再改变。
    • 类型固定:只能存储声明时指定的数据类型(包括其子类)。
    • 底层连续内存:所有元素在内存中是连续存储的,这使得通过索引访问元素非常快(时间复杂度 O(1))。

ArrayList

ArrayList 是 Java 集合框架 (java.util.ArrayList) 中最常用的一个类,它内部是基于动态数组实现的,可以看作是一个长度可变的数组。

  • 特点
    • 长度可变:可以根据需要动态地增加或减少元素。
    • 只能存储对象ArrayList 只能存储对象类型,不能存储基本数据类型(如 int, char),如果要存储基本数据类型,需要使用它们的包装类(如 Integer, Character)。
    • 底层是数组:虽然它的大小可变,但其内部实现仍然依赖于一个数组,当元素数量超过当前数组容量时,它会创建一个更大的新数组,并将旧数组的元素复制过去,这是一个相对耗时的操作(摊销时间复杂度 O(1))。

主要区别对比 (一张图看懂)

特性 数组 ArrayList
长度 固定,创建时必须指定 可变,可以动态增长和缩小
数据类型 可以是基本数据类型 (如 int[]),也可以是对象 只能存储对象类型,基本类型需用包装类 (如 ArrayList<Integer>)
性能 访问 (get/set) 极快,增删慢(需移动元素)。 访问 (get/set) 很快,增删在末尾时快,在中间/开头时慢(同数组),扩容时较慢。
功能/方法 功能非常有限,只有 length 属性和一些基本操作 功能非常丰富,提供 add(), remove(), size(), get(), sort() 等大量便捷方法
声明和初始化 int[] arr = new int[5];int[] arr = {1, 2, 3}; ArrayList<Integer> list = new ArrayList<>();
泛型支持 不支持泛型,类型在编译时通过检查 支持泛型,提供编译时类型安全检查

详细特性分析

数组

  • 优点
    • 内存占用小:没有额外的对象开销(ArrayList 自身的一些属性)。
    • 访问速度快:由于内存连续,可以通过索引直接计算出内存地址,访问元素是 O(1) 操作。
  • 缺点
    • 长度固定:如果不知道需要多少元素,很难确定一个合适的长度,太浪费空间,太小又不够用。
    • 功能单一:没有内置的方法来方便地排序、搜索、添加或删除元素,所有操作都需要自己手动实现,非常繁琐。

ArrayList

  • 优点
    • 长度灵活:可以动态添加或删除元素,非常方便,无需关心底层数组的大小。
    • 功能强大:提供了大量标准库方法,如 add(), remove(), contains(), sort() 等,极大地提高了开发效率。
    • 支持泛型:可以在编译时检查类型,避免了 ClassCastException,代码更安全、更清晰。
  • 缺点
    • 性能开销:由于是对象,每个元素都会有一个额外的引用开销,在频繁扩容时,会有性能损耗(复制旧数组到新数组)。
    • 不能存基本类型:必须使用包装类,而包装类是对象,会涉及到自动装箱/拆箱,会有一定的性能开销。

代码示例

数组操作

public class ArrayExample {
    public static void main(String[] args) {
        // 1. 声明和初始化
        int[] numbers = new int[3]; // 创建一个长度为3的整型数组
        numbers[0] = 10;
        numbers[1] = 20;
        numbers[2] = 30;
        // 也可以这样初始化
        String[] names = {"Alice", "Bob", "Charlie"};
        // 2. 获取长度
        System.out.println("Numbers array length: " + numbers.length); // 输出 3
        // 3. 访问元素
        System.out.println("First number: " + numbers[0]); // 输出 10
        // 4. 遍历数组
        System.out.println("--- Traversing names array ---");
        for (int i = 0; i < names.length; i++) {
            System.out.println(names[i]);
        }
        // 5. 数组的缺点:长度固定,无法直接添加元素
        // numbers[3] = 40; // 这行代码会抛出 ArrayIndexOutOfBoundsException 异常
    }
}

ArrayList 操作

import java.util.ArrayList;
import java.util.Collections;
public class ArrayListExample {
    public static void main(String[] args) {
        // 1. 声明和初始化 (注意:必须使用包装类 Integer)
        ArrayList<Integer> numberList = new ArrayList<>();
        // 2. 添加元素 (长度可变)
        numberList.add(10);
        numberList.add(20);
        numberList.add(30);
        System.out.println("Initial list: " + numberList); // 输出 [10, 20, 30]
        // 3. 获取大小
        System.out.println("List size: " + numberList.size()); // 输出 3
        // 4. 在指定位置插入元素
        numberList.add(1, 15); // 在索引1的位置插入15
        System.out.println("After inserting 15: " + numberList); // 输出 [10, 15, 20, 30]
        // 5. 访问元素
        System.out.println("Element at index 2: " + numberList.get(2)); // 输出 20
        // 6. 删除元素
        numberList.remove(0); // 删除索引为0的元素 (10)
        System.out.println("After removing element at index 0: " + numberList); // 输出 [15, 20, 30]
        numberList.remove(Integer.valueOf(20)); // 删除值为20的元素
        System.out.println("After removing value 20: " + numberList); // 输出 [15, 30]
        // 7. 遍历 ArrayList (多种方式)
        System.out.println("--- Traversing with for-each loop ---");
        for (Integer num : numberList) {
            System.out.println(num);
        }
        // 8. 使用 Collections 工具类进行排序
        numberList.add(5);
        numberList.add(25);
        System.out.println("Before sorting: " + numberList); // 输出 [15, 30, 5, 25]
        Collections.sort(numberList);
        System.out.println("After sorting: " + numberList);    // 输出 [5, 15, 25, 30]
    }
}

如何选择:何时用数组,何时用 ArrayList

这是一个非常常见的面试问题,答案取决于你的具体需求。

优先选择 ArrayList 的情况 (90% 的情况):

  • 当你不知道需要存储多少个元素时:这是 ArrayList 最核心的优势。
  • 当你需要频繁地添加或删除元素时ArrayList 提供了非常方便的 API。
  • 当你需要使用丰富的集合操作时:如排序、查找、清空等。
  • 当你需要代码的简洁性和可读性时ArrayList 的泛型和丰富方法让代码更现代化、更易维护。

在绝大多数业务开发场景下,默认选择 ArrayList,它更灵活、更安全、更方便。

优先选择数组 的情况 (10% 的情况):

  • 当你确定数据量大小且固定不变时:表示一周的七天、一年的十二个月,数组更节省内存。
  • 在追求极致性能的高频计算场景中:在游戏引擎、科学计算或图像处理中,对内存布局和访问速度有极高要求,数组的连续内存访问模式对 CPU 缓存更友好。
  • 作为 ArrayList 或其他集合的底层存储时ArrayList 内部就是用数组来实现的。
  • 在需要与 JNI (Java Native Interface) 交互时:因为 C/C++ 代码对原生数组操作更直接。

数组 ArrayList
一句话概括 一个固定大小的原始数据容器 一个可动态扩展的对象集合
核心优势 速度快,内存占用小 灵活方便,功能强大
核心劣势 不灵活,功能有限 有性能开销,不能存基本类型
使用场景 性能敏感、大小固定的场景 绝大多数日常开发场景

ArrayList 是数组的“超级升级版”,它保留了数组快速访问的优点,同时解决了其长度固定、功能单一的缺点,在日常开发中,除非你有非常特殊的需求,否则都应该优先使用 ArrayList

Java数组和ArrayList到底该用哪个?-图2
(图片来源网络,侵删)
Java数组和ArrayList到底该用哪个?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇