JDK中ArrayList的实现分析

来源:互联网 发布:加密算法java 编辑:程序博客网 时间:2024/05/22 04:32

JDK中ArrayList的实现分析

第一次使用Markdown写博客,使用MarkDownPad 2先写好拷贝过来的。

查看Android中的JDK源码ArrayList的实现.

JDK中的List类族,参考《Java程序性能优化》3.2.1节:

这里写图片描述

ArrayList继承自AbstractList,后者是一个抽象类,又继承自AbstractCollection,并实现了List接口。

public abstract class AbstractList<Eextends AbstractCollection<Eimplements List<E>

查看源码注释:

AbstractList is an abstract implementation of the List interface, optimized
for a backing store which supports random access. This implementation does
not support adding or replacing. A subclass must implement the abstract
methods get() and size(), and to create a
modifiable List it’s necessary to override the add() method that
currently throws an UnsupportedOperationException.

可以知道,AbstractList支持随机访问,但是并不支持添加和替换,它的子类必须实现get()和size()这两个抽象方法来创建一个可以修改的List。另外要复写add()方法,因为在AbstractList中,该方法仅仅是抛出了一个UnsupportedOperationException异常。

public void add(int location, E object) {     throw new UnsupportedOperationException();  }  

AbstractList里面定义了两个内部类SimpleListIterator和FullListIterator,前者可以从List第一个元素开始遍历,而后者继承自前者,可以实现从指定位置开始遍历,AbstractList里面的访问都是通过这两个迭代器实现的。

回头再来看ArrayList的实现。
先看注释:

ArrayList is an implementation of List, backed by an array.
All optional operations including adding, removing, and replacing elements are supported.
All elements are permitted, including null.
This class is a good choice as your default List implementation.
Vector synchronizes all operations, but not necessarily in a way that’s meaningful to your application: synchronizing each call to get, for example, is not equivalent to synchronizing the list and iterating over it (which is probably what you intended).
java.util.concurrent.CopyOnWriteArrayList is intended for the special case of very high
concurrency, frequent traversals, and very rare mutations.

ArrayList是List的一个基于数组的实现。它支持添加,删除以及替换原始这些所有的操作。包括null在内的所有元素都是允许的。
这个类是你实现List的一个好的选择。Vector类对所有操作进行了同步化,但是可能在你的程序中不是十分必要:比如同步化每一次get的调用,不等于同步化整个list以及它的遍历(这才可能是你希望的)。
java.util.concurrent.CopyOnWriteArrayList用在高并发,经常遍历以及很少变化的情况下。

可以知道Vector和CopyOnWriteArrayList是两个线程安全的List的实现,而ArrayList不是线程安全的。并发List的讲解可以参考《Java程序性能优化》一书的4.3节“JDK并发数据结构”中的“并发List”小节。

又跑题了。
ArrayList支持随机访问,并使用数组实现,其中定义一个容量增长的最小值:

private static final int MIN_CAPACITY_INCREMENT = 12;

这个值是时间和空间上的一个权衡。这就表示虽然基于数组实现,但是容量并不一定要指定,必要时候容量可以自动扩充。

毫无疑问,类里面会定义一个数组:

transient Object[] array;

这个数组用于存储元素,元素值当然可以为null,另外也支持泛型,可以存储不同类型的元素。

如果我们指定了构造函数中的容量,array就会根据容量进行初始化,当然如果容量小于0就会抛出IllegalArgumentException异常:

array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);

EmptyArray.OBJECT是定义的一个容量为0的对象。

ArrarList还实现了其他的构造函数:

public ArrayList(Collection<? extends E> collection) 

该函数可以从一个集合中进行初始化,拷贝它的元素。

我们在ArrayList中添加元素的时候,调用add()方法,下面看看是怎么实现的。

@Override public boolean add(E object) {    Object[] a = array;    int s = size;    if (s == a.length) {        Object[] newArray = new Object[s +                (s < (MIN_CAPACITY_INCREMENT / 2) ?                 MIN_CAPACITY_INCREMENT : s >> 1)];        System.arraycopy(a, 0, newArray, 0, s);        array = a = newArray;    }    a[s] = object;    size = s + 1;    modCount++;    return true;}

当要添加元素object的时候,首先定义array的一个引用,判断一下元素个数是否等于数组的长度,即元素是否已经填满了数组,如果填满了,就没有多余的空间存储新的元素,所以此时要进行扩容。
扩容算法是这样的:
如果元素个数比MIN_CAPACITY_INCREMENT / 2小,就扩充MIN_CAPACITY_INCREMENT大小的空间,反之扩充元素个数一半的空间,用新的大小定义一个新的数组,将旧的数组的元素拷贝进去,然后将array的引用转移到新的数组,最后更新元素个数等信息。

如果要在指定位置添加元素,实现方式就不一样了。

@Override public void add(int index, E object) {    Object[] a = array;    int s = size;    if (index > s || index < 0) {        throwIndexOutOfBoundsException(index, s);    }    if (s < a.length) {        System.arraycopy(a, index, a, index + 1, s - index);    } else {        // assert s == a.length;        Object[] newArray = new Object[newCapacity(s)];        System.arraycopy(a, 0, newArray, 0, index);        System.arraycopy(a, index, newArray, index + 1, s - index);        array = a = newArray;    }    a[index] = object;    size = s + 1;    modCount++;}

首先判断位置是否超出边界,超出就抛出throwIndexOutOfBoundsException异常。然后如果数组空间足够,将插入位置以后的元素整体向后挪动一个单位,否则先用前面的算法扩容,然后整体挪动。
数组整体搬移一个单位的操作还是比较消耗性能的,尤其是大量存在插入操作的时候,这个时候可以考虑性能更好的LinkedListArray,它基于链表,我们下次再分析它的实现。

当要remove一个位置的元素时,源码是这样的:

@Override public E remove(int index) {    Object[] a = array;    int s = size;    if (index >= s) {        throwIndexOutOfBoundsException(index, s);    }    @SuppressWarnings("unchecked") E result = (E) a[index];    System.arraycopy(a, index + 1, a, index, --s - index);    a[s] = null;  // Prevent memory leak    size = s;    modCount++;    return result;}

很简单,直接将指定位置元素的数组整体前移一个单位,这样就将要删除的元素覆盖掉了。

如果要remove某个对象元素时,remove(Object object)函数需要先判断object是否为null,再分为两种情况处理。如果不是null,就寻找满足object.equals(a[i])的位置,进行整体前移的操作;如果为null,则直接查找第一个为a[i] == null的地方,整体前移。

ArrayList其他的操作包括contains()、indexOf()等都比较好理解,就是简单的数组遍历和比较,就不再赘述了。

到此为止,我们基本上了解了ArrayList的实现。

0 0
原创粉丝点击