【源碼解析】扒開ArrayList的外衣

積千里跬步,匯萬里江河;天天進步一點點,終有一天將成大佬。

本文內容

固然ArrayList裏的方法不止這些,本文主要講一些經常使用的方法java

方法變量

Arraylist裏的方法變量主要有如下幾個數組

1. 構造方法

1.1 有參構造

1.1.1 傳入數組的大小

1.1.1.1代碼實現
List<String> list=new ArrayList<>(5);
1.1.1.2源碼解析

1.1.2 傳入一個list對象

其實這個就至關於把傳入的list對象裏的數據複製到新的ArrayList對象工具

1.1.2.1 代碼實現
List<String> list=new ArrayList<>(Arrays.asList("z","m","h"));
這裏用來 Arrays工具類裏的 asList方法,它的源碼裏是直接返回一個List,有興趣的能夠去看看,這裏就不介紹了
1.1.2.2 源碼解析

1.2 無參構造

這個比較簡單,直接賦值一個空數組源碼分析

1.2.1代碼實現

List<String> list=new ArrayList<>();

1.2.2源碼解析

2. add方法

add通常經常使用的有兩個方法,一個就是add(E e)在尾部添加數據,一個就是add(int index,E element)在指定位置插入元素性能

2.1 add(E e)

這個是Arrayist的主要方法,平時用的也是最多的方法之一,因此源碼比較複雜,比較長spa

2.1.1 代碼實現

List<String> list=new ArrayList<>();
list.add("灰灰HK");

2.1.2 源碼解析

  • ensureCapacityInternal(int minCapacity)確保數組容量充足

  • calculateCapacity(Object[] elementData, int minCapacity)

  • 再回到ensureExplicitCapacity(int minCapacity)這個方法,這個方法先修改次數加1,而後判斷size+1是否是比當前的數組容量大,若是比當前的數組容量大,則進行擴容操做,擴大容量爲原數組的1.5倍
好比第二次調用add方法,此時 size+1=2, elementData.length=10,爲何等於10呢?由於第一次默認把數組容量從0擴大到了10,這時 size+1elementData.length小,就不會進行擴容操做

  • grow(int minCapacity)擴容
這裏調用 Arrays.copyOf()方法進行復制操做,當進一步深刻這個方法時,發現是由 System.arraycopy()這個方法實現複製功能的,這個方法由 native關鍵字修飾,表示不是由 Java語言實現的,通常是c/cpp實現

2.1.3 小結

到這裏,add的方法流程就走完了,其核心步驟:3d

  • 每次添加元素時判斷數組容量是否充足
  • 第一次添加元素,把數組容量擴容到10
  • 擴容時,除第一次,之後的每次擴容爲原大小的1.5倍
  • 擴容後調用System.arraycopy()方法把原數組的元素複製到擴容後的新數組

2.2 add(int index, E element)

該方法爲在指定位置插入元素,該位置及後面全部元素後移code

2.2.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.add(0,"灰灰");

2.2.2 源碼解析

能夠看到,這邊又用到了 System.arraycopy()這個方法
  • rangeCheckForAdd(int index)判斷是否越界
這裏他是和 size對比,而不是和數組的 length對比,我我的認爲這樣第一節省了空間,第二方便後面移動的操做

  • System.arraycopy()拷貝數組
public static native void arraycopy(Object src,  int  srcPos,
                                     Object dest, int destPos,
                                    int length)
  • src 原數組對象
  • srcPos 原數組起始位置
  • dest 目標數組
  • destPos 目標數組起始位置
  • length 複製多少個數據

2.2.3 小結

插入方法其主要步驟以下:對象

  • 檢查插入的位置是否越界
  • 檢查數組容量是否充足,不充足進行擴容相關操做
  • 調用System.arraycopy()進行index及後面的元素後移

3. get方法

3.1 get(int index)

3.1.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.get(0);

3.1.2 源碼解析

  • rangeCheck(int index)判斷是否越界
get個add方法判斷越界的方法是不同的,這邊是 index>=size,多了個 等於,爲何要多個等於呢?由於數組是從0開始的,而size 至關於是開始的從1開始的
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
  • elementData(int index)直接返回對應下標的數組元素
E elementData(int index) {
    return (E) elementData[index];
}

3.1.3 小結

get方法比較簡單,主要步驟爲:blog

  • 檢查是否越界
  • 返回對應元素

4. set方法

4.1 set(int index, E element)

4.1.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.set(0,"灰灰");

4.1.2 源碼解析

5. remove方法

5.1 remove(int index)

5.1.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.remove(0);

5.1.2 源碼解析

當刪除的元素爲最後一個元素時, numMoved就小於0了,就不會進行移動元素的操做

5.2 remove(Object o)

這個方法在實際中用的比較少,由於 AraryList是能夠保存重複的元素,因此刪除是 刪除最先添加的元素

5.2.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.remove("hk");

5.2.2 源碼解析

  • fastRemove(int index)刪除元素
這個方法和remove(int index)內部的操做相似,不過這邊不保存被刪除的元素
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

6. clear方法

6.1 clear()

6.1.1 代碼實現

List<String> list=new ArrayList<>();
list.add("hk");
list.clear();

6.1.2 源碼分析

總結

ArrayList底層擴容或者移動數組元素時都調用了System.arraycopy()來進行相關操做,平時進行咱們進行數組複製或移動的時候也能夠調用這個方法了,這個性能比循環複製性能高多了,特別是在大量數據的時候。

文章好幾回出現了modCount++這個操做,這個modCount主要用戶內部類的迭代器

相關文章
相關標籤/搜索