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

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

本文內容

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

方法變量

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對象app

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的主要方法,平時用的也是最多的方法之一,因此源碼比較複雜,比較長url

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小,就不會進行擴容操做spa

  • grow(int minCapacity)擴容

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

2.1.3 小結

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

  • 每次添加元素時判斷數組容量是否充足

  • 第一次添加元素,把數組容量擴容到10

  • 擴容時,除第一次,之後的每次擴容爲原大小的1.5倍

  • 擴容後調用System.arraycopy()方法把原數組的元素複製到擴容後的新數組

2.2 add(int index, E element)

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

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)直接返回對應下標的數組元素
elementData(int index) {
    return (E) elementData[index];
}
複製代碼

3.1.3 小結

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

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

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主要用戶內部類的迭代器

相關文章
相關標籤/搜索