ArrayList


ArrayList

ArrayList是一个数组队列,相当于动态数组,与Java中的数组相比,它没有固定大小的限制,其容量可以动态增长。

在添加大量元素前,应用程序可以使用ensureCapacity方法来增加ArrayList实例的容量。这样可以减少递增式再分配的数量

/**
 * Increases the capacity of this <tt>ArrayList</tt> instance, if
 * necessary, to ensure that it can hold at least the number of elements
 * specified by the minimum capacity argument.
 *
 * @param   minCapacity   the desired minimum capacity
 */
public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

来看看ArrayList继承和实现了什么东西

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
	// ......
}

继承了AbstractList抽象类,实现了ListRandomAccessCloneablejava.io.Serializable这些接口

  • List:表明ArrayList是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问
  • RandomAccess:是一个标志接口,表明实现这个接口的List集合是支持快速随机访问的,在ArrayList中,我们可以通过元素的下标快速获取元素对象,这就是快速随机访问
  • Cloneable:表明ArrayList具有拷贝能力,可以进行深拷贝或者浅拷贝
  • Serializable:表明ArrayList可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输

在IDEA中的类图:

image-20240425102103030

ArrayList和Vector的区别?

  • ArrayList:是List的主要实现类,底层使用Object[]进行存储适用于频繁的查找工作,线程不安全
  • VectorList的古老实现类,底层使用Object[]存储,线程安全

ArrayList可以添加null值吗?

可以,AarrayList中可以存储任何类型的对象,包括null

@Test
public void testArrayList() {
    ArrayList<String> arrayList = new ArrayList<>();
    arrayList.add("String");
    arrayList.add(null);
    System.out.println(arrayList);
}

运行结果:

[String, null]

虽然可以添加null值,但是不建议,因为null值无意义,而且会让代码难以维护,比如忘记做判空处理就会导致空指针异常(NPE

ArrayList与LinkedList的区别?

  • 线程安全?

    • ArrayListLinkedList都是不同步的,也就是不保证线程安全
  • 底层数据结构?

    • ArrayList底层使用Object[]数组
    • LinkedList底层使用双向链表数据结构(JDK1.6以前为循环链表,JDK1.7取消了循环。注意不是双向循环链表
  • 插入和删除是否受元素位置的影响?

    • ArrayList采用数组存储,所以插入和删除的时间复杂度受元素位置影响

      执行add(E e)方法,默认将元素加到元素列表的末尾,这种情况的时间复杂度就是O(1)

      如果是指定位置插入和删除元素(add(index index, E element)),时间复杂度为O(n)

    • LinkedList采用双向链表存储,所以在头尾插入和删除元素不受元素位置的影响例如:add(E e)addFirst(E e)removeFirst()removeLast()时间复杂度是0(1)

      如果是指定位置插入和删除元素(add(int index, E element)remove(Object o)remove(int index))时间复杂度为O(n)

  • 是否支持快速随机访问?(快速随机访问:通过元素的序号快速获取元素对象(get(int index)))

    • LinkedList不支持高效的随机元素访问
    • ArrayList因为实现了RandomAccess接口,所以支持
  • 内存空间占用

    • ArrayList的空间花费主要体现在:在list列表的结尾会预留一定的容量空间
    • LinkedList的空间花费主要体现在:它的每一个元素都要消耗比ArrayList更多的空间(因为要存放直接前驱直接后继数据

ArrayList核心源码

属性值

// 序列号
private static final long serialVersionUID = 8683452581122892189L;
// 默认初始容量大小
private static final int DEFAULT_CAPACITY = 10;
// 空数组(用于空实例)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小空实例的共享空数组实例
// 把他从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量要增加多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 保存ArrayList数据的数组
transient Object[] elementData; // non-private to simplify nested class access
// ArrayList所包含的元素个数
private int size;
// 要分配的最大数组大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造器

  • 带初始容量参数的构造器(用户可以在创建ArrayList对象时自己指定集合的初始大小)

    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);
        }
    }
  • 默认无参构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA为0,初始化为10就是说初始其实是空数组,只有当添加第一个元素的时候数组容量才变成10)

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
  • 可以指定集合的元素列表的构造器,按照他们由集合的迭代器返回的顺序

    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

方法

修改当前ArrayList实例的容量为列表当前的大小,可以使用该方法来时ArrayList实例的存储最小化

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
    }
}

ArrayList的扩容机制

AarrayList的扩容机制提高了性能,如果每次之扩充一个,那么频繁的插入会导致频繁地拷贝,降低性能,而ArrayList的扩容机制避免了这个问题

// 如果有必要,增加当前AarrayList实例的容量,以确保其至少能容纳元素的数量
public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;
	// 如果最小容量大于已有的最大容量,就调用ensureExplicitCapacity方法判断是否扩容
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

// 根据给定的最小容量和当前数组元素来计算所需容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果当前数组元素为空数组,返回默认容量和最小容量中的较大值作为所需容量
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    // 否则直接返回最小容量
    return minCapacity;
}

// 确保内部容量达到指定的最小值
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        // 调用grow方法进行扩容,调用grow方法就代表已经开始扩容了
        grow(minCapacity);
}


// MAX_ARRAY_SIZE:要分配的最大数组大小
// ArrayList扩容的核心方法
private void grow(int minCapacity) {
    // oldCapacity:旧容量
    // newCapacity:新容量
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 + 旧容量 / 2(左移一位就相当于除以2)
    // 位运算速度远远快于整除运算,新容量等于1.5倍的旧容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 检查新容量是否大于最小需要容量,如果小于则将最小需要容量当作数组的新容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 再次检查新容量是否超出了ArrayList所定义的最大容量
    // 如果超出了就调用hugeCapacity方法来比较minCapacity来比较minCapacity和
    // MAX_ARRAY_SIZE,如果minCapacity大于MAX_ARRAY_SIZE,则新容量为Integer.MAX_VALUE
    // 否则新容量就是MAX_ARRAY_SIZE
    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);
}

// 比较minCapacity和MAX_ARRAY_SIZE
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

基本方法

// 返回当前ArrayList中的元素数
public int size() {
    return size;
}

// 判断当前ArrayList是否为空
public boolean isEmpty() {
    return size == 0;
}

// 判断当前ArrayList中是否包含指定的元素
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

// 返回当前ArrayList中指定元素首次出现对应的索引,如果没找到就返回-1
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

// 返回当前ArrayList中指定元素的最后一次出现的索引,找不到就返回-1
public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

// 返回当前ArrayList对象的浅拷贝,元素本事不被复制
public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

// 以正确的顺序返回一个包含当前ArrayList对象中所有元素的数组
// 返回的数组是安全地,因为当前ArrayList对象不保留对其的引用(即调用该方法就必须要分配一个新的数组)
// 因此调用者可以自由地修改返回的数组
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

// 以正确的顺序返回一个包含当前ArrayList对象中所有元素的数组
// 返回的数组的运行时类型是指定数组的运行时类型,如果当前ArrayList对象适合指定的数组,则返回其中
// 否则将为指定数组的运行时类型和当前ArrayList对象的大小分配一个新数组
// 如果ArrayList对象适用于指定的数组,其余空间(数组长度大于当前ArrayList对象的长度)
// 则紧跟在集合结束后的数组中的元素设置为null
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // 新建一个运行时类型的数组,但是是ArrayList数组的内容
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    // 调用System提供的arraycopy方法,实现数组之间的复制
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

// Positional Access Operations

@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

// 返回指定索引位置的元素
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

// 用指定索引以及元素替换掉当前ArrayList对象中指定位置的元素
public E set(int index, E element) {
    // 对index索引进行界限检查
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    // 返回原来在这个位置的元素
    return oldValue;
}

// 将元素e添加到当前ArrayList对象的末尾
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

// 在当前ArrayList中的指定index位置中插入元素
public void add(int index, E element) {
    // 对index索引进行界限检查
    rangeCheckForAdd(index);

    // 保证capacity足够大
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将index开始后的所有元素往后移一位,将element插入到指定index位置,再让size+1
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

// 移除指定index索引处的元素,并将后续元素往左移动一位
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
	// 返回被删除的元素
    return oldValue;
}

// 从当前ArrayList对象中删除指定元素的第一个出现,如果没找到则不会更改
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

// 删除当前ArrayList对象中所有元素
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

// 按指定集合的Iterator返回的顺序,将指定集合c中的所有元素追加到当前ArrayList对象的末尾
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

// 将指定集合中的所有元素插入到此列表中,从指定的位置开始
public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

// 从此列表中删除所有索引为fromIndex (含)和toIndex之间的元素
// 将任何后续元素移动到左侧(减少其索引)
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private String outOfBoundsMsg(int index) {
    return "Index: "+index+", Size: "+size;
}

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

public ListIterator<E> listIterator() {
    return new ListItr(0);
}

public Iterator<E> iterator() {
    return new Itr();
}

AarrayList扩容机制

以无参构造器创建的ArrayList来分析一下

默认无参构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA为0,初始化为10就是说初始其实是空数组,只有当添加第一个元素的时候数组容量才变成10)

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

add()

// 将元素e添加到当前ArrayList对象的末尾
public boolean add(E e) {
    // 在添加元素之前先调用ensureCapacityInternal方法,以确保内部容量达到指定的最小容量
    // size:当前ArrayList对象拥有的元素数量
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

JDK11移除了 ensureCapacityInternal()ensureExplicitCapacity() 方法

ensureCapacityInternal

// 确保内部容量达到指定的最小容量。
private void ensureCapacityInternal(int minCapacity) {
   ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

ensureCapacityInternal内部直接调用了ensureExplicitCapacity方法

calculateCapacity

// 根据给定的最小容量和当前数组元素来计算所需容量。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
   // 如果当前数组元素为空数组(初始情况),返回默认容量和最小容量中的较大值作为所需容量
   if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
       return Math.max(DEFAULT_CAPACITY, minCapacity);
   }
   // 否则直接返回最小容量
   return minCapacity;
}

ensureExplicitCapacity

//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
   modCount++;
   //判断当前数组容量是否足以存储minCapacity个元素
   if (minCapacity - elementData.length > 0)
       //调用grow方法进行扩容
       grow(minCapacity);
}
  • 当我们add第一个元素到ArrayList时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以 minCapacity 此时为 10。此时,minCapacity - elementData.length > 0成立,所以会进入 grow(minCapacity) 方法。
  • add 第 2 个元素时,minCapacity 为 2,此时 elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。
  • 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。

直到添加第11个元素,minCapacity(为11)比elementData(为10)要大,就进入grow方法进行扩容

grow()

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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);
}

int newCapacity = oldCapacity + (oldCapacity >> 1);ArrayList扩充容量为原来的1.5倍由此可见,如果oldCapacity的值为偶数就是1.5倍,如果是奇数就是1.5倍左右,因为奇数在进行运算的时候会丢掉小数(33+33/2=49)

位移运算符(>>):>>1右移一位相当于除以2,右移n位相当于除以2的n次幂

这里oldCapacity明显右移一位,所以相当于oldCapacity/2,对于大数据的二进制位运算,位移运算符比普通的运算符要快很多,因为程序仅仅只是移动了一下而非计算,这样可以提高效率节省资源

  • 当我们add1个元素到ArrayList时,oldCapacity=0,第一个if判断成立,newCapacity = minCapacity(是10),第二个if判断不会成立,不会进入hugeCapacity,此时数组容量为10,在add()中size变为1
  • add到第11个元素到ArrayList时,newCapacity(为15)比minCapacity(为11)大,第一个if判断不成立,newCapacity也不大于MAX_ARRAY_SIZE(数组最大size),第二个if判断也不成立,不会进入hugeCapacity()。数组容量扩容到newCapacity即15,回到add()中size变为11。

hugeCapacity()

grow()方法中,当我们的新容量大于数组最大size的时候就会调用hugeCapacity(),用来比较minCapacityMAX_ARRAY_SIZE,如果minCapacity大于最大容量则新容量就为Integer.MAX_VALUE(2^31 - 1),否则就为Integer.MAX_VALUE - 8

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
public static final int   MAX_VALUE = 0x7fffffff;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ensureCapacity()

一开始就提到过这个方法,在添加大量元素前,应用程序可以使用ensureCapacity方法来增加ArrayList实例的容量。这样可以减少递增式再分配的数量

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

理论上来说,最好在向 ArrayList 添加大量元素之前用 ensureCapacity 方法,以减少增量重新分配的次数

测试:

@Test
public void testEnsureCapacity1() {
    ArrayList<Integer> list = new ArrayList<>();
    final int N = 10000000;
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
}

image-20240426231514340

@Test
public void testEnsureCapacity2() {
    ArrayList<Integer> list = new ArrayList<>();
    final int N = 10000000;
    list.ensureCapacity(N);
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法后:"+(endTime - startTime));
}

image-20240426231557163


文章作者: Feliks
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Feliks !
评论
  目录