在 Java 中,ArrayList 内部是通过一个数组来存储元素的,是一个数组结构的存储容器。当向一个 ArrayList 中添加元素时,如果当前数组已经满了,就需要扩容。
集合的继承关系图
一、面试回答
( ArrayList 的扩容机制原理 )
面试官好,ArrayList 是一个数组结构的存储容器,默认情况下,设置数组长度是 10. 当然我们也可以在构建 ArrayList 对象的时候自己指定初始长度。 随着在程序里面不断的往 ArrayList 中添加数据,当添加的数据达到 10 个的时候, ArrayList 就没有多余容量可以存储后续的数据。 这个时候 ArrayList 会自动触发扩容。 扩容的具体流程很简单, 1. 首先,创建一个新的数组,这个新数组的长度是原来数组长度的 1.5 倍。 2. 然后使用 Arrays.copyOf 方法把老数组里面的数据拷贝到新的数组里面。 扩容完成后再把当前要添加的元素加入到新的数组里面,从而完成动态扩容的过程。 以上就是我对这个我对这个问题的理解!
或者不直接问: ArrayList 扩容是在第10个元素还是第11个元素触发的 ?
在 Java 中,ArrayList
的扩容是在添加第11个元素时触发的,当 ArrayList
中的元素数量达到了其初始容量(默认为 10)时,ArrayList
会自动扩容,新的容量为原来的 1.5 倍。当然也可以在创建 ArrayList
对象时指定其初始容量,以避免频繁的扩容操作。
二、源码理解(Debug模式)
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class ArrayListSource {
public static void main(String[] args) {
//解读源码
//注意,注意,注意,Idea 默认情况下,Debug 显示的数据是简化后的,如果希望看到完整的数据
//需要做设置. //使用无参构造器创建 ArrayList 对象
ArrayList list = new ArrayList();
// ArrayList list = new ArrayList(8);
//使用 for 给 list 集合添加 1-10 数据
for (int i = 1; i <= 10; i++) {
list.add(i);
}
//使用 for 给 list 集合添加 11-15 数据
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
}
F7 | 单步调试进行下一步,遇到方法会进入方法内,同一行有多个方法时可以用左右键选择; |
F8 | 单步调试,进行下一步,不会进入方法内; |
Alt+Shift+F7 | 强制进入方法内; |
Shift+F8 | 直接跳出方法; |
F9 | 跳到下一个断点或者直接执行完程序 |
首先进入ArrayList的构造器,看到 elementData 第一次初始化的时候就是一个空数组
接下来进入到for循环,第一次进去会把int 给类型转化,进行一个装箱操作。
然后在添加的时候 ,先执行了判断这个要添加的这个 e 的大小是否达到要求,满足了再将e放入
第一次返回的一定是 10 (是规定好的了)
拿到 minCapacity 然后再进入到 ensureExplicitCapacity 其中modCount 表示被修改的次数(这里主要防止有多个线程同时去修改,如果有,则会抛出异常)
只有当 elementData 的大小小于10的时候就调用grow 方法进行扩容。
先把传进来的数组大小赋值给一个变量 oldCapacity ,然后按照原先数组的1.5倍进行扩容(右移一位,相当于除以2) 而 Arrays.copyOf 其实就是数组的复制。
即得到了 10个 elementData 的空元素
ArrayList 的扩容机制是在添加元素时判断当前数组大小是否已经满了,如果已经满了,则创建一个新的更大的数组,并将原来的元素全部复制到新的数组中。具体的扩容规则如下:
- 当添加元素后,size 大小已经等于或超过了数组的容量 capacity 时,就会触发扩容操作。
- 扩容的大小默认情况下为原数组大小的一半。比如原数组大小为 10,那么扩容后的数组大小为 15。
- 如果扩容后的大小还是不够,那么就以添加元素的数量作为扩容大小,即新数组的大小为 oldCapacity + (oldCapacity >> 1) + 1。
需要注意的是,ArrayList 中的数组无法动态地调整大小,因此每次扩容都需要创建新的数组和复制元素,这可能会带来一些性能损失。为了避免频繁扩容,我们可以在使用 ArrayList 时尽量预估元素数量,初始化时指定一个合适的初始容量。
在实际使用中,我们可以通过指定初始容量和适当的预估来优化扩容操作,以提高性能。