ArrayList部分一共五篇文章了,並且引入了時間複雜度來分析,強烈建議大家一定要按順序閱讀,本文是第1篇。
前些天的文章,反覆的畫圖,不停的重複,就是想讓大家理解,物件在記憶體中是什麼樣的。也是為今天的及以後的講解打下基礎。如果要說大家在寫Java程式碼的時候哪個類用得最多,我想除了String,基本上就是ArrayList了吧,那今天我們說說ArrayList。
首先ArrayList是一個普通的類,我們來看一段程式碼:
首先:執行List<Person> list1 = new ArrayList<>();當看到new這個關鍵字的時候,我們腦袋裡應該第一印象就是這貨在堆記憶體開闢了一塊空間,好我們再來畫一畫。
注:常量池位於方法區,方法區位於堆記憶體,前面沒涉及到,所以沒畫方法區,現在補上
好,既然是new出來的,那我們直接從建構函式入手,看一下建構函式做了什麼。
很簡單,就一行程式碼,繼續看一下,this.elementData和DEFAULTCAPACITY_EMPTY_ELEMENTDATA分別是什麼
紅框裡的內容是不是似曾相識?是的,和String一樣,底層是陣列,唯一的區別是String底層是char[]陣列(忘了的可以複習一下,傳送門:String是一個很普通的類 – Java那些事兒),而這兒是Object[]陣列,也就是說該陣列可以放任何物件(所有物件都繼承自父類Object),執行完建構函式後,如下圖。
注:static修飾的變數,常駐於方法區,我們不需要new,JVM會提前給我們初始化好,這個特性在實際開發過程中,經常拿來做快取。在讓人疑惑的Java程式碼 – Java那些事兒 一文中,我們文中Integer的快取就是最好的例子。static變數又叫類變數,不管該類有多少個物件,static的變數只有一份,獨一無二。
fianl修飾的變數,JVM也會提前給我們初始化好。
transient這個關鍵字告訴我們該物件在序列化的時候請忽略這個元素,後續我們會講序列化,這兒先跳過。
繼續執行:List<Person> list2 = new ArrayList<>();
ArrayList這個類的作者真是好貼心,new的時候連快取都考慮到了,為了避免我們反覆的建立無用陣列,所有新new出來的ArrayList底層陣列都指向快取在方法區裡的Object[]陣列。
繼續執行Person person1 = new Person(“張三”)
繼續,執行list1.add(person1),不多說,看原始碼ArrayList是怎麼處理add的。
我們先看ensureCapacityInternal方法,方法裡有個引數是size,看們先看一下這個size從哪來的。
原來是一個成員變數,相信大家看到size一猜就知道大概是幹嘛的了吧。好,我們在圖裡的ArrayList物件裡補上它,size是int基本資料型別,成員變數初始化的為0。
繼續往下看
ensureCapacityInternal方法是在add裡面呼叫的。
再看grow方法
跟進到Arrays這個工具類,很簡單
再看copyOf()方法
最後我們來看一下System.arraycopy()方法,好奇怪,這個方法只有定義,卻沒有實現,方法用了一個native來修飾。native的方法,是由其它語言來實現的,一般是(C或C++),所以這兒沒有實現程式碼。這是一個陣列拷貝方法,大家還在寫for迴圈拷貝陣列嗎?以後多用這個方法吧,簡單又方便還能獲得得更好的效能。
注:native方法,我們會後續會講解,我們先關注本章內容。
由於陣列內容目前為空,相當於沒有拷貝。折騰了這麼久,原來只是為了建立一個預設長度為10的Object[]陣列,有些朋友說,直接new不就行了,這麼費勁,其實這裡面大有文章,別急,稍後會說,繼續畫圖。
再回過頭來看,add()這個方法,繼續往下執行:
很簡單,size現在是0,就是把傳進來的這個e(這裡是person1),放到list1的elementData[]下標為0的陣列裡面,同時size加1,老規矩,上圖。
注意看紅框裡,雖然我們list1裡的elementData陣列的長度是10,但是size是1,size是邏輯長度,並不是陣列長度。
現在debug一下,驗證我們圖裡的內容:
好的,執行一下本文開始那段程式碼,看結果:
順便看一看size()方法的原始碼:
有人說,呀,就一個元素,在堆記憶體中佔了10個位置,好浪費呀,沒辦法,你要享受ArrayList的便利與豐富的API,就得犧牲一下空間作為代價。
注:本專欄文章首發於公眾號:saysayJava。所有示例程式碼均已上傳至公眾號,需要請關注下載。
如果喜歡本系列文章,請為我點贊或順手分享,您的支援是我繼續下去的動力,您也可以在評論區留言想了解的內容,有機會本專欄會做講解,最後別忘了關注一下我。
轉載無限歡迎,但請註明「作者」和「原文地址」。轉載請在文中保留此段,感謝您對作者版權的尊重。如需商業轉載或刊登,請聯絡作者獲得授權。