ArrayList源码分析(JDK1.8)

简介:   

    ArrayList是动态数组,是java集合框架collection中list接口的一个实现类,继承于AbstractList。底层数据结构为一个Object数组。如下,其实现了List、RandomAccess、Cloneable、Serializable接口。

   ArrayList继承于AbstractList,实现了list接口,提供了增删改查,遍历等功能。

   ArrayList实现了java.io.Serializable接口,表示其可序列化。

   ArrayList实现了RandomAccess接口,这是一个标记接口,表明实现这个接口的List集合支持快速随机访问,查找时可以通过下标快速获取相应元素对象,时间复杂度为O(1)。与之相对的LinkedList没有实现这个接口。所以ArrayList查找元素快,插入、删除慢,LinkedList底层为双向链表,链表插入删除速度很快,只需更改其前后节点的指向即可,ArrayList在增删时对数组元素进行下标的挪动。这是两种list的性能差距。

   ArrayList实现了Cloneable接口,表示其可克隆,并覆盖了clone方法。

   ArrayList是非线程安全的,与之对应的Vector是线程安全的,两者无本质区别,Vector中会在其大部分方法中添加synchronized以确保线程安全。在多线程环境中应避免使用ArrayList,可使用vector或使用JUC里面的CopyOnWriteArrayList。

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

属性:

//默认容量为10
private static final int DEFAULT_CAPACITY = 10;

//static final常量,空数组,长度为0
private static final Object[] EMPTY_ELEMENTDATA = {};

//无参构造时会默认使用这个数组实例,与EMPTY_ELEMENTDATA区分开,以了解添加第一个元素时的膨胀程度
//空数组,会在ensureCapacity中进行判断,若使用此常量对elementData进行赋值,
//则会将elementData的扩容量至少设置为10。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//存储数据的数组,transient关键字表示在已序列化的类里,该变量不可序列化
transient Object[] elementData; // non-private to simplify nested class access

//数组所包含元素个数
private int size;

transient说明:有了transient关键字声明,则这个变量不会参与序列化操作,即使所在类实现了Serializable接口,反序列化后该变量为空值。

在参与网络传输时,ArrayList在序列化的时候会调用writeObject()方法,将sizeelement写入ObjectOutputStream;反序列化时调用readObject(),从ObjectInputStream获取sizeelement,再恢复到elementData

至于为什么不直接用elementData来序列化,原因在于elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。

构造方法:

构造方法很简单,这里只在注释中进行说明。

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        //按传入容量进行初始化
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        使用默认实例赋值
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        //传入容量小于0,抛出不合法容量的异常
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

public ArrayList() {
    //使用默认空实例进行初始化赋值
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//传入集合进行初始化,会将该集合转化为array,再逐元素进行拷贝,
//拷贝元素用到了Arrays类的copyOf静态方法
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

常用方法:

trimToSize方法:

 public void trimToSize() {
    modCount++;//操作数加1
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    //判断,为0则赋值为空数组,否则根据size重新创建数组并拷贝元素,以达到重新裁剪容量的目的
    }
}

由于扩容机制的存在,使得elementData中我们存入的元素个数size不一定等于其真实长度,所以提供了这个方法将elementData修剪,去掉多余的长度

看一下Arrays.copyOf方法:

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


public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
        return copy;
}

其原理就是生成一个新的数组,长度取传入的长度,然后将original数组的元素通过System.arraycopy方法拷贝进去,长度取original的长度与传入长度newLength中的较小值。

由于trimToSize中传入的newLength为size,elementData的长度也为size,所以此处会将一个生成的数组赋值给elementData,这个数组元素和elementData中的元素相同,长度也变回了size。

关于System.arraycopy:

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

参数解释:
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度

扩容机制:

public void ensureCapacity(int minCapacity) {//确保数组容量
    //若为使用无参构造进行赋值的elementData,即默认空数组,则最小扩容为0,否则为10
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        ? 0
       : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {//minCapacity大于数组容量,则扩容
        ensureExplicitCapacity(minCapacity);
    }
}

//计算最小容量,只在ensureCapacityInternal中使用,若是通过无参方法赋值的elementData,
//则会比较扩容量与传入的minCapacity,取较大值
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

//根据上面方法返回的数值大小,调用ensureExplicitCapacity方法,决定是否扩容
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}


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

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

//最大长度,可能会造成OOM异常
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//扩容方法
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    //右移一位等于除以二,这里可以看出ArrayList扩容机制是一点五倍扩容的
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    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;
}

取得元素个数、判空、包含、get与set:

public int size() {
        return size;
}

public boolean isEmpty() {
    return size == 0;
}

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

这三个方法比较简单,不赘述。

get与set方法:

 

取下标:

//正向遍历,取第一个相同元素下标
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;
}

//逆向遍历,取最后一个相同元素的下表
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是允许存储null的

clone方法:

public Object clone() {
    try {
        //首先调用Object的克隆方法
        ArrayList<?> v = (ArrayList<?>) super.clone();
        //将自身的elementData的所有元素拷贝到v里面,这里的元素还是同一引用,因此是浅克隆
        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);
    }
}

添加方法:

//默认添加到尾部
public boolean add(E e) {
    ensureCapacityInternal(size + 1);//确保数组长度,不够会加扩容
    elementData[size++] = e;//将size的下一号元素赋值为e
    return true;
}


//添加到指定位置
public void add(int index, E element) {
    rangeCheckForAdd(index);//检查是否下标越界

    ensureCapacityInternal(size + 1);  //同上
    
    //这是一个native方法
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);//将从index后面的数据都向后移一位
    elementData[index] = element;//给index下标元素赋值为传入值element
    size++;
}

//将制定集合的元素拷贝到elementData中
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray(); //toArray方法转化为数组
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}


//将制定集合的元素拷贝到elementData从index下标开始的位置中
public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    //toArray方法转化为数组
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  //确保长度

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);//将index之后的所有数据往后移动numMoved位

    //将数组a的所有元素拷贝到element的index开始的位置
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

    在添加元素的时候会调用ensureCapacityInternal方法,当我们采用无参构造生成ArrayList对象的时候,elementData成员会被赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA实例。此时,当我们为存入任何元素时,calculateCapacity的计算结果值为10(因为初始的size为0,size+1与10比较,10更大,还是会返回10)。在添加元素的时候,ensureCapacityInternal就会通过调用ensureExplicitCapacity方法将数组扩容为10。这就能解释为什么ArrayList的成员信息中,我们可以看到DEFAULTCAPACITY_EMPTY_ELEMENTDATA实例为一个空数组,但是注释却说其是一个长度为10的空数组了。以后直到size大于等于10了,才会再次扩容,每次扩容为原来的1.5倍

检查下标是否越界的方法rangeCheckForAdd:

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

toArray方法:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

无参的toArray可以将其自身转化为数组,带参的toArray可以将自身转化为指定类型的数组。

删除方法:

//删除指定下标的元素
public E remove(int index) {
    rangeCheck(index);//检查下标

    modCount++;//操作数加1
    
    E oldValue = elementData(index);

    int numMoved = size - index - 1;//要移动的元素个数
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);//从index+1开始将numMoved个元素向前移动一位
    elementData[--size] = null;//原最后一个元素赋空

    return oldValue;//返回删除的元素
}

//删除指定元素,删除成功返回false
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;
}

//批量删除
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 {
        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;
}

//删除指定范围的元素
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;
}

检查下标的方法rangeCheck:、

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

get与set方法:


public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

清除所有元素:

public void clear() {
    modCount++;

    for (int i = 0; i < size; i++)
        elementData[i] = null;

        size = 0;
}

保留指定元素:

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);//检测是否为空
    return batchRemove(c, true);
}

读写对象:(用于网络传输)

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    int expectedModCount = modCount;
    s.defaultWriteObject();

    s.writeInt(size);

    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;

    s.defaultReadObject();

    s.readInt(); // ignored

    if (size > 0) {
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        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();
}

 

ArrayList有四个内部类:

private class Itr implements Iterator<E>

private class ListItr extends Itr implements ListIterator<E>

private class SubList extends AbstractList<E> implements RandomAccess
    
static final class ArrayListSpliterator<E> implements Spliterator<E>    

说明:

内部迭代器Itr,ArrayList的iterator获取的迭代器就是这个内部类的对象,Itr对象是顺序迭代器对象。

 内部迭代器ListItr,Itr的子类,对其进行了扩展。

内部类SubList,表示list某个区间的所有元素集合

内部迭代器ArrayListSpliterator,支持并发迭代。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值