ArrayList的构造函数分析ArrayList有三个主要的构造函数ArrayList(): 无参构造函数以无参数构造方法创建ArrayList时实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时才真正分配容量。即向数组中添加第一个元素时数组容量扩为 10。ArrayList(int initialCapacity): 通过指定初始容量来创建一个ArrayList。ArrayList(Collection? extends E c): 通过另一个集合来初始化ArrayList容量初始为c.size()。/** * 默认初始容量大小 */ private static final int DEFAULT_CAPACITY 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {}; /** * 默认构造函数使用初始容量10构造一个空列表(无参数构造) */ public ArrayList() { this.elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 带初始容量参数的构造函数。用户自己指定容量 */ public ArrayList(int initialCapacity) { if (initialCapacity 0) {//初始容量大于0 //创建initialCapacity大小的数组 this.elementData new Object[initialCapacity]; } else if (initialCapacity 0) {//初始容量等于0 //创建空数组 this.elementData EMPTY_ELEMENTDATA; } else {//初始容量小于0抛出异常 throw new IllegalArgumentException(Illegal Capacity: initialCapacity); } } /** *构造包含指定collection元素的列表这些元素利用该集合的迭代器按顺序返回 *如果指定的集合为nullthrows NullPointerException。 */ public ArrayList(Collection? extends E c) { elementData c.toArray(); if ((size elementData.length) ! 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() ! Object[].class) elementData Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData EMPTY_ELEMENTDATA; } }add()方法及其扩容逻辑ArrayList的add()方法会将元素添加到内部数组。如果数组容量不够就会触发扩容机制。/** * 将指定的元素追加到此列表的末尾。 */ public boolean add(E e) { // 加元素之前先调用ensureCapacityInternal方法 ensureCapacityInternal(size 1); // Increments modCount!! // 这里看到ArrayList添加元素的实质就相当于为数组赋值 elementData[size] e; return true; }ensureCapacityInternal(size 1)这是扩容机制的核心。ensureCapacityInternal()首先检查当前数组的容量是否足够存放新元素。如果不够就会调用grow()方法进行扩容。elementData[size] e在扩容之后元素会被添加到elementData数组中size会增加表示当前ArrayList包含的元素数量。ensureCapacityInternal方法的源码如下// 确保内部容量达到指定的最小容量。 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }// 根据给定的最小容量和当前数组元素来计算所需容量。 private static int calculateCapacity(Object[] elementData, int minCapacity) { // 如果当前数组元素为空数组初始情况返回默认容量和最小容量中的较大值作为所需容量 if (elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } // 否则直接返回最小容量 return minCapacity; }//判断是否需要扩容 private void ensureExplicitCapacity(int minCapacity) { modCount; //判断当前数组容量是否足以存储minCapacity个元素 if (minCapacity - elementData.length 0) //调用grow方法进行扩容 grow(minCapacity); }grow()方法分析grow()方法用于扩容它会创建一个新数组并复制旧数组中的数据/** * 要分配的最大数组大小 */ private static final int MAX_ARRAY_SIZE Integer.MAX_VALUE - 8; /** * ArrayList扩容的核心方法。 */ private void grow(int minCapacity) { // oldCapacity为旧容量newCapacity为新容量 int oldCapacity elementData.length; // 将oldCapacity 右移一位其效果相当于oldCapacity /2 // 我们知道位运算的速度远远快于整除运算整句运算式的结果就是将新容量更新为旧容量的1.5倍 int newCapacity oldCapacity (oldCapacity 1); // 然后检查新容量是否大于最小需要容量若还是小于最小需要容量那么就把最小需要容量当作数组的新容量 if (newCapacity - minCapacity 0) newCapacity minCapacity; // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) hugeCapacity() 方法来比较 minCapacity 和 MAX_ARRAY_SIZE // 如果minCapacity大于最大容量则新容量则为Integer.MAX_VALUE否则新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8。 if (newCapacity - MAX_ARRAY_SIZE 0) newCapacity hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData Arrays.copyOf(elementData, newCapacity); }关键步骤通过oldCapacity 1来增加1.5倍容量。如果newCapacity大于Integer.MAX_VALUE则会调用hugeCapacity()来防止溢出。hugeCapacity()方法分析private static int hugeCapacity(int minCapacity) { if (minCapacity 0) // overflow throw new OutOfMemoryError(); // 对minCapacity和MAX_ARRAY_SIZE进行比较 // 若minCapacity大将Integer.MAX_VALUE作为新数组的大小 // 若MAX_ARRAY_SIZE大将MAX_ARRAY_SIZE作为新数组的大小 // MAX_ARRAY_SIZE Integer.MAX_VALUE - 8; return (minCapacity MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }hugeCapacity()方法比较minCapacity最小需要容量和MAX_ARRAY_SIZE如果minCapacity大于最大容量则新容量则为Integer.MAX_VALUE否则新容量大小则为MAX_ARRAY_SIZE即为Integer.MAX_VALUE - 8。ArrayList扩容机制总结ArrayList是Java中的一个动态数组实现其扩容机制是为了应对在添加元素时数组容量不足的情况。扩容的主要目的是避免频繁的内存分配和拷贝提高性能。以下是ArrayList扩容机制的核心概念和步骤1.初始容量默认情况下ArrayList使用一个容量为10的内部数组来存储元素使用无参构造器ArrayList()创建时先创建一个空数组第一次添加元素是使用默认大小10.通过ArrayList(int initialCapacity)构造函数可以设置自定义的初始容量这样在构造时就可以避免不必要的扩容。2.add()方法add(E e)方法是将元素添加到ArrayList中的核心方法。每当调用add()方法时会执行以下步骤检查容量首先会通过ensureCapacityInternal()方法检查当前容量是否足够容纳新元素。扩容如果当前容量不够则会调用grow()方法进行扩容。添加元素将元素添加到elementData数组中并更新size。3.扩容过程当ArrayList的容量不足时ensureCapacityInternal()会调用grow()方法进行扩容。grow()方法扩容规则grow(int minCapacity)方法的作用是动态扩展内部数组的容量。扩容的基本策略是将当前数组的容量增大1.5倍即newCapacity oldCapacity (oldCapacity 1)这种增量是为了平衡内存利用率和性能。如果扩容后的容量小于minCapacity则直接将容量设置为minCapacity。如果扩容后的容量大于Integer.MAX_VALUE则会采用hugeCapacity()来避免溢出。4.扩容时的数组复制扩容过程中grow()方法会创建一个新的数组并使用Arrays.copyOf()方法将原数组中的元素复制到新的数组中。这种数组的复制是一个相对昂贵的操作因此扩容的次数尽量应保持在最低。5.扩容的影响每次扩容都会导致内存的重新分配和元素的复制这可能会影响性能尤其是在大量元素被频繁添加的场景下。为了减少扩容次数可以通过设置合适的初始容量来避免过多的扩容操作。