List集合就這麼簡單【源碼剖析】

前言

聲明,本文用得是jdk1.8java

前一篇已經講了Collection的總覽:Collection總覽,介紹了一些基礎知識。node

如今這篇主要講List集合的三個子類:面試

  • ArrayList
    • 底層數據結構是數組。線程不安全
  • LinkedList
    • 底層數據結構是鏈表。線程不安全
  • Vector
    • 底層數據結構是數組。線程安全

這篇主要來看看它們比較重要的方法是如何實現的,須要注意些什麼,最後比較一下哪一個時候用哪一個~c#

看這篇文章以前最好是有點數據結構的基礎:Java實現單向鏈表棧和隊列就是這麼簡單二叉樹就這麼簡單數組

固然了,若是講得有錯的地方還請你們多多包涵並不吝在評論去指正~安全

1、ArrayList解析

首先,咱們來說解的是ArrayList集合,它是咱們用得很是很是多的一個集合~微信

首先,咱們來看一下ArrayList的屬性:數據結構

根據上面咱們能夠清晰的發現:ArrayList底層其實就是一個數組,ArrayList中有擴容這麼一個概念,正由於它擴容,因此它可以實現「動態」增加post

1.2構造方法

咱們來看看構造方法來印證咱們上面說得對不對:性能

1.3Add方法

add方法能夠說是ArrayList比較重要的方法了,咱們來總覽一下:

1.3.1add(E e)

步驟:

  • 檢查是否須要擴容
  • 插入元素

首先,咱們來看看這個方法:

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

該方法很短,咱們能夠根據方法名就猜到他是幹了什麼:

  • 確認list容量,嘗試容量加1,看看有無必要
  • 添加元素

接下來咱們來看看這個小容量(+1)是否知足咱們的需求:

隨後調用ensureExplicitCapacity()來肯定明確的容量,咱們也來看看這個方法是怎麼實現的:

因此,接下來看看grow()是怎麼實現的~

進去看copyOf()方法:

到目前爲止,咱們就能夠知道add(E e)的基本實現了:

  • 首先去檢查一下數組的容量是否足夠
    • 足夠:直接添加
    • 不足夠:擴容
      • 擴容到原來的1.5倍
      • 第一次擴容後,若是容量仍是小於minCapacity,就將容量擴充爲minCapacity。

1.3.2add(int index, E element)

步驟:

  • 檢查角標
  • 空間檢查,若是有須要進行擴容
  • 插入元素

咱們來看看插入的實現:

咱們發現,與擴容相關ArrayList的add方法底層其實都是arraycopy()來實現的

看到arraycopy(),咱們能夠發現:該方法是由C/C++來編寫的,並非由Java實現:

總的來講:arraycopy()仍是比較可靠高效的一個方法。

參考R大回答:www.zhihu.com/question/53…

1.4 get方法

  • 檢查角標
  • 返回元素

// 檢查角標
   private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
	
	// 返回元素
    E elementData(int index) {
        return (E) elementData[index];
    }
複製代碼

1.5 set方法

步驟:

  • 檢查角標
  • 替代元素
  • 返回舊值

1.6remove方法

步驟:

  • 檢查角標
  • 刪除元素
  • 計算出須要移動的個數,並移動
  • 設置爲null,讓Gc回收

1.7細節再說明

  • ArrayList是基於動態數組實現的,在增刪時候,須要數組的拷貝複製
  • ArrayList的默認初始化容量是10,每次擴容時候增長原先容量的一半,也就是變爲原來的1.5倍
  • 刪除元素時不會減小容量,若但願減小容量則調用trimToSize()
  • 它不是線程安全的。它能存放null值。

參考資料:

2、Vector與ArrayList區別

Vector是jdk1.2的類了,比較老舊的一個集合類。

Vector底層也是數組,與ArrayList最大的區別就是:同步(線程安全)

Vector是同步的,咱們能夠從方法上就能夠看得出來~

在要求非同步的狀況下,咱們通常都是使用ArrayList來替代Vector的了~

若是想要ArrayList實現同步,可使用Collections的方法:List list = Collections.synchronizedList(new ArrayList(...));,就能夠實現同步了~

還有另外一個區別:

  • ArrayList在底層數組不夠用時在原來的基礎上擴展0.5倍,Vector是擴展1倍。

Vector源碼的解析可參考:

3、LinkedList解析

LinkedList底層是雙向鏈表~若是對於鏈表不熟悉的同窗可先看看個人單向鏈表(雙向鏈表的練習我還沒作)【Java實現單向鏈表

理解了單向鏈表,雙向鏈表也就不難了。

從結構上,咱們還看到了LinkedList實現了Deque接口,所以,咱們能夠操做LinkedList像操做隊列和棧同樣~

LinkedList變量就這麼幾個,由於咱們操做單向鏈表的時候也發現了:有了頭結點,其餘的數據咱們均可以獲取獲得了。(雙向鏈表也同理)

3.1構造方法

LinkedList的構造方法有兩個:

3.2add方法

若是作過鏈表的練習,對於下面的代碼並不陌生的~

  • add方法實際上就是往鏈表最後添加元素
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++;
    }
複製代碼

3.3remove方法

實際上就是下面那個圖的操做:

3.4get方法

能夠看到get方法實現就兩段代碼:

public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
複製代碼

咱們進去看一下具體的實現是怎麼樣的:

3.5set方法

set方法和get方法其實差很少,根據下標來判斷是從頭遍歷仍是從尾遍歷

public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

複製代碼

......LinkedList的方法比ArrayList的方法多太多了,這裏我就不一一說明了。具體可參考:

4、總結

其實集合的源碼看起來並非很困難,遇到問題能夠翻一翻,應該是可以看懂的~

ArrayList、LinkedList、Vector算是在面試題中比較常見的的知識點了。下面我就來作一個簡單的總結:

ArrayList:

  • 底層實現是數組
  • ArrayList的默認初始化容量是10,每次擴容時候增長原先容量的一半,也就是變爲原來的1.5倍
  • 增刪時候,須要數組的拷貝複製(navite 方法由C/C++實現)

LinkedList:

  • 底層實現是雙向鏈表[雙向鏈表方便實現往前遍歷]

Vector:

  • 底層是數組,如今已少用,被ArrayList替代,緣由有兩個:
    • Vector全部方法都是同步,有性能損失
    • Vector初始length是10 超過length時 以100%比率增加,相比於ArrayList更多消耗內存
    • 參考資料:www.zhihu.com/question/31…

總的來講:查詢多用ArrayList,增刪多用LinkedList。

ArrayList增刪慢不是絕對的(在數量大的狀況下,已測試):

  • 若是增長元素一直是使用add()(增長到末尾)的話,那是ArrayList要快
  • 一直刪除末尾的元素也是ArrayList要快【不用複製移動位置】
  • 至於若是刪除的是中間的位置的話,仍是ArrayList要快

但通常來講:增刪多仍是用LinkedList,由於上面的狀況是極端的~

參考資料:

文章的目錄導航zhongfucheng.bitcron.com/post/shou-j…

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y。爲了你們方便,剛新建了一下qq羣:742919422,你們也能夠去交流交流。

相關文章
相關標籤/搜索