源码分析之ArrayList(基于jdk12)

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

ArrayList是java容器中典型的容器,继承于AbstractList,并实现了List接口。

ArrayList是基于数组实现的,默认大小为10,如果ArrayList实例中元素的个数超过了10,便会对容器进行扩容。

 private static final int DEFAULT_CAPACITY = 10;		//数组默认大小
 
 transient Object[] elementData;						//容器中维护的数组

1.构造方法

ArrayList提供了一系列的构造方法,用的最多的:

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

eg:ArrayList<> list = new ArrayList<>();

该构造方法将容器中的数组elementData初始化;

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

该种构造方法指定了elementData的大小,如果指定容量为0,则初始化elementData为EMPTY_ELEMENTDATA(空),作用是区别于上个构造方法时指定的初始化DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默认空,没有指定的空);

另外一种构造方法将集合类型转化为ArrayList。

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

2.动态扩容机制

ArrayList支持动态扩容,当ArrayList实例的size等于了elementData时,就会进行扩容,将elementData的大小扩大到当前容量的1.5倍,做法是新创建一个数组,大小为原数组容量的1.5倍,将原数组的元素copy到新数组中。

public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            grow(minCapacity);
        }
    }

该方法用来确定数组大小是否足够(能否满足当前容量),minCapacity表示满足当前容器能接受的最小容量,如果最小容量都大于了数组的长度,那么就有必要进行扩容,即调用grow(minCapacity)。

private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

grow方法调用Arrays.copyOf()返回了一个新的elementData,大小为原数组的1.5倍,并且将原数组的元素都复制到新的数组中。

public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

其中Arrays.copyOf(elementData,newCapacity(minCapacity))的第二个参数,

newCapacity(minCapacity)返回了新数组的大小。

private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;			//原来的数组大小
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容之后的数组大小,原数组的1.5倍。
        if (newCapacity - minCapacity <= 0) {		//扩容后还不够
        		//针对没有指定容量的初始化时的数组大小的扩容
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
     
        return (newCapacity - MAX_ARRAY_SIZE <= 0)    //正常时的扩容
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

其中:MAX_ARRAY_SIZE定义为

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

为了避免有些虚拟机针对大于此值时报的OutOfMemoryError。

/**
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */

hugeCapacity(minCapacity)则是将最后的8也加进来,即将最大容量增加为Integer能表示的最大值。

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常见的add方法:

public boolean add(E e) {
        modCount++;	//arraylist变化次数
        add(e, elementData, size);  //私有add方法
        return true;
    }
private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)		//s为容器目前的大小,如果和当前数组容量相等便扩容
            elementData = grow();			//调用grow方法扩容,返回新elementData
        elementData[s] = e;						//将新add进来的元素加入到数组中
        size = s + 1;							
    }
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();    //将新元素转化为对象数组
        modCount++;
        int numNew = a.length;	//新元素长度
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        //新元素长度大于目前容器剩余长度时,扩容至当前size+新元素长度
        if (numNew > (elementData = this.elementData).length - (s = size)) 
            elementData = grow(s + numNew);
        System.arraycopy(a, 0, elementData, s, numNew);//将加入进来的元素拷贝进elementData中。
        size = s + numNew;//数组容量增加。
        return true;
    }

ArrayList常见的remove方法:

public E remove(int index) {
        Objects.checkIndex(index, size);  //判断index是否正确
        final Object[] es = elementData;  

        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index); //将被删除元素后面元素依次向前拷贝1格。

        return oldValue;
    }
private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

由此可见,元素的remove操作是很耗费时间的。

总结:

ArrayList基于数组实现,默认容量为10;
不断添加元素使得size等于数组elementData大小时便进行扩容,为原大小的1.5倍;
扩容、remove都会造成较大的开销,
前者会copy元素到新数组中,后者会调整数组中元素的位置;

建议:使用ArrayList时最好指定大小,减少扩容的开销,同时避免过多的remove操作,特别是元素较多时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值