List接口是Java中经常用到的接口,如果对具体的List实现类的特性不了解的话,可能会导致程序性能的下降,下面从原理上简单的介绍List的具体实现:
可以看到,List继承了Collection接口,而Collection接口继承了Iterable接口。其中还有AbstractCollection和AbstractList的实现,用于List对象的公共部分代码的复用:
public interface List extends Collection{//Query Operations
public interface Collection extends Iterable{//Query Operations
public abstract class AbstractList extends AbstractCollection implements List{/*** Sole constructor. (For invocation by subclass constructors, typically
* implicit.)*/
protectedAbstractList() {
}
public abstract class AbstractCollection implements Collection{/*** Sole constructor. (For invocation by subclass constructors, typically
* implicit.)*/
protectedAbstractCollection() {
}
具体这三个接口定义的方法如下:
现在来看看List接口的具体特性:
1、Vector
Vector类是通过数组实现的,支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它的效率不是很高。
现在来看一下Vector的成员变量和其中部分的构造方法:
public class Vector
extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{/*** The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
*
Any array elements following the last element in the Vector are null.
*
*@serial
*/
protectedObject[] elementData;/*** The number of valid components in this {@codeVector} object.
* Components {@codeelementData[0]} through
* {@codeelementData[elementCount-1]} are the actual items.
*
*@serial
*/
protected intelementCount;/*** The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
*@serial
*/
protected intcapacityIncrement;/**use serialVersionUID from JDK 1.0.2 for interoperability*/
private static final long serialVersionUID = -2767605614048989439L;/*** Constructs an empty vector with the specified initial capacity and
* capacity increment.
*
*@paraminitialCapacity the initial capacity of the vector
*@paramcapacityIncrement the amount by which the capacity is
* increased when the vector overflows
*@throwsIllegalArgumentException if the specified initial capacity
* is negative*/
public Vector(int initialCapacity, intcapacityIncrement) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = newObject[initialCapacity];this.capacityIncrement =capacityIncrement;
}
对数据结构有一定了解的人都知道,使用数组作为存储底层,当存储空间不够了的时候,是会重新分配一块更大的内存空间,然后把原空间的数据全部都copy过去,最后释放原空间,这样会导致程序性能的下降,或者是GC的消耗(Vector是默认扩展1倍)。所以我们对处理的数据量要有一定的预估,初始化Vector的时候指定容量大小。由于对Vector所有动作都添加synchronized关键字,同样会导致执行的性能下降。
下面是对Vector添加元素的时候的代码:
/*** Appends the specified element to the end of this Vector.
*
*@parame element to be appended to this Vector
*@return{@codetrue} (as specified by {@linkCollection#add})
*@since1.2*/
public synchronized booleanadd(E e) {
modCount++;
ensureCapacityHelper(elementCount+ 1);
elementData[elementCount++] =e;return true;
}
/*** This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
*@see#ensureCapacity(int)*/
private void ensureCapacityHelper(intminCapacity) {int oldCapacity =elementData.length;if (minCapacity >oldCapacity) {
Object[] oldData=elementData;int newCapacity = (capacityIncrement > 0) ?(oldCapacity+ capacityIncrement) : (oldCapacity * 2);if (newCapacity
newCapacity=minCapacity;
}
elementData=Arrays.copyOf(elementData, newCapacity);
}
}
2、Stack
Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展,同样是线程安全的 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。当操作的集合不存在元素的时候,抛出EmptyStackException。
public
class Stack extends Vector{/*** Creates an empty Stack.*/
publicStack() {
}
3、ArrayList
同样,ArrayList内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高(ArrayList在内存不够时默认是扩展50% + 1个)。因此,它适合随机查找和遍历,不适合插入和删除。由于没有添加同步,所以是非线程安全的,执行效率也要比Vector高很多
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{private static final long serialVersionUID = 8683452581122892189L;/*** The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.*/
private transientObject[] elementData;/*** The size of the ArrayList (the number of elements it contains).
*
*@serial
*/
private intsize;/*** Constructs an empty list with the specified initial capacity.
*
*@paraminitialCapacity the initial capacity of the list
*@exceptionIllegalArgumentException if the specified initial capacity
* is negative*/
public ArrayList(intinitialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = newObject[initialCapacity];
}
public void ensureCapacity(intminCapacity) {
modCount++;int oldCapacity =elementData.length;if (minCapacity >oldCapacity) {
Object oldData[]=elementData;int newCapacity = (oldCapacity * 3)/2 + 1;if (newCapacity
newCapacity=minCapacity;//minCapacity is usually close to size, so this is a win:
elementData =Arrays.copyOf(elementData, newCapacity);
}
}
4、LinkedList
LinkedList是用双向链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
{private transient Entry header = new Entry(null, null, null);private transient int size = 0;/*** Constructs an empty list.*/
publicLinkedList() {
header.next= header.previous =header;
}
Entry(E element, Entry next, Entryprevious) {this.element =element;this.next =next;this.previous =previous;
}
}
总结:根据不同实现类的特性,选择对应的数据结构,能提高程序的运行效率 。需要注意的是,一条线程在进行list列表元素迭代的时候,另外一条线程如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素,会发生ConcurrentModificationException并发修改异常。当然后来版本的CopyOnWrite容器解决了该问题,后续会介绍。