ArrayList 和 Array区别
ArrayList
和 Array
都用于存储元素,但它们的实现和行为有很多不同。以下是这两者的详细对比和区别:
1. 存储方式
-
Array(数组):数组是 Java 中最基本的数据结构,用于存储一组相同类型的元素。数组的大小在创建时确定,且大小不可改变。
int[] array = new int[5]; // 创建一个固定大小为 5 的数组
-
ArrayList:
ArrayList
是基于数组的动态集合类,大小是可变的。ArrayList
内部使用数组来存储元素,但在元素增加时会自动扩展数组的大小。ArrayList<Integer> arrayList = new ArrayList<>(); // 创建一个空的 ArrayList
2. 固定大小 vs 动态大小
-
Array(数组):数组的大小在创建时决定,一旦设置后,数组的大小无法改变。如果需要更大的数组,必须创建一个新的数组并复制元素。
int[] array = new int[3]; // 数组大小为 3 array = new int[5]; // 重新创建一个更大的数组
-
ArrayList:
ArrayList
的大小是动态的,当元素数量超过当前数组的容量时,ArrayList
会自动扩容,通常扩容为原容量的 1.5 倍。ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); // 可以动态增加元素,ArrayList 会自动扩容
3. 类型
-
Array(数组):数组是一个基本的数据结构,支持基本数据类型(如
int
,char
等)以及对象类型。int[] arr1 = new int[3]; // 存储基本类型 String[] arr2 = new String[3]; // 存储对象类型
-
ArrayList:
ArrayList
只能存储对象(如Integer
,String
等),不能直接存储基本数据类型。如果需要存储基本数据类型,可以使用其包装类(如Integer
,Double
等)。ArrayList<Integer> list = new ArrayList<>(); // 存储对象类型
4. 访问元素的方式
-
Array(数组):通过索引访问数组中的元素,支持快速随机访问(O(1) 时间复杂度)。
int[] arr = {1, 2, 3}; System.out.println(arr[1]); // 输出 2
-
ArrayList:
ArrayList
也通过索引访问元素,访问时间同样是 O(1),但它内部是通过方法调用来访问元素。ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); System.out.println(list.get(1)); // 输出 2
5. 插入和删除元素
-
Array(数组):数组大小是固定的,插入和删除元素较为麻烦。如果需要增加或删除元素,必须手动移动元素或者创建一个新的数组。
int[] arr = {1, 2, 3}; arr[1] = 5; // 可以直接修改元素
-
ArrayList:
ArrayList
支持动态插入和删除元素,插入和删除元素时,ArrayList
会自动扩展或收缩,并且内部会处理元素的移动。ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(1, 3); // 在索引 1 位置插入 3 list.remove(0); // 删除索引 0 位置的元素
6. 性能
-
Array(数组):由于数组的大小是固定的,且直接在内存中存储,访问速度非常快。对数组的读取和写入操作的时间复杂度为 O(1)。
-
ArrayList:
ArrayList
的插入和删除操作会导致数组的重新分配(当数组容量不足时)。因此,插入和删除元素的性能相对较差,尤其是在数组的中间位置。扩容操作可能导致性能下降。ArrayList<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); // 插入元素,可能会触发扩容 }
7. 内存管理
-
Array(数组):数组的内存分配是静态的,即数组创建时需要指定大小,这会消耗一块固定的内存区域。
-
ArrayList:
ArrayList
会根据需要动态分配内存,在扩容时,ArrayList
会创建一个新的更大的数组,并将原数组的元素复制到新数组中。这个过程会消耗更多的内存。
8. 常见操作性能
访问元素(通过索引) | O(1) | O(1) |
插入元素(末尾) | O(1)(前提是数组容量足够) | 平均 O(1),但可能需要扩容 |
插入元素(中间) | O(n)(需要移动元素) | O(n)(需要移动元素) |
删除元素(末尾) | O(1) | O(1)(除非需要扩容) |
删除元素(中间) | O(n)(需要移动元素) | O(n)(需要移动元素) |
9. 代码示例
Array(数组)使用示例:
public class ArrayExample {
public static void main(String[] args) {
int[] arr = new int[5]; // 创建一个大小为 5 的数组
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
// 访问元素
System.out.println(arr[2]); // 输出 3
// 修改元素
arr[2] = 10;
System.out.println(arr[2]); // 输出 10
}
}
ArrayList 使用示例:
import java.util.*;
public class ArrayListExample {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(); // 创建一个空的 ArrayList
list.add(1);
list.add(2);
list.add(3);
// 访问元素
System.out.println(list.get(1)); // 输出 2
// 修改元素
list.set(1, 10);
System.out.println(list.get(1)); // 输出 10
// 插入元素
list.add(1, 5); // 在索引 1 位置插入 5
System.out.println(list); // 输出 [1, 5, 10, 3]
// 删除元素
list.remove(2); // 删除索引 2 位置的元素
System.out.println(list); // 输出 [1, 5, 3]
}
}
10. 总结
大小 | 固定大小,创建时指定 | 动态大小,自动扩容 |
存储类型 | 支持基本数据类型和对象 | 只能存储对象(需包装类) |
访问效率 | O(1) | O(1) |
插入删除效率 | 插入删除效率低,特别是在中间位置 | 插入删除效率较低,特别是在中间位置 |
内存管理 | 静态分配内存,大小固定 | 动态分配内存,扩容时复制元素 |
灵活性 | 不够灵活 | 很灵活,适合动态数据操作 |
- 数组(Array):适用于大小固定的情况,访问速度快,但不适合频繁插入和删除操作。
ArrayList
:适用于需要动态扩展和灵活操作的场景,但在插入和删除操作中可能会有性能损失,尤其是在大规模数据操作时。
来一杯咖啡,聊聊Java的碎碎念呀