面試準備 - ArrayList與LinkedList的實現和區別

寫在前面的話

明年換工做,總要複習刷刷各個方面的題,看別人的總感受差點什麼,這幾天索引了一些很是好的題目,能放在文字不難說明的我便記錄下來,分享給你們。我也不建議你們直接看這樣的文章,本身隨便看看源碼比較好,面試也能夠觸類旁通,固然你要是犯懶能夠隨意通讀一下大概有個概念,了勝於無。html

我有時間會把我這幾天整理到的問題列出來,強烈推薦只看面試問題本身尋找答案。java

ArrayList與LinkedList的實現和區別

都是 List 的實現,一個數組實現一個鏈表實現,直奔源碼,先看類:git

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, Serializable
{...}

複製代碼
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, Serializable
{
複製代碼

先從實現初看端倪

RandomAccess

首先一眼發現雙方繼承的 List 都挺像的,一個是 AbstractSequentialList 一個是 AbstractList 類。都是 1300 多行代碼,先看哪一個都差很少,先看 ArrayList 他實現了個 RandomAccess LinkedList 卻沒有實現github

package java.util;

/**
 * Marker interface used by <tt>List</tt> implementations to indicate that
 * they support fast (generally constant time) random access.  The primary
 * purpose of this interface is to allow generic algorithms to alter their
 * behavior to provide good performance when applied to either random or
 * sequential access lists.
 *
 * <p>The best algorithms for manipulating random access lists (such as
 * <tt>ArrayList</tt>) can produce quadratic behavior when applied to
 * sequential access lists (such as <tt>LinkedList</tt>).  Generic list
 * algorithms are encouraged to check whether the given list is an
 * <tt>instanceof</tt> this interface before applying an algorithm that would
 * provide poor performance if it were applied to a sequential access list,
 * and to alter their behavior if necessary to guarantee acceptable
 * performance.
 *
 * <p>It is recognized that the distinction between random and sequential
 * access is often fuzzy.  For example, some <tt>List</tt> implementations
 * provide asymptotically linear access times if they get huge, but constant
 * access times in practice.  Such a <tt>List</tt> implementation
 * should generally implement this interface.  As a rule of thumb, a
 * <tt>List</tt> implementation should implement this interface if,
 * for typical instances of the class, this loop:
 * <pre>
 *     for (int i=0, n=list.size(); i &lt; n; i++)
 *         list.get(i);
 * </pre>
 * runs faster than this loop:
 * <pre>
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * </pre>
 *
 * <p>This interface is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @since 1.4
 */
public interface RandomAccess {
}


複製代碼

該接口的主要目的是容許通用算法更改其行爲,以便在應用於隨機訪問或順序訪問列表時提供良好的性能.他只是一個標誌性接口,作標註用的,1.5後通常都用註解來作這個事情。代表實現這個這個接口的 List 集合是支持 快速隨機訪問面試

實現了這個接口的 list 實例使用 for 會比使用 Iterator 更快!算法

出自上面的註釋數組

因此咱們得出第一點結論:ArrayList 支持 快速隨機訪問,LinkedList 不支持 。bash

ArrayList 隨機訪問很快, LinkedList「鏈表」 插入刪除很快數據結構

qeue

擴容角度

  • ArrayList 擴容

先給 ArrayList 個初始長度:app

List a  = new ArrayList<>(4);
複製代碼

ArrayList 內部會產生一個等長的 Object 數組 elementData,每次 add 的時候都會檢測是否須要擴容:

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

        // overflow-conscious code
        // add後的長度 - 當前容量 > 0
        if (minCapacity - elementData.length > 0)
            //擴容
            grow(minCapacity);
    }
    
     
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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);
    }

複製代碼
int oldCapacity = elementData.length;
 int newCapacity = oldCapacity + (oldCapacity >> 1);
複製代碼

經過代碼可知,每次擴容爲舊的1.5倍。切最大長度不超過 2147483639

  • LinkedList
List a  = new LinkedList();
複製代碼

LinkedList「鏈表」不能初始化開闢容量,由於 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++;
    }   
複製代碼

鏈表源碼更簡單,定義經典的節點(Node)結構追加到鏈表尾部,鏈表長度 +1, addFirst 、addLast 同理。

因此鏈表不須要提早擴容,不怕越界,隨用隨插便可。理論上沒有邊界。

結論

實現方式 應用場景 擴容方式 最大長度
ArrayList 動態數組 讀取 1.5倍 2147483639
LinkedList 鏈表 插入、刪除 正常插入末尾 看內存:無限大
插入刪除元素時間複雜度 讀取元素時間複雜度
ArrayList O(n) O(1)
LinkedList O(1) O(n)

原文連接:github.com/pkwenda/Blo…

相關文章
相關標籤/搜索