【Java】ArrayList扩容过程 JDK1.8

本文深入解析了Java中ArrayList的工作原理,包括不同构造方法下数组的初始化方式、扩容机制及失败快速检测机制等关键细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  以下代码均取自ArrayList源码:

  1. 无参构造ArrayList后,list长度为0,内部数组长度为0。
	// 属性
	private int size;
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	// 无参构造方法
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    // size()方法
    public int size() {
        return size;
    }
  1. 指定初始长度initialCapacity构造ArrayList后,数组长度和list的size都为指定长度initialCapacity
	// 指定初始容量的构造方法
    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);
        }
    }
  1. 指定集合c构造ArrayList后,数组长度和list的size都为传入的集合c的size。
	// 使用给定集合创建ArrayList的构造方法
    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;
        }
    }
  1. 如果是第一次add或addAll,添加数量不足10个时,都扩容为10,addAll数量n大于10个时,所需最小容量minCapacity被指定为n;如果不是第一次add,所需最小容量minCapacity被指定为当前list的size加上add或addAll进去的数量n。
	// 属性;默认容量10
	private static final int DEFAULT_CAPACITY = 10;
	/**
	 * 计算所需最小容量minCapacity的方法
	 * 这里的入参minCapacity是从add()和addAll()方法传过来的
	 * 从add()传过来时,minCapacity = size + 1,size就是list.size()的返回值
	 * 从addAll()传过来时,minCapacity = size + n,n就是addAll进来的元素个数
	 * 这个方法仅仅是对于空的ArrayList(指无参构造的ArrayList且)的特殊处理,如果不是空的直接返回了minCapacity
	 */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
  1. 接下来,检查所需最小容量minCapacity是否超过了当前数组长度,如果超过了,则进行扩容grow(),并且modCount加1。
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

  modCount是继承自父类AbstractList的一个属性,记录了这个ArrayList被修改的次数,会在迭代器中用到。在遍历ArrayList的同时用不恰当的方式修改其中的元素会抛出ConcurrentModificationException,就是依靠这个属性判断的。那么什么是不恰当的方式呢?
  比如,使用增强for循环遍历,并使用ArrayList的remove()方法删除元素,是不恰当的,会抛出异常;而使用Iterator遍历,并使用Iterator提供的remove()方法删除元素,是恰当的,不会抛出异常。
  下面给出源码对属性modCount的注释:

  The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
  This field is used by the iterator and list iterator implementation returned by the {@code iterator} and {@code listIterator} methods. If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a {@code ConcurrentModificationException} in response to the {@code next}, {@code remove}, {@code previous}, {@code set} or {@code add} operations. This provides fail-fast behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.
  Use of this field by subclasses is optional. If a subclass wishes to provide fail-fast iterators (and list iterators), then it merely has to increment this field in its {@code add(int, E)} and {@code remove(int)} methods (and any other methods that it overrides that result in structural modifications to the list). A single call to {@code add(int, E)} or {@code remove(int)} must add no more than one to this field, or the iterators (and list iterators) will throw bogus {@code ConcurrentModificationExceptions}. If an implementation does not wish to provide fail-fast iterators, this field may be ignored.

  1. 接下来,比较当前容量乘以1.5(如果是小数则采用去尾法取近似值,保留整数部分)和所需最小容量minCapacity的大小关系,新容量newCapacity被定为二者中的较大者。

  2. 接下来,检查新容量newCapacity是否超过了Integer.MAX_VALUE - 8。

  ArrayList是使用数组来存储数据的,Java中数组长度属性length的变量类型是int,因此数组最大长度就是int类型的最大值,减去8是因为部分虚拟机会在数组中保留一些header信息,要为这些header信息留出空间。

  1. 如果没超过则按照新容量newCapacity进行扩容,如果超过了,再检查所需最小容量minCapacity是否超过了Integer.MAX_VALUE - 8。

  2. 如果所需最小容量minCapacity也超过了Integer.MAX_VALUE - 8,则扩容为Integer.MAX_VALUE(不再为部分虚拟机预留空间),没超过则扩容为Integer.MAX_VALUE - 8。

    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);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

  在新容量newCapacity超过了Integer.MAX_VALUE - 8的情况下,为什么不直接扩容为Integer.MAX_VALUE - 8,而是再拿minCapacityInteger.MAX_VALUE - 8作比较?

  个人理解,这样多比较一步可以增加ArrayList的可用率。极限情况下,如果minCapacity恰好介于Integer.MAX_VALUE - 8和Integer.MAX_VALUE之间,则扩容为Integer.MAX_VALUE,这样可以满足本次add或addAll的需求,也就是可以正常添加元素进去而不报错;如果没有这一步比较,则扩容为Integer.MAX_VALUE - 8,这个容量不能满足本次add或addAll的要求。

  1. 最终,一个ArrayList的最大长度会停留在Integer.MAX_VALUE,元素超过这个容量就会报OutOfMemoryError。

  何时会报OutOfMemoryError?

  ① 元素数量超过Integer.MAX_VALUE时,必定会报OutOfMemoryError。

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

  ② 元素数量没超过Integer.MAX_VALUE时,如果虚拟机存储header占用的空间加上ArrayList本身存储元素占用的空间超过了Integer.MAX_VALUE也会报OutOfMemoryError。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值