架构社区SSM社区SSH社区

ArrayList核心源码解析

2018-04-25  本文已影响16人  慕凌峰

一、基本方法解析

1、构造器

ArrayList有三种创建方式

ArrayList的底层实现方式为数组,在创建ArrayList时,底层会创建数组对象Object[]

1.1 无参构造器


private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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

通过这种方式创建ArrayList,底层对创建一个空的数据,这个数组对象是 static final 类型的,即该数组对象在初始化的是否就会被创建,并且不能够被改变。

1.2 自带数组长度的的构造方式

private static final Object[] EMPTY_ELEMENTDATA = {};

transient Object[] 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);
    }
}

不难看出,这种方式在创建ArrayList对象时,会进行一系列的判断,根据你传入的参数不同,进行不同的构造方式。

这里要特别注意的是,elementData数组对象被transient所描述,因为Arraylist实现了java.io.Serializable接口,即在序列化的时候,Arraylist会使用Serializable的默认序列化方式,而被transient关键字修饰的属性会在序列化时被忽略,不进行序列化。

1.3 传入一个集合Collection对象,构造ArrayList对象

transient Object[] elementData; 

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

2、元素的添加:add()、addAll()

Arraylist提供了四种的添加新增元素的方式,两种add(),以及两种addAll()。

2.1 直接在集合的结尾添加元素:add(E e)

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

在这个过程中,会做一下的操作:

核心代码1:


private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 判断当前集合的属性elementData是否为空
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // DEFAULT_CAPACITY 默认为 10,取较大的一个值
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 判断新的集合长度与数组长度的大小
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

核心代码2:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
    // 获取当前的数组长度
    int oldCapacity = elementData.length;
    // 新的数组长度为:原来的数组长度 + 原来数组长度的1/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 比较新的数组长度与新的集合长度的大小
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 比较新的数组长度与jvm所允许的最大数组长度    
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 创建一个新的数组,将原来的数组信息添加进去,然后再赋值给elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}

2.2 将某个元素添加到集合的指定位置

public void add(int index, E element) {
    // 首先判断当前传入的位置是否合法,即在0-size之间
    rangeCheckForAdd(index);

    // 判断是否需要添加数组长度,此处的操作同上add()中的操作
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 用System.arraycopy()方法将数组index-1之后的所有数据往后移动一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 在index的位置加入新的元素
    elementData[index] = element;
    // 更新size
    size++;
}

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

基本错作流程如下:

2.3 将整个Collection对象中所有的元素添加到ArrayList中

public boolean addAll(Collection<? extends E> c) {
    // 通过toArray()方法将Collection转化为数组a
    Object[] a = c.toArray();
    // 获取传入的集合的长度
    int numNew = a.length;
    // 把 size + numNew 作为新的集合长度,进行数组的处理,同add()方法中的操作
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 使用System.arraycopy()方法将传入的数据信息copy到 elementData 中
    System.arraycopy(a, 0, elementData, size, numNew);
    // 更新size
    size += numNew;
    return numNew != 0;
}

基本操作流程如下:

2.4 将整个Collection对性中的元素从特定位置开始全部加入到Arraylist

public boolean addAll(int index, Collection<? extends E> c) {
    // 首先判断当前传入的位置是否合法,即在0-size之间
    rangeCheckForAdd(index);
    // 通过toArray()方法将Collection转化为数组a
    Object[] a = c.toArray();
    // 获取传入的集合的长度
    int numNew = a.length;
    // 把 size + numNew 作为新的集合长度,进行数组的处理,同add()方法中的操作
    ensureCapacityInternal(size + numNew);  // Increments modCount

    // 通过System.arraycopy()方法将原本elementData中index-1之后的所有元素向后移动 numNew位
    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    // 通过System.arraycopy()方法将传入数组中的数据添加到elementData中
    System.arraycopy(a, 0, elementData, index, numNew);
    // 跟新size的值
    size += numNew;
    return numNew != 0;
}

基本操作流程如下:

上一篇 下一篇

猜你喜欢

热点阅读