ArrayList 扩容

本文深入剖析Java ArrayList在JDK1.8中的扩容源码,详细解释了构造方法、添加元素过程及扩容策略。通过分析add方法,揭示了何时进行扩容以及如何计算新的容量。在扩容核心方法grow()中,探讨了旧容量的一半与最小需求容量的取舍,确保扩容满足需求。

本文是作者在准备面试中记录自己的一些心得体会,大佬路过请帮忙点评一下,有错请指正。

分析的是jdk1.8的源码,源码上面的注释英语学渣表示看不懂,使用百度翻译配合理解。有错请评论告知。

一、首先来认识一下几个参数变量:

1、默认初始容量。

 2、定义了一个空数组

 3、定义一个默认空数组,将其与EMPTY_ELEMENTDATA区分开,用来判断是否需要扩容。

4、elementData是用来存储元素的数组缓冲区。

5、数组长度size

二、三个构造方法

1、有参构造方法,参数是列表的初始容量。如果传进来的参数大于零则创建一个大小为initial

### Java ArrayList 扩容机制与源码分析 #### 1. ArrayList 的初始化 当使用无构造函数创建 `ArrayList` 时,内部会初始化一个空数组 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`,其长度为 0。此时,`ArrayList` 并不会分配实际的存储空间[^3]。 ```java public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } ``` 只有在调用 `add()` 方法添加第一个元素时,才会触发扩容操作,确保有足够的容量来存储新元素[^4]。 #### 2. 扩容触发条件 `ArrayList` 的扩容机制主要通过 `ensureCapacityInternal()` 方法实现。每当尝试添加新元素时,该方法会检查当前数组的容量是否足够。如果容量不足,则会调用 `grow()` 方法进行扩容[^5]。 ```java private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } ``` #### 3. 扩容实现细节 扩容的核心逻辑由 `grow()` 方法完成。默认情况下,`ArrayList` 的容量会增加到原容量的 1.5 倍(即 `(oldCapacity * 3)/2 + 1`),以减少频繁扩容带来的性能开销。 ```java private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); // 增加原容量的一半 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } ``` 这里使用了 `Arrays.copyOf()` 方法来创建一个新的数组,并将原有数据复制到新数组中[^5]。 #### 4. 扩容中的特殊情况处理 - 如果计算出的新容量仍小于所需的最小容量 `minCapacity`,则直接将容量设置为 `minCapacity`。 - 如果新容量超过了 `MAX_ARRAY_SIZE`(即 `Integer.MAX_VALUE - 8`),则调用 `hugeCapacity()` 方法处理极端情况。 ```java private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // 溢出 throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } ``` #### 5. 性能考量 每次扩容都会涉及数组的复制操作,时间复杂度为 O(n)。因此,频繁的扩容会导致性能下降。为了优化性能,可以在创建 `ArrayList` 时指定初始容量,避免多次扩容[^3]。 ```java public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } ``` --- ### 示例代码 以下是一个简单的代码示例,展示 `ArrayList` 的扩容过程: ```java import java.util.ArrayList; public class ArrayListDemo { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); for (int i = 0; i < 15; i++) { list.add(i); System.out.println("Size: " + list.size() + ", Capacity: " + getCapacity(list)); } } private static int getCapacity(ArrayList<?> list) { return java.lang.reflect.Array.getLength(((ArrayList<?>) list).toArray()); } } ``` 输出结果将显示每次扩容后的新容量变化。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值