ArrayList初始化 - Java那些事兒

喝水會長肉發表於2021-12-23

ArrayList部分一共五篇文章了,並且引入了時間複雜度來分析,強烈建議大家一定要按順序閱讀,本文是第1篇。

前些天的文章,反覆的畫圖,不停的重複,就是想讓大家理解,物件在記憶體中是什麼樣的。也是為今天的及以後的講解打下基礎。如果要說大家在寫Java程式碼的時候哪個類用得最多,我想除了String,基本上就是ArrayList了吧,那今天我們說說ArrayList。

首先ArrayList是一個普通的類,我們來看一段程式碼:

ArrayList初始化 - Java那些事兒

首先:執行List<Person> list1 = new ArrayList<>();當看到new這個關鍵字的時候,我們腦袋裡應該第一印象就是這貨在堆記憶體開闢了一塊空間,好我們再來畫一畫。

ArrayList初始化 - Java那些事兒
注:常量池位於方法區,方法區位於堆記憶體,前面沒涉及到,所以沒畫方法區,現在補上

好,既然是new出來的,那我們直接從建構函式入手,看一下建構函式做了什麼。

ArrayList初始化 - Java那些事兒

很簡單,就一行程式碼,繼續看一下,this.elementData和DEFAULTCAPACITY_EMPTY_ELEMENTDATA分別是什麼

ArrayList初始化 - Java那些事兒

紅框裡的內容是不是似曾相識?是的,和String一樣,底層是陣列,唯一的區別是String底層是char[]陣列(忘了的可以複習一下,傳送門:String是一個很普通的類 - Java那些事兒),而這兒是Object[]陣列,也就是說該陣列可以放任何物件(所有物件都繼承自父類Object),執行完建構函式後,如下圖。

ArrayList初始化 - Java那些事兒
注:static修飾的變數,常駐於方法區,我們不需要new,JVM會提前給我們初始化好,這個特性在實際開發過程中,經常拿來做快取。在讓人疑惑的Java程式碼 - Java那些事兒 一文中,我們文中Integer的快取就是最好的例子。static變數又叫類變數,不管該類有多少個物件,static的變數只有一份,獨一無二。
fianl修飾的變數,JVM也會提前給我們初始化好。
transient這個關鍵字告訴我們該物件在序列化的時候請忽略這個元素,後續我們會講序列化,這兒先跳過。

繼續執行:List<Person> list2 = new ArrayList<>();

ArrayList初始化 - Java那些事兒

ArrayList這個類的作者真是好貼心,new的時候連快取都考慮到了,為了避免我們反覆的建立無用陣列,所有新new出來的ArrayList底層陣列都指向快取在方法區裡的Object[]陣列。

繼續執行Person person1 = new Person("張三")

ArrayList初始化 - Java那些事兒

繼續,執行list1.add(person1),不多說,看原始碼ArrayList是怎麼處理add的。

ArrayList初始化 - Java那些事兒

我們先看ensureCapacityInternal方法,方法裡有個引數是size,看們先看一下這個size從哪來的。

ArrayList初始化 - Java那些事兒

原來是一個成員變數,相信大家看到size一猜就知道大概是幹嘛的了吧。好,我們在圖裡的ArrayList物件裡補上它,size是int基本資料型別,成員變數初始化的為0。

ArrayList初始化 - Java那些事兒

繼續往下看

ArrayList初始化 - Java那些事兒

ensureCapacityInternal方法是在add裡面呼叫的。

ArrayList初始化 - Java那些事兒

再看grow方法

ArrayList初始化 - Java那些事兒

跟進到Arrays這個工具類,很簡單

ArrayList初始化 - Java那些事兒

再看copyOf()方法

ArrayList初始化 - Java那些事兒

最後我們來看一下System.arraycopy()方法,好奇怪,這個方法只有定義,卻沒有實現,方法用了一個native來修飾。native的方法,是由其它語言來實現的,一般是(C或C++),所以這兒沒有實現程式碼。這是一個陣列拷貝方法,大家還在寫for迴圈拷貝陣列嗎?以後多用這個方法吧,簡單又方便還能獲得得更好的效能。

ArrayList初始化 - Java那些事兒
注:native方法,我們會後續會講解,我們先關注本章內容。

由於陣列內容目前為空,相當於沒有拷貝。折騰了這麼久,原來只是為了建立一個預設長度為10的Object[]陣列,有些朋友說,直接new不就行了,這麼費勁,其實這裡面大有文章,別急,稍後會說,繼續畫圖。

ArrayList初始化 - Java那些事兒

再回過頭來看,add()這個方法,繼續往下執行:

ArrayList初始化 - Java那些事兒

很簡單,size現在是0,就是把傳進來的這個e(這裡是person1),放到list1的elementData[]下標為0的陣列裡面,同時size加1,老規矩,上圖。

ArrayList初始化 - Java那些事兒

注意看紅框裡,雖然我們list1裡的elementData陣列的長度是10,但是size是1,size是邏輯長度,並不是陣列長度。

現在debug一下,驗證我們圖裡的內容:

ArrayList初始化 - Java那些事兒

好的,執行一下本文開始那段程式碼,看結果:

ArrayList初始化 - Java那些事兒

順便看一看size()方法的原始碼:

ArrayList初始化 - Java那些事兒

有人說,呀,就一個元素,在堆記憶體中佔了10個位置,好浪費呀,沒辦法,你要享受ArrayList的便利與豐富的API,就得犧牲一下空間作為代價。

注:本專欄文章首發於公眾號:saysayJava。所有示例程式碼均已上傳至公眾號,需要請關注下載。

如果喜歡本系列文章,請為我點贊或順手分享,您的支援是我繼續下去的動力,您也可以在評論區留言想了解的內容,有機會本專欄會做講解,最後別忘了關注一下我。

轉載無限歡迎,但請註明「作者」和「原文地址」。轉載請在文中保留此段,感謝您對作者版權的尊重。如需商業轉載或刊登,請聯絡作者獲得授權。

上一篇:說說Java裡的equals(中) - Java那些事兒

下一篇:ArrayList底層陣列擴容原理 - Java那些事兒



相關文章