引言
在 Java 编程中,ArrayList 是一个非常常用的动态数组实现。然而,在使用它时,我发现了一些让我困惑的现象,尤其是在执行 tmp.set(i, nums[j]) 时,不同的初始化方式竟然导致了完全不同的行为。这让我意识到,我之前对 ArrayList 的 size 和 capacity 存在误解。本文将通过我的经历,总结这两种初始化方式的差异,并澄清 size 和 capacity 的具体含义,希望能帮助其他开发者避免类似的困惑。
问题的背景
我在编写代码时,尝试用 tmp.set(i, nums[j]) 来设置 ArrayList 中的元素,却发现结果出乎意料。我比较了以下两种初始化方式:
List<Integer> tmp = Arrays.asList(new Integer[nums.length]);List<Integer> tmp = new ArrayList<>(nums.length);
通过调试,我发现这两种方式在执行 set 操作时的表现截然不同,而原因就藏在 size 和 capacity 的差异中。更让我惊讶的是,我之前误以为 new ArrayList<>(16) 创建了一个可以直接通过 set 操作索引 4 的列表,但事实并非如此。
size 和 capacity 的定义
在 ArrayList 中,size 和 capacity 是两个核心概念,它们含义不同:
size:表示ArrayList中当前实际存储的元素数量。可以用size()方法获取。capacity:表示ArrayList内部数组的容量,也就是在需要扩容之前,数组能容纳的最大元素数量。
初始化时的表现
-
默认构造:
new ArrayList<>()size= 0(没有元素)capacity= 10(默认容量)
-
指定容量构造:
new ArrayList<>(16)size= 0(仍然没有元素)capacity= 16(内部数组预分配 16 个位置)
-
使用
Arrays.asList:Arrays.asList(new Integer[3])size= 3(列表长度等于数组长度)capacity不适用(Arrays.asList返回的是固定大小的列表,不是ArrayList,没有动态扩容的capacity概念)
我的误解
我之前以为,new ArrayList<>(16) 创建了一个容量为 16 的列表后,就可以直接用 tmp.set(4, someValue) 设置索引 4 的元素。然而,当我运行代码时,却抛出了 IndexOutOfBoundsException。原因在于,我混淆了 size 和 capacity:
new ArrayList<>(16)只设置了capacity为 16,但size仍然是 0。set(int index, E element)方法要求index < size(),而不能依赖capacity。- 因为
size是 0,任何set操作都会失败。
这让我意识到,capacity 只是预留空间,而 size 才是决定能否使用 set 的关键。
两种初始化方式的具体差异
为了更清楚地说明问题,我以 nums.length = 3 为例,分析两种初始化方式在执行 tmp.set(i, nums[j]) 时的行为:
1. List<Integer> tmp = Arrays.asList(new Integer[3]);
-
初始状态:
size= 3- 元素为
[null, null, null] - 这是一个固定大小的列表,不能扩容。
-
set操作:- 可以执行
tmp.set(0, 10)、tmp.set(1, 20)、tmp.set(2, 30),因为size是 3,索引 0 到 2 都有效。 - 不能执行
tmp.set(3, 40),会抛出异常,因为索引超出了size。
- 可以执行
-
限制:
- 不能用
add添加新元素,因为列表大小固定。
- 不能用
2. List<Integer> tmp = new ArrayList<>(3);
-
初始状态:
size= 0capacity= 3- 内部数组为空。
-
set操作:- 不能执行
tmp.set(0, 10),因为size是 0,没有任何元素可供修改,会抛出IndexOutOfBoundsException。
- 不能执行
-
解决方法:
- 需要先用
add添加元素,例如:tmp.add(0); // size = 1 tmp.add(0); // size = 2 tmp.add(0); // size = 3 tmp.set(0, 10); // 现在可以修改 - 添加超过 3 个元素时,
capacity会自动扩展(通常翻倍)。
- 需要先用
需求总结:size 和 capacity 的差异
通过这次经历,我总结了在使用 ArrayList 时,如何根据需求选择初始化方式,以及 size 和 capacity 的具体差异:
| 属性 | Arrays.asList(new Integer[n]) | new ArrayList<>(n) |
|---|---|---|
| size | 初始为 n,等于数组长度 | 初始为 0,需要通过 add 增加 |
| capacity | 不适用(固定大小,无扩容机制) | 初始为 n,可动态扩容 |
set 操作 | 可直接对 0 到 n-1 的索引操作 | 只能对 0 到 size-1 的索引操作 |
| 动态性 | 固定大小,不能 add 或 remove | 动态大小,支持 add 和 remove |
适用场景
-
需要固定大小且初始元素为
null:- 使用
Arrays.asList(new Integer[nums.length])。 - 适合直接用
set修改元素,但不能扩展。
- 使用
-
需要动态调整大小:
- 使用
new ArrayList<>(initialCapacity)。 - 适合通过
add逐步填充数据,capacity可根据需要扩容。
- 使用
注意事项
set的限制:只能修改已有元素(index < size),不能用来添加新元素。add的作用:增加size,并在必要时扩展capacity。- 误解澄清:
new ArrayList<>(16)并不意味着可以直接set索引 0 到 15 的元素,必须先通过add让size增长。
结论
通过分析 tmp.set(i, nums[j]) 的行为,我终于厘清了 ArrayList 中 size 和 capacity 的区别。size 是当前元素数量,直接影响 set 操作的有效范围;capacity 是内部数组的容量,影响性能而非功能的可用性。