ArrayList、LinkedList和Vector的異同點

一、相同點

首先我們要知道,不管是ArrayList,LinkedList還是Vestor他們都是java.util.List接口的實現類。換而言之他們三個就是具有List特徵的集合,List集合的特點就是他們三個的相同點

我們通過查詢API可以的到List接口的描述:

有序的 collection(也稱爲序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。

我們可以將其簡略的總結爲:List是有序的,元素可重複的集合。請注意這裏的有序指的是元素存入集合的順序和取出的順序一致。


二、不同點

我將從源碼中爲大家簡單講解他們的特點讓大家能更加直觀的認識到他們的不同之處


1.ArrayList

在源碼中我們首先可以看到

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
也就是說ArrayList是AbstractList同時是支持泛型的。


在大多數情況下我們通常使用 ArrayList的構造方法來創建一個ArrayList對象

在JAVA中ArrayList提供了三個構造方法分別是

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

    public ArrayList() {
        this.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;
        }
    }


在這裏我們僅拿無參構造進行分析

/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; 

   private int size;
    ArrayList定義只定義類兩個私有屬性:elementData和size

    elementData存儲ArrayList內的元素,size表示它包含的元素的數量

    當我們在給ArryList 放入元素時ArryList 會數組的大小自動對數組進行擴容具體過程如下

//添加元素
 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//擴容長度0.5倍
        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);//數組擴容
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
 

   從這裏我們可以看出,ArrayList實際上是使用數組在存儲數據的,也就是說ArrayList具有數組的特性。同時每次擴容0.5倍

    簡單來說,ArrayLis是實現了基於動態數組的數據結構,由於使用索引從指定的位置(用index)檢索一個對象,他的查詢效率很高,由於在非末尾插入和刪除時需要重新移動數據,所以他的刪除或者插入的效率比較低。每次自增0.5倍容量

2.Vector

我們還是先看看Vector的構造方法

  public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    public Vector() {
        this(10);
    }

可以看得出來,Vector如ArrayList一樣初始化了一個Object數組用於存放數據同時。規定了如果向量的大小大於其容量時,容量自動增加的量。

那麼Vector和ArrayList都是使用數組來存放數據,他們又有什麼區別呢。

繼續翻看源碼,可以發現,Vector和ArrayList都有很多相同名稱的方法但是存在一定的區別,以add()方法舉例,對比如下

 //Vector
public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }


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

我們可以發現Vector的方法是加鎖的,這保證了他可以保證他的線程是安全的,與此相對的他的效率會比不加鎖的ArrayList相對遜色。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
可以看出Vector每次自增一倍的容量

由於Vector具有ArrayList的所有功能,所以在一般情況下我們可以把Vector當做線程安全的ArrarList使用,每次擴容增大一倍容量,效率低於ArrayList.

3.LinkedList

LinkedList可以直譯爲鏈表,顧名思義LinkedList一定是和鏈表脫不開關係的

在API中對LinkedList是這樣描述的:List 接口的鏈接列表實現。實現所有可選的列表操作,並且允許所有元素(包括 null)。除了實現 List 接口外,LinkedList 類還爲在列表的開頭及結尾 getremoveinsert 元素提供了統一的命名方法。這些操作允許將鏈接列表用作堆棧、隊列雙端隊列

事實上LinkedList是一個雙向的鏈表,那麼他肯定會具備鏈表的特點

在插入、刪除集合中任何位置的元素所花費的時間都是一樣的,但它在索引一個元素的時候比較慢。

    transient Node<E> first;

    transient Node<E> last;
我們用add和get方法舉例簡單的瞭解一下LinkedList的工作原理

    public boolean add(E e) {
        linkLast(e);
        return true;
    }
 void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
LinkedList的工作原理並不是該文重點所以在此不再贅述了。我們需要知道的是由於LinkedList是一個雙鏈表,所以如果我想得到他裏面的一個元素我需要一個個的去找,當我需要對這個插入和刪除一個元素的時候只需要修改他的節點而不需要對數據進行挪動。

由此我們可以總結出LinkedList的特點:LinkedLis是實現了基於雙鏈表的數據結構,在插入、刪除集合中任何位置的元素所花費的時間都是一樣的,但它在索引一個元素的時候比較慢。

還有一點,由於LinkedList實現了 Deque 接口,爲 addpoll 提供先進先出隊列操作,以及其他堆棧和雙端隊列操作。我們也可以用LinkedList來模擬隊列和棧。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章