Java教程

ArrayList扩容过程

本文主要是介绍ArrayList扩容过程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

在研究ArrayList扩容过程之前,先看下ArrayList的几个参数和构造函数:

// 默认的list的长度10
private static final int DEFAULT_CAPACITY = 10;

// 空实例的共享空数组实例(有参构造函数使用),相比jdk1.7,这里是1.8版本优化,减少空数组产生
private static final Object[] EMPTY_ELEMENTDATA = {};

// 空实例的非共享数组(只有无参构造函数使用)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 实际存储数据的对象
transient Object[] elementData;

// 数组长度
private int size;

无参构造:

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

有参构造函数1:

    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、初始长度大于0时,直接初始化一个当前长度的数组;

2、初始长度为0时,初始化一个空数组;

3、初始长度小于0时,抛出异常;

有参构造函数2:

    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;
        }
    }

传参为一个集合对象,直接将传参对象转换为数组,并把引用赋值给elementData数组。

1、数组对象长度大于0时,根据传参对象构造一个数组;

2、如果传参对象集合对象长度为0,则直接把空对象EMPTY_ELEMENTDATA赋值给elementData。

下面进入正题:

重点分析add(E e)函数(扩容就发生在新增接口哦中)

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

重点阅读方法:

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

该方法将原数组长度加一作为扩容后的最小长度

如果是第一次向list中添加元素,则最小会把数组扩容为默认值10,(原来数组长度小于10),

通过Math.max(DEFAULT_CAPACITY, minCapacity)取DEFAULT_CAPACITY和minCapacity的最大值。
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

这段代码特意加上了注释,其实下面那步是为了防止溢出的处理。

    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);
    }

这段主要是为了防止数组长度超过数组默认的长度

Integer.MAX_VALUE - 8;

这里还有个有意思的地方:

@Native public static final int   MAX_VALUE = 0x7fffffff;

默认的Integer的最大长度为2的31次-1,但是java限制的最大长度还要再减去8,其中在最长数组变量上有这么一句注释:

Some VMs reserve some header words in an array.

大概意思是某些VMs在数组前面会有标记长度的标记,所以减去的8为就是存这些东西了。

所以java(1.8)的意思大概是:我们规定数组的长度就是这么长啊,如果你一定要超过最长我也让你设,你自己心里有数就行(别说我非要扣掉你一些啊)。

最后就是把旧的数组里的元素赋值到新的元素,整个扩容过程结束。

这篇关于ArrayList扩容过程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!