您的位置:首页 > 其它

ArrayList,LinkedList,Vector,Stack之间的区别

2015-05-06 17:47 337 查看
一,线程安全性

Vector、Stack:线程安全

ArrayList、LinkedList:非线程安全



二,实现方式

LinkedList:双向链表

ArrayList,Vector,Stack:数组



三,容量扩展方面

由于ArrayList和Vector(Stack继承自Vector,只在Vector的基础上添加了几个Stack相关的方法,故之后不再对Stack做特别的说明)使用数组实现,当数组长度不够时,其内部会创建一个更大的数组,然后将原数组中的数据拷贝至新数组中

//ArrayList
public boolean add(E e) {
	ensureCapacity(size + 1);
	elementData[size++] = e;
	return true;
}

public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
		//如果这次扩展不能满足要求,那就直接用minCapacity
    	if (newCapacity < minCapacity)
			newCapacity = minCapacity;
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
	}
}

如需扩展,则每次至少扩展至(原长度*3)/2 + 1

//Vector
public synchronized void addElement(E obj) {
	modCount++;
	ensureCapacityHelper(elementCount + 1);
	elementData[elementCount++] = obj;
}

private void ensureCapacityHelper(int minCapacity) {
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object[] oldData = elementData;
	    int newCapacity = (capacityIncrement > 0) ?
			(oldCapacity + capacityIncrement) : (oldCapacity * 2);
		//如果这次扩展不能满足要求,那就直接用minCapacity
    	if (newCapacity < minCapacity) {
			newCapacity = minCapacity;
	    }
        elementData = Arrays.copyOf(elementData, newCapacity);
	}
}

如果在创建Vector时不指定capacityIncrement(自动扩展长度)的值,如需扩展,则每次至少扩展至原长度的2倍



四,效率方面

这里仅仅比较ArrayList和LinkedList之间的效率差异

1,查询

ArrayList直接通过下标进行定位

//ArrayList
public E get(int index) {
	RangeCheck(index);//检查下标是否超过数组长度
	return (E) elementData[index];
}

LinkedList则需要进行遍历,平均遍历次数应为n/4

//LinkedList
public E get(int index) {
    return entry(index).element;
}
	
private Entry<E> entry(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+
                ", Size: "+size);
    Entry<E> e = header;
	//size >>1 相当于size/2
	//由于LinkedList由双向链表实现,故从离得较近的一端开始遍历更快
    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的速度远快于LinkedList

但是如果都为首尾位置的查询,情况会大为不同,因为LinkedList也是可以直接定位到首尾位置的

//LinkedList
public E getFirst() {
	if (size==0)
	    throw new NoSuchElementException();
	return header.next.element;
}

public E getLast()  {
	if (size==0)
	    throw new NoSuchElementException();
	return header.previous.element;
}

此时ArrayList和LinkedList的效率相同

2,插入

对于ArrayList,指定位置插入有可能首先需要对数组容量进行扩展,之后还有可能导致数组中的数据需要顺次移动(代码中通过数组拷贝实现,避免了数据一个一个的移动),极端情况下插入一个数据将进行两次数组拷贝

//ArrayList
public void add(int index, E element) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
				"Index: "+index+", Size: "+size);
	ensureCapacity(size+1);  //如必要,将对数组容量进行扩展
	System.arraycopy(elementData, index, elementData, index + 1,
			size - index);
	elementData[index] = element;
	size++;
}

如果不指定插入位置,则插入至数组末端,此时只需考虑可能的数组容量扩展对性能带来的影响

//ArrayList
public boolean add(E e) {
	ensureCapacity(size + 1); 
	elementData[size++] = e;
	return true;
}

由于LinkedList是由链表实现的,并没有指定位置插入的方法,即便如此,一切也显得如此美好

//LinkedList
public boolean add(E e) {
	addBefore(e, header);
        return true;
}
	
private Entry<E> addBefore(E e, Entry<E> entry) {
	Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
	newEntry.previous.next = newEntry;
	newEntry.next.previous = newEntry;
	size++;
	modCount++;
	return newEntry;
}

当然了,LinkedList可以直接将数据插入至首尾

//LinkedList
public void addFirst(E e) {
	addBefore(e, header.next);
}

public void addLast(E e) {
	addBefore(e, header);
}

总体来说,LinkedList效率高于ArrayList,即使在末尾插入,ArrayList也需要考虑可能的容量扩展对性能带来的影响

3,修改

和查询属于同一种情况

4,删除

指定位置的删除和插入属于同一种情况

除了删除指定位置数据,ArrayList和LinkedList都包含一个clear()方法用来清除所有数据

//ArrayList
public void clear() {
	modCount++;

	// Let gc do its work
	for (int i = 0; i < size; i++)
	    elementData[i] = null;
	size = 0;
}
//LinkedList
public void clear() {
    Entry<E> e = header.next;
    while (e != header) {
        Entry<E> next = e.next;
        e.next = e.previous = null;
        e.element = null;
        e = next;
    }
    header.next = header.previous = header;
    size = 0;
	modCount++;
}
由于都需要进行遍历,故效率相同
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: