你真的了解ArrayList 与 Array(数组)的区别吗?

引言

在Java编程中,数据存储和操作是最基础也是最重要的部分。Java提供了两种主要的方式来存储一组数据:传统的数组(Array)和ArrayList。虽然它们都用于存储元素集合,但在实现方式、功能和性能上有着显著差异。本文将深入探讨这两者的区别,并通过JDK 1.8中的代码示例进行详细说明。

1. 基本概念

数组(Array)

数组是Java中最基本的数据结构之一,它是一个固定长度的容器,可以存储相同类型的元素。

// 数组声明和初始化
int[] intArray = new int[5]; // 固定长度为5的整型数组
String[] stringArray = {"Java", "Python", "C++"}; // 初始化时指定元素

ArrayList

ArrayList是Java集合框架的一部分,它实现了List接口,基于动态数组实现,可以根据需要自动调整容量。

// ArrayList声明和初始化
ArrayList<Integer> intList = new ArrayList<>(); // 初始容量为10的空列表
ArrayList<String> stringList = new ArrayList<>(Arrays.asList("Java", "Python", "C++"));

2. 核心区别

2.1 大小固定性

数组的大小在创建时就固定了,无法动态改变。

int[] fixedArray = new int[3];
fixedArray[0] = 1;
fixedArray[1] = 2;
fixedArray[2] = 3;
// fixedArray[3] = 4; // 抛出ArrayIndexOutOfBoundsException

ArrayList的大小是动态的,会根据需要自动增长。

ArrayList<Integer> dynamicList = new ArrayList<>();
dynamicList.add(1);
dynamicList.add(2);
dynamicList.add(3);
dynamicList.add(4); // 自动扩容,不会抛出异常
System.out.println(dynamicList.size()); // 输出4

2.2 类型安全

数组在运行时检查类型安全。

Object[] objectArray = new String[3];
objectArray[0] = "Hello";
// objectArray[1] = 123; // 抛出ArrayStoreException

ArrayList使用泛型在编译时提供类型安全。

ArrayList<String> stringList = new ArrayList<>();
stringList.add("Hello");
// stringList.add(123); // 编译时错误

2.3 性能比较

基本操作性能对比

// 数组访问
long start = System.nanoTime();
int arrayElement = fixedArray[1];
long end = System.nanoTime();
System.out.println("数组访问时间: " + (end - start) + " ns");

// ArrayList访问
start = System.nanoTime();
int listElement = dynamicList.get(1);
end = System.nanoTime();
System.out.println("ArrayList访问时间: " + (end - start) + " ns");

通常数组的访问速度略快于ArrayList,因为ArrayList有额外的方法调用开销。

2.4 功能丰富性

ArrayList提供了更多便捷的方法:

ArrayList<String> languages = new ArrayList<>(Arrays.asList("Java", "Python", "C++"));

// 添加元素
languages.add("JavaScript"); 

// 删除元素
languages.remove("Python"); 

// 检查包含
boolean hasJava = languages.contains("Java"); 

// 获取子列表
List<String> subList = languages.subList(1, 3); 

// 遍历(JDK 1.8 lambda)
languages.forEach(System.out::println);

相比之下,数组的功能非常基础,需要手动实现许多操作。

3. 内存管理

数组的内存分配

数组在创建时就分配了连续的内存空间:

int[] largeArray = new int[1000000]; // 立即分配内存

ArrayList的内存管理

ArrayList使用动态扩容策略:

ArrayList<Integer> list = new ArrayList<>(); // 初始容量10

// 当元素数量超过容量时,自动扩容(通常增加50%)
for (int i = 0; i < 100; i++) {
    list.add(i);
}

可以通过ensureCapacity方法预先分配空间以提高性能:

ArrayList<Integer> optimizedList = new ArrayList<>();
optimizedList.ensureCapacity(1000000); // 预先分配大容量

4. 多维结构

多维数组

Java支持真正的多维数组:

int[][] matrix = new int[3][3];
matrix[0][0] = 1;

ArrayList的多维实现

ArrayList需要通过嵌套实现多维结构:

ArrayList<ArrayList<Integer>> matrixList = new ArrayList<>();
matrixList.add(new ArrayList<>(Arrays.asList(1, 2, 3)));
matrixList.add(new ArrayList<>(Arrays.asList(4, 5, 6)));

5. 原始类型处理

数组处理原始类型

数组可以直接存储原始类型:

int[] primitiveArray = new int[10]; // 不涉及装箱/拆箱

ArrayList处理原始类型

ArrayList只能存储对象,需要使用包装类:

ArrayList<Integer> wrapperList = new ArrayList<>(); // 存储Integer对象
wrapperList.add(5); // 自动装箱
int value = wrapperList.get(0); // 自动拆箱

6. 使用场景建议

适合使用数组的场景

  1. 知道确切且不会改变的元素数量
  2. 需要最高性能的原始类型操作
  3. 需要多维数据结构
  4. 与需要数组作为输入的低级API交互
// 图像处理中的像素矩阵
int[][] imagePixels = new int[1024][768];

适合使用ArrayList的场景

  1. 元素数量不确定或会变化
  2. 需要频繁的插入、删除操作
  3. 需要丰富的集合操作方法
  4. 需要与其他集合框架组件交互
// 用户动态添加的商品列表
ArrayList<Product> shoppingCart = new ArrayList<>();
shoppingCart.add(new Product("Laptop", 999.99));
shoppingCart.removeIf(p -> p.getPrice() > 1000); // JDK 1.8新方法

7. 相互转换

数组转ArrayList

String[] array = {"A", "B", "C"};
ArrayList<String> listFromArray = new ArrayList<>(Arrays.asList(array));

注意:Arrays.asList()返回的是固定大小的列表,不能添加/删除元素。

ArrayList转数组

ArrayList<String> list = new ArrayList<>(Arrays.asList("X", "Y", "Z"));
String[] arrayFromList = list.toArray(new String[0]);

8. JDK 1.8中的新特性应用

JDK 1.8为ArrayList引入了Stream API支持:

ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// 使用Stream API过滤和收集
List<Integer> evenNumbers = numbers.stream()
                                  .filter(n -> n % 2 == 0)
                                  .collect(Collectors.toList());

// 并行流处理
int sum = numbers.parallelStream()
                 .mapToInt(Integer::intValue)
                 .sum();

结论

特性

数组(Array)

ArrayList

大小

固定

动态可变

类型安全

运行时检查

编译时泛型检查

性能

原始类型操作更快

有额外方法调用开销

功能

基础

丰富的方法集

多维支持

直接支持

需要嵌套实现

原始类型

直接支持

需要使用包装类

内存管理

一次性分配

动态扩容

在选择使用数组还是ArrayList时,应根据具体需求决定。对于性能关键且大小固定的原始类型数据,数组是更好的选择。而对于需要动态大小和丰富操作的情况,ArrayList提供了更多便利。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凡尘扰凡心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值