ArrayList源码解析

一直在理论层面学习,今天来解析一下ArrayList的源码(JDK1.8):

首先ArrayList是放在java.util包下的,是一个最简单的线性结构

打开之后发现

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

ArrayList继承了AbstractList<E>抽象类(从名字看出的抽象类)

然后分别实现了List<E>, RandomAccess, Cloneable, java.io.Serializable接口,我们可以看出List<E>和AbstractList<E>接口是有上下级关系的,本着追根溯源的精神,我们把其他三个接口也看看源码长什么样

 

RandomAccess:从名字我们可以看出这是随机读取接口,实现了它应该就标记着该类支持随机存取,那么我们可以就来看一下它的源码:

public interface RandomAccess {
}

这是一个空接口,看来又是一个标记接口,那么标记了它有什么用呢?

答案是在Collections下的binarySearch()方法以及其他查找方法中如果发现实现了RandomAccess接口就会进行二分查找,不实现就用迭代器进行查找,简单来说就是标记了RandomAccess方便了对其进行查找的方法进行优化.

Cloneable:我们知道这是一个典型的标记接口,标记该类可以克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。那么我们猜测它也是一个空接口,我们来查看一下源码:

public interface Cloneable {
}

果然也是一个空接口,接下来我们查看一下Serializable接口:

Serializable:这也是一个典型的标记接口,实现了这个接口标记该类可以序列化,我们仍然猜测这是一个空接口:

public interface Serializable {
}

果然不出所料,从上面我们知道了ArrayList实现了三个接口以表示ArrayList是支持随机访问的,可以克隆,可以序列化的.接下来我们来看看与它密切相关的List<E>,和Abstract<E>:

我们先来看List<E>接口,因为我们知道它一定比Abstract<E>层级高,并且很可能Abstract<E>继承自它:

public interface List<E> extends Collection<E> {

int size();

boolean isEmpty();

boolean contains(Object o);

Iterator<E> iterator();

Object[] toArray();

<T> T[] toArray(T[] a);

boolean add(E e);

boolean remove(Object o);

boolean containsAll(Collection<?> c);

boolean addAll(Collection<? extends E> c);

boolean addAll(int index, Collection<? extends E> c);

boolean removeAll(Collection<?> c);

boolean retainAll(Collection<?> c);

default void replaceAll(UnaryOperator<E> operator) {
    Objects.requireNonNull(operator);
    final ListIterator<E> li = this.listIterator();
    while (li.hasNext()) {
        li.set(operator.apply(li.next()));
    }
}

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

void clear();

boolean equals(Object o);

int hashCode();

E get(int index);

E set(int index, E element);

void add(int index, E element);

E remove(int index);

int indexOf(Object o);

int lastIndexOf(Object o);

ListIterator<E> listIterator();

ListIterator<E> listIterator(int index);

List<E> subList(int fromIndex, int toIndex);

default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.ORDERED);
    }
}

我们看到List<E>接口中定义了List的常用的方法和一些JDK8可以在接口中定义的默认方法,从名字我们很容易知道它们所代表的意思,下面我们来看一下Abstract<E>接口:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

protected AbstractList() {
}

public boolean add(E e) {
    add(size(), e);
    return true;
}

abstract public E get(int index);

public E set(int index, E element) {
    throw new UnsupportedOperationException();
}

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

public E remove(int index) {
    throw new UnsupportedOperationException();
}

public int indexOf(Object o) {
    ListIterator<E> it = listIterator();
    if (o==null) {
        while (it.hasNext())
            if (it.next()==null)
                return it.previousIndex();
    } else {
        while (it.hasNext())
            if (o.equals(it.next()))
                return it.previousIndex();
    }
    return -1;
}

public int lastIndexOf(Object o) {
    ListIterator<E> it = listIterator(size());
    if (o==null) {
        while (it.hasPrevious())
            if (it.previous()==null)
                return it.nextIndex();
    } else {
        while (it.hasPrevious())
            if (o.equals(it.previous()))
                return it.nextIndex();
    }
    return -1;
}

public void clear() {
    removeRange(0, size());
}

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);
    boolean modified = false;
    for (E e : c) {
        add(index++, e);
        modified = true;
    }
    return modified;
}

public Iterator<E> iterator() {
    return new Itr();
}

public ListIterator<E> listIterator() {
    return listIterator(0);
}

public ListIterator<E> listIterator(final int index) {
    rangeCheckForAdd(index);

    return new ListItr(index);
}

public List<E> subList(int fromIndex, int toIndex) {
    return (this instanceof RandomAccess ?
            new RandomAccessSubList<>(this, fromIndex, toIndex) :
            new SubList<>(this, fromIndex, toIndex));
}

public boolean equals(Object o) {
    if (o == this)
        return true;
    if (!(o instanceof List))
        return false;

    ListIterator<E> e1 = listIterator();
    ListIterator<?> e2 = ((List<?>) o).listIterator();
    while (e1.hasNext() && e2.hasNext()) {
        E o1 = e1.next();
        Object o2 = e2.next();
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    return !(e1.hasNext() || e2.hasNext());
}

public int hashCode() {
    int hashCode = 1;
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

protected void removeRange(int fromIndex, int toIndex) {
    ListIterator<E> it = listIterator(fromIndex);
    for (int i=0, n=toIndex-fromIndex; i<n; i++) {
        it.next();
        it.remove();
    }
}

protected transient int modCount = 0;

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

    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size();
    }
}

里面还包含了两个内部类Itr和ListItr,由于篇幅就不黏贴了,我们可以看到AbstractList类对List<E>中的大部分方法进行了实现,其中我只发现了一个abstract方法get方法,也因为这个方法是abstract的所以类要加上abstract.

接下来我们真正来看ArrayList的源码:

private static final long serialVersionUID = 8683452581122892189L;

序列化编号,没什么好说的,控制反序列化与序列化一致的

private static final int DEFAULT_CAPACITY = 10;

 

从名字我们可以看出这是声明创建一个ArrayList的默认容量是10

private static final Object[] EMPTY_ELEMENTDATA = {};

这条非常重要!定义了一个空集合,从侧面反映了ArrayList是用集合来存储数据的!

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

这里又声明了一个默认容量时的空数组,暂时不明白有什么用,等看到下面是否有发现吧.

transient Object[] elementData; // non-private to simplify nested class access

这里是真正的ArrayList的底层数据接口,就是Object[]数组,用transient修饰,嗯.

嗯?不对!transient修饰的变量不可被序列化吗?ArrayList不是实现了Serializable接口吗?再说了,ArrayList不可被序列化可还行?

在经过一番百度之后,对transient有了更深的认识:

transient关键字的作用:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。

原来:

Transient只保证在使用java默认序列化机制的时候不序列化它修饰的变量,但对用户自定义的序列化是没有效果的,我们看ArrayList的最后就会发现ArrayList实现了

private void writeObject(java.io.ObjectOutputStream s)

private void readObject(java.io.ObjectInputStream s)

这两个方法,便可以用自己的方式进行序列化,那么ArrayList为什么不用java默认的序列化机制呢?

原因在于ArrayLsit是一个缓冲结构,如果ArrayList当前容量为10,但你只存储了6个数据,那么剩余的4个还用不用存储呢?显然,java默认的序列化机制将会一一进行序列化,而自定义序列化过程将会优化这个过程.

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

终于读到了ArrayList的构造器,在无参进行构造的时候elementData被赋值为默认容量的空集合(DEFAULTCAPACITY_EMPTY_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);
    }
}

这里用一个int参数来进行构造,当值大于0的时候构建相等值的Object[]数组,当值等于0的时候elementData = EMPTY_ELEMENTDATA,当值小于0的时候当然抛出异常啦,这里我们仍然没有发现EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_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;
    }
}

用另外一个集合对象进行构造ArrayList,我们看到toArray()方法很自然地想到会返回一个数组,但是真的是这样吗?我们来验证一下:

我们先来创建一个interfaceA:

public interface interfaceA {
    Object[] toArray();
}

用一个类去实现它:

public class Impla implements interfaceA{
    @Override
    public ArrayList[] toArray() {
        return new ArrayList[0];
    }
}

在IDE中是没有报错的,原来重写父类方法的时候返回方法也是可以用子类的,所以到这里,toArray方法返回的不一定是Object[]数组,而有可能是子类的数组,我们凭直觉感觉这样不好,可是,为什么不好?带着这个疑问我又百度了一波,发现了其中的道理:

如果在Object[]数组中存放的不是Object类型,然后再替换其中一个为Object元素的时候就会出错(向下转型错误,如果数组类型为Object[],那么向里面添加Object的子类是可以的),所以在下一步还是要要判断一下返回的Object[]数组中真的是Object实例吗?(这里参考的文章是https://blog.csdn.net/gulu_gulu_jp/article/details/51457492)

接下来就判断如果是Object[]实例的话就不变,如果不是的话用Arrays的copyOf进行copy回一个真正的Object[]数组,如果传入的集合长度为0的话就仍然elementData = EMPTY_ELEMENTDATA,但是我们还是没有发现EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA区别开有什么好处.

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

如果需要,增加此 ArrayList实例的容量,以确保它至少可以容纳最小容量参数指定的元素数。

参数

minCapacity - 所需的最小容量

Arraylist在add时是会根据大小自动扩容的,这个方法的意义在于你已知要存储的容量大小,直接设定之后直接扩容到你需要的大小,不需要中途几次扩容.

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

 

定义了ArrayList的最大容量为int的最大值减8,至于减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);
}

进行扩容的方法,可以看出一次以1.5倍速增长

public int size() {
    return size;
}

返回size大小

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

返回是否为空

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

返回是否包含

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

倒着找索引

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        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);
    }
}

Clone方法,注意modCount改成0

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

返回Object[]数组

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

    return elementData(index);
}

E elementData(int index) {
    return (E) elementData[index];
}

返回指定位置的元素

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

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

设置指定的元素

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

添加一个元素,当容量不够时会自动扩容

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

指定位置进行插入,可以看到插入位置后的全部元素进行复制,十分惨烈

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

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

删除指定位置的元素,与上同理

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; // clear to let GC do its work
}

Remove实际上是利用数组复制把要删除的那个元素覆盖没了

public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

Clear会把每个元素设置成null,GC(垃圾收集器)会释放它们

其他的方法不是很重要,有需要继续研究~!

另外,ArrayList不是同步的,所以在并发访问的时候,如果在迭代的同时有其他线程修改了ArrayList,迭代器会报ConcurrentModificationException异常,如果要在不并发环境下使用List,需要给List加个同步锁,List list = Collections.synchronizedList(new ArrayList(...));

Over!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值