手寫ArrayList集合

2021-09-25 15:11:08 字數 4571 閱讀 9413

最近仔細的研究了下集合框架的原始碼,並完全手寫下來,這裡將每一步的進展記錄下來,歡迎指點。

arraylist的構造器,分別為無參構造器、傳入初始大小的有參構造器、傳入乙個集合型別的有參構造器,這裡只介紹前面兩種常用的構造器。

無參構造器:

arraylist的底層實際就是使用陣列來實現的,我們建立乙個陣列,用於之後對arraylist的操作。

//arraylist底層就是用object陣列實現的

private object elementdata;

建立乙個空的object陣列,用於無參構造器使用

//宣告乙個空的陣列,用於空構造器的初始化

private static final object defaultcapacity_empty_elementdata = {};

宣告無參構造器時,沒有指定大小,在add方法時,才進行了初始化容量,這裡直接將空陣列的位址,賦給之後需要運算元組即可。

//未指定初始容量大小的構造器

public exarraylist()

帶初始化的有參構造器:

這裡需要判斷傳入值的乙個正確性,注意這裡之所以將initialcapacity等於0的邏輯單獨抽出來,而沒有new object的原因是,這樣做會導致add方法的判斷錯誤,所以直接等於我們建立的空陣列的位址。

//指定初始容量大小的構造器

public exarraylist(int initialcapacity) else if (initialcapacity == 0) else

}

新增方法作為我們最常用的乙個方法,其實內部原理非常簡單,但設計的非常巧妙,這裡介紹四個新增方法,add(e e)、add(int index,object element)、addall(collection c)、add(int index,collection c)

add(e e):

所有的新增方法在新增前,都需要判斷當前的乙個容量大小是否足夠,即陣列在宣告時,大小都是固定了的,新增乙個元素,就需要判斷容量是否足夠,如果不夠就進行乙個擴容,然後在新增元素。

傳入的引數表示擴容的乙個最小大小,即當前使用大小加一。

public boolean add(e e)
size是記錄當前陣列實際使用的大小,即陣列裡有多少資料,新增乙個元素會加一,刪除則會減一。

//記錄當前陣列實際使用大小

private int size;

這裡就可以知道,當時帶初始化的有參構造器在判斷時,為什麼將等於0的邏輯分離出來了,如果當時是直接new object,下面的if條件就不滿足了,最小容量就還是1,影響之後的乙個擴容。

/**

* @param mincapacity 最小擴容大小(即當前實際使用容量大小+1)

*/private void ensurecapacityinternal(int mincapacity)

ensureexplicitcapacity(mincapacity);

}

在新增元素的時候,如果elementdata還是乙個空陣列,之後就會將其擴容為大小為10的陣列。

//預設的初始容量大小

private static final int default_capacity = 10;

這個方法就是在判斷是否需要進行擴容,如果你傳入的最小擴容量是大於當前陣列的長度時,就需要進行擴容。

private void ensureexplicitcapacity(int mincapacity)
modcount 引數是用於記錄修改的次數,主要是用於使用迭代器進行迴圈輸出時,如果你在迴圈中,對arraylist進行新增或刪除的操作時,會導致modcount的迴圈前和迴圈後的值不一致,然後丟擲乙個併發異常的錯。

//修改次數,防止併發修改

protected transient int modcount = 0;

擴容的策略預設是為之前大小的1.5倍, arrays.copyof是將elementdata的大小變為newcapacity,並且保留elementdata裡的資料。

//擴容

private void grow(int mincapacity)

if (newcapacity - max_array_size > 0)

newcapacity = hugecapacity(mincapacity);

//將陣列的大小變為newcapacity,值依舊存在

elementdata = arrays.copyof(elementdata, newcapacity);

}

max_array_size 陣列允許的最大容量

//陣列允許的最大容量(減8的原因是因為陣列需要8個bytes去儲存自己的大小)

private static final int max_array_size = integer.max_value - 8;

一系列操作完畢後,就將物件新增到陣列中去,並且size加一。

add(int index, object element):

指定下標插入物件

public void add(int index, object element)
大致的思路和直接新增乙個物件一樣,只不過需要判斷下標值的準確性

private void rangecheckforadd(int index)
之後判斷是否需要擴容,然後將你指定的下標後的元素,全部向後複製,為之後插入的值騰位置,並且保持其餘元素值不變

arraycopye方法:

//① 源陣列 ② 源陣列開始複製的下標 ③ 複製後目標陣列

//④ 目標陣列開始複製的下標 ⑤ 源陣列需要複製的長度

system.arraycopy(elementdata, index, elementdata, index + 1,

size - index);

之後在將指定下標的元素,替換為需要插入的元素,實際使用的陣列大小加一即可。

addall(collection c)

將傳入的集合裡的元素,都新增到elementdata陣列中,修改實際使用大小,完成新增

首先將該集合轉為陣列,獲取該陣列的長度,然後按照慣例判斷當前陣列的容量是否足夠,這裡傳入的最小容量的引數就不是size+1,而是size+需要插入的元素個數,然後使用arraycopy方法將元素都複製到elementdata中去,之後修改實際使用大小。

public boolean addall(collection c)
addall(int index, collection c):

在指定下標中,插入乙個集合的元素,其實這個方法等於就是前面兩個方法的結合體了

和前乙個方法比,多了兩步,乙個是判斷需要插入的集合,是否可以直接在當前陣列的尾巴後面插入,如果是直接在尾部新增元素,即直接複製元素即可,如果是在中間插入元素的話,就需要將源陣列的中間的元素給向後複製需要新增的元素的個數字,然後在覆蓋元素。

public boolean addall(int index, collection c) 

//5.將傳入的集合中所有的元素,複製到elementdata中

system.arraycopy(a, 0, elementdata, index, numnew);

//6.修改實際使用長度

size = size + numnew;

return numnew != 0;

}

刪除方法的話,在原始碼中也是有兩種形式,乙個是根據下標刪除,乙個是根據物件刪除,後面的方面過於呆板,不進行解釋了。

elementdata(int index):

獲取值,即根據對應的下標返回對應的元素

private e elementdata(int index)
remove(int index):

我們可以發現,在對陣列的操作時,尤其是增刪對中間元素操作時,都是需要移動元素,來騰出位置或直接覆蓋某個元素,來達到目標的,這也就是為什麼陣列比鍊錶的增刪操作慢的乙個原因,並且陣列在宣告時就需要申請一段連續的空間記憶體來存值,申請的空間過大之後又可能會造成浪費,而過小的話動不動就需要擴容。

public e remove(int index) 

//5.將最後乙個元素置為null

elementdata[--size] = null;

return oldvalue;

}

之後會介紹linkedlist的原始碼,可以發現兩者的優缺點。

純手寫ArrayList集合 二

在閱讀arraylist的jdk原始碼的時候,你經常會看到這兩個系統函式 arrays.copyof elementdata,size 而這個方法的原始碼是 public static t copyof t original,int newlength public static t copyof ...

手寫ArrayList入門

arraylist是集合的一種實現,實現了介面list,list介面繼承了collection介面。collection是所有集合類的父類。arraylist使用非常廣泛,不論是資料庫表查詢,excel匯入解析,還是 資料爬取都需要使用到,了解arraylist原理及使用方法顯得非常重要。那麼arr...

Java集合 ArrayList集合

以陣列實現。節約空間,但是陣列有容量限制。超出限制時會增加50 容量,用system.arraycopy 複製到新的陣列,因此最好能給出陣列大小的預估值。預設第一次插入元素時建立大小為10 的陣列。按照陣列下標來訪問元素 get i set i,e 的效能很高,這是陣列的基本優勢。直接在陣列末尾加入...