List接口用于存放多个元素,能够维护元素的次序,并且允许元素的重复,一共有三个实现类:LinkedList、ArrayList、Vector和LinkedList。其中LinkedList使用的是双向链表结构,增删元素快,查找慢;ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长;Vector是一个矢量队列,它是JDK1.0版本添加的类。
ArrayList与Vector相对LinkedList来说都是查找元素快,增删元素慢,前两者都是利用数组实现的,本质上都是动态数组,相似性较大。所以对于List这三个实现类的区别,将从LinkedList和ArrayList性能差异分析与Vector与ArrayList的区别来探讨。
一、LinkedList和ArrayList区别分析
在实现List类时,LinkedList与ArrayList有种互补的既视感,前者增删元素快,后者查找元素快,这里从源码来思考这两个类的性能区别。
1.增删元素区别
LinkedList.java中向指定位置插入元素的代码如下:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
void linkBefore(E e, Node succ) {
// assert succ != null;
final Node pred = succ.prev;
final Node newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点,插入节点后无需进行其余额外操作。同时使用add插入节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。
ArrayList.java中向指定位置插入元素的代码如下
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
与数组动态扩容类似,增删元素时,先确认ArrayList的容量再进行删减,若容量不够,则增加容量。
注意调用的一个方法: 这个方法是移动index之后所有元素。这就意味着,ArrayList的add(int index, E element)函数,会引起index之后所有元素的改变,这也是LinkedList中插入元素很快,而ArrayList中插入元素很慢的原因了。
2.查找元素性能区别
相对于ArrayList,LinkedList中查找元素(随机访问)的速度很慢
LinkedList随机访问的代码:
// 返回LinkedList指定位置的元素
public E get(int index) {
return entry(index).element;
}
// 获取双向链表中指定位置的节点
private Entry entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry e = header;
// 获取index处的节点。
// 若index < 双向链表长度的1/2,则从前先后查找;
// 否则,从后向前查找。
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
ArrayList随机访问的代码 :
// 获取index位置的元素值
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
}
再ArrayList随机查找数据元素的时候,当查找数据元素索引小于数组长度的时候,只需要直接return数据元素就好,反观LinkedList,即使利用二分法排除掉一半的数据,还需要利用for循环通过现有节点来访问下一个或者上一个节点的数据来进行查找。
二、Vector和ArrayList比较
Vector和ArrayList相似之处有他们都继承于AbstractList;都实现了RandomAccess接口与Cloneable接口;它们都是通过数组实现的,本质上都是动态数组;它们都支持Iterator和listIterator遍历
主要不同之处有以下几个方面:
1.线程安全性不同
vector是线程(Thread)同步(Synchronized)的,所以它也是线程安全的,而Arraylist是线程异步(ASynchronized)的,是不安全的。ArrayList适用于单线程,Vector适用于多线程。但是由于vector是线程同步的,导致它比ArrayList效率要地上很多
由于只有一条线程能进入到已同步的逻辑当中,在使用同步时,因为需要从主存当中重新获取主存中对象最新的信息,然后对其修改,提交回主存当中,而且申请锁是要会占用系统资源,导致同步操作效率低的情况发生。
2 对序列化支持不同
ArrayList支持序列化,而Vector不支持;即ArrayList有实现java.io.Serializable接口,而Vector没有实现该接口。
3 构造函数个数不同
ArrayList有3个构造函数,而Vector有4个构造函数。Vector除了包括和ArrayList类似的3个构造函数之外,另外的一个构造函数可以指定容量增加系数。
ArrayList构造函数如下:
// 默认构造函数
ArrayList()
// capacity是ArrayList的默认容量大小。当由于增加数据导致容量不足时,容量会添加上一次容量大小的一半。
ArrayList(int capacity)
// 创建一个包含collection的ArrayList
ArrayList(Collection collection)
Vector构造函数如下:
// 默认构造函数
Vector()
// capacity是Vector的默认容量大小。当由于增加数据导致容量增加时,每次容量会增加一倍。
Vector(int capacity)
// 创建一个包含collection的Vector
Vector(Collection collection)
// capacity是Vector的默认容量大小,capacityIncrement是每次Vector容量增加时的增量值。
Vector(int capacity, int capacityIncrement)
4 容量增加方式不同
如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
ArrayList增长函数:
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
Vector增长函数:
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
if (minCapacity > elementData.length)
grow(minCapacity);
}
}