ArrayList和LinkedList、Vector的優缺點?


一般在面試中可能會被問到ArrayList、LinkedList、Vector三者相關的區別!

一般來說我想大概都會回答如下的這些:

 

ArrayList底層是數組結構,查詢快,增刪慢,線程不安全,效率高。

LinkedList底層是鏈表數據結構,查詢慢,增刪快,線程不安全,效率高。

Vector底層是數組結構,查詢快,增刪慢,線程安全,效率低。

 

以上就是最基本的一個優缺點,但是他們的內部結構,具體怎么實現添加查詢這一塊的,我想應該有一部分人還是不太清楚。

下面我將帶領一起去集合的內部看一看具體的代碼實現。

 

ArrayList:

  首先是ArrayList的一個實例化,java提供了一個有參構造和無參構造。下面一起查看代碼: 

    /**
     * Constructs an empty list with the specified initial capacity.
      構造具有指定初始容量的空列表。 * *
@param initialCapacity the initial capacity of the list 初始容量列表的初始容量 * @throws IllegalArgumentException if the specified initial capacity 如果指定的初始容量為負,則拋出IllegalArgumentException * is negative */ 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); } }

  通過上述我們可以看到,這是ArrayList的有參構造,可以自定義集合的初始化長度,否則如果輸入的是0那么就使用ArrayList自帶的默認的數組緩存區。

    /**
     * Constructs an empty list with an initial capacity of ten.  構造初始容量為10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

  使用無參構造,將會創建一個默認長度的數組。初始長度為10。

    /**
     * Default initial capacity. 默認初始容量  
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.  用於空實例的共享空數組實例
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

   * 解析add添加方法的全過程,下面的add方法相關的所有源代碼,

/**
     * Appends the specified element to the end of this list.  將指定的元素追加到列表的末尾
     *
     * @param e element to be appended to this list  將追加到此列表中的e元素
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    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) {
       // 如果是,就設置數組的長度為默認長度 10 minCapacity
= Math.max(DEFAULT_CAPACITY, minCapacity); }      // 確保能夠有儲存能力 ensureExplicitCapacity(minCapacity); }
private void ensureExplicitCapacity(int minCapacity) {
     // 保存這個列表在結構上被修改的次數。 modCount
++; // overflow-conscious code
     // 如果默認的長度減去實際數組的長度大於0,那么就調用
grow()方法
    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 + 0/2)
        if (newCapacity - minCapacity < 0)      // 如果新的值 - 最小值小於0   (0-10)
            newCapacity = minCapacity;        // 將默認值 10 賦值給 新的值
        if (newCapacity - MAX_ARRAY_SIZE > 0)    // 如果新的值 - 最大長度 大於 0  (0 - 2147483639) > 0
            newCapacity = hugeCapacity(minCapacity);  // 調用方法
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);  // 復制原本的數組,定義長度,賦值給自己,來達到自動擴容
    }

  * 總結以上,在ArrayList被無參實例化的時候就會被創建一個空的數組,在添加第一個值時,ArrayList底層的自動擴容機制將會被執行,也就是private void grow(int minCapacity)這個方法會被調用,給內部的elementData數組定義初始長度為10,然后再將值添加到數組的末尾。這里面主要就是牽涉到一個自動擴容機制,在每一次添加之前,都會去判斷,當前數組長度是否有實際的存儲能力,如果沒有那么自動擴容機制就會根據當前數組長度+當前長度/2來計算的方式,對當前數組進行擴容。

 

linkedList:

  * 鏈接內部結構圖

  * 查看linkedList的無參構造和有參構造

  * 無參構造

/**
     * Constructs an empty list.  // 構造一個空列表
     */
    public LinkedList() {
    }

  * 有參構造

/**
     * Constructs a list containing the elements of the specified  構造包含指定元素的列表
     * collection, in the order they are returned by the collection's 集合,按照集合返回的順序
     * iterator. 迭代器
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

  * 解析add添加方法:

/**
     * Appends the specified element to the end of this list.  將指定的元素追加到列表的末尾
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
/**
     * Links e as last element. 鏈接做為最后一個元素
     */
    void linkLast(E e) {
     // 將最后一個節點臨時存儲起來
final Node<E> l = last;

     // 創建一個新的節點,設置新的節點的上一個節點和當前節點的值
final Node<E> newNode = new Node<>(l, e, null);

     // 將新創建的節點重新存儲到專門用於保存最后一個節點的值的對象 last
= newNode;

     // 判斷是否是第一次添加,如果是第一次添加值,那么上一個值一定是null,否則就會一個值
if (l == null)
        // 如果是第一次添加,那么我們就要將新創建的節點保存到鏈表的頭first first
= newNode; else // 否則就設置l的下一個節點為新的節點
       l.next
= newNode;

     // 長度增加 size
++;
    // 修改次數 modCount
++; }

  * 總結以上:從源代碼上我們可以看到,linkedList內部采用的實際上是通過多個節點來保存值,每個節點對象中對它的上一個節點和下一個節點繼續記錄,以此將所有的節點串聯起來,就形成了鏈表。 

 

Vector:

   vector其實本質上和ArrayList是一樣的,底層都是使用了數組來完成,只是vector是從jdk1.0版本開始,ArrayList是1.2版本開始,可以理解的是ArrayList其實就是用來代替Vector的。Vector和ArrayList最大的區別就是一個是線程安全,一個是線程不安全的。這個我們可以通過查看底層代碼來得知。

  * 解析add代碼

/**
     * Appends the specified element to the end of this Vector.  將指定的元素附加到這個向量的末尾
     * 
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

  * 總結上述可得知:相比較ArrayList的add方法,我們可以看出,Vector的add方法添加的同步鎖。

 

------------------------------------------------------   分割  -------------------------------------------------------------------

  學無止境,永遠不要輕言放棄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM