一点一点看JDK源码(四)java.util.ArrayList 中篇
?一點(diǎn)一點(diǎn)看JDK源碼(四)java.util.ArrayList 中篇
?
liuyuhang原創(chuàng),未經(jīng)允許禁止轉(zhuǎn)載
本文舉例使用的是JDK8的API
?
目錄:一點(diǎn)一點(diǎn)看JDK源碼(〇)
1.綜述
?
在前篇中,對于java.util.ArrayList進(jìn)行了一些源碼注釋,能堅持看完的估計都是神一般的存在。
不過看源碼并需要一個艱苦的過程,枯燥是很正常的。
但是不是要一直都很枯燥,本文將對此類進(jìn)行分類解析。
?
2.關(guān)注點(diǎn)
2.0.ArrayList是如何構(gòu)成的?
在java中,一個類的構(gòu)成并不是十分復(fù)雜,列舉出來,一頁紙應(yīng)該是足夠的,我嘗試下。
2.0.1.類的定義
類的定義中,會聲明是class,AbstractClass,或者interface。
該類是否含有該類沒有顯示定義的方法,取決于向上有多少個extends的父類的存在。
該類是否有必須實現(xiàn)的方法,取決于向上有多少個implments的接口的存在。
?
2.0.2.類的構(gòu)造
無參構(gòu)造器,帶參構(gòu)造器。
?
構(gòu)造器的方法名是和類名同名的,并且沒有返回值。
構(gòu)造器不管是無參的,還是帶參的,不管使用何種方式調(diào)用,
都說明該類已經(jīng)被實例化了。
構(gòu)造器內(nèi)也可以寫很多奇葩的代碼的,當(dāng)然也許是常用手段。
在構(gòu)造器內(nèi)寫代碼以實現(xiàn)自己想實現(xiàn)的功能,相當(dāng)于一些容器的init,實際上就是初始化。
?
2.0.3.類的成員變量
類的成員變量,不管如何定義,都是為該類內(nèi)使用該變量提供一定的便利性
類的成員變量,理論上就是該類內(nèi)的全局變量,如果是public,或者default,protected,
都是一種對該全局變量的開放性。
成員變量,可以是static的(靜態(tài),類加載即加載),可以是final的(只允許實例化一次)
?
2.0.4.類的成員方法
類的成員方法,在不論其使用范圍的情況下,都是一種方法,根據(jù)實例化使用的構(gòu)造不同,
可調(diào)用的方法范圍不同。
若使用父類構(gòu)造器來接收子類實例(如:Object obj = new ArrayList()),
會發(fā)現(xiàn)obj可使用的方法就變得很少了(只有Object的方法允許使用了)。如下圖:
(Object是java中所有類的基類,沒有顯示繼承也是被繼承的)
因此,即使List list = new ArrayList();
用起來,字?jǐn)?shù)更少,寫起來更方便,但是失去了一些功能。
當(dāng)然,簡單的使用List接口來操作ArrayList實例化對象也是能滿足一定要求的,
也并非不可以使用。
?
2.0.5.類的內(nèi)部類
?
ArrayList也有一些內(nèi)部類,內(nèi)部類使用成員方法進(jìn)行實例化,返回的是其Implments接口的對象。
由于其接口的方法在內(nèi)部類中被復(fù)寫,所以直接調(diào)用接口的方法,實際上是調(diào)用其內(nèi)部類的方法。
關(guān)于內(nèi)部類,下文中有列舉。
?
2.0.6.類的實例化
類的成員方法,隨著實例化時接收的對象類型不同而不同,因為我們只能調(diào)用對象所在類提供
的方法,所以了解ArrayList實例化后的特性,就應(yīng)該使用ArrayList類來接收實例化對象。
(上文已有,不再贅述)
2.1.ArrayList提供了什么?
?
提供了什么?我也并非十分清楚。在第一篇中,我認(rèn)為Collection下都是容器,因此作為一個容器,
應(yīng)該提供容器應(yīng)該有的特性吧,比如:
容器存儲結(jié)構(gòu)(底層存儲結(jié)構(gòu))
容積計算(定容和擴(kuò)容)
增刪改查方法
特性方法(取決于儲存結(jié)構(gòu)和要實現(xiàn)的特性)
?
實際上這個內(nèi)容還是能夠進(jìn)行一些分類的。ArrayList提供了如下具體內(nèi)容:
2.1.1.常量和成員變量(無法直接訪問,允許調(diào)用public方法訪問)
常量包括容量(size,MAX。。),底層存儲結(jié)構(gòu)(Object數(shù)組),序列化版本,默認(rèn)儲存等。
?
2.1.2.構(gòu)造器和初始化(可調(diào)用構(gòu)造器)
?
clinit是該類在VM裝載的時候初始化用的,暫不深究。
它提供了三個構(gòu)造器:
一個無參構(gòu)造器ArrayList();
兩個帶參構(gòu)造器ArrayList(int)和ArrayList(Collection< ? extends E>)
?
初學(xué)的時候總有一種迷惑的感覺,構(gòu)造器無非就是實例化的,為什么要提供好幾個構(gòu)造器?
這三個構(gòu)造器都應(yīng)該在什么時候使用呢?
如果不確定你定義這個容器的時候,容量多大,容器內(nèi)容是什么,那么應(yīng)該使用無參構(gòu)造器。
?
如果確定你定義這個構(gòu)造器的容量,或者至少容量會有多少,可以使用ArrayList(int)構(gòu)造。
因為在ArrayList底層是Object數(shù)組,數(shù)組的容量是確定的,因此每次增加內(nèi)容都需要對數(shù)組
進(jìn)行擴(kuò)容,擴(kuò)容過程中要用新數(shù)組接收拷貝后的舊數(shù)組,所以節(jié)約計算資源效率,在能確定
容量的情況下,最好使用定容構(gòu)造器ArrayList(int)。
?
如果一個容器內(nèi)將直接增加數(shù)據(jù),那么該數(shù)據(jù)最好是來自集合,也就是說向上兩層的接口
Collection之下的所有結(jié)構(gòu),都可以直接轉(zhuǎn)化為ArrayList的,此時就應(yīng)該選擇使用
ArrayList(Collection< ? extends E>)構(gòu)造器了。
ArrayList查詢快,增刪慢,這個是官方說法。快慢實際上是相對而言的,相對于誰呢?
一般說到數(shù)組的相對性,都指的是鏈表。
即,接收參數(shù)的時候,使用鏈表,查詢和加工參數(shù)的時候,使用數(shù)組。
?
2.1.3.成員方法
ArrayList提供的成員方法很多,主要分為三類:
1.增刪改查對容器直接操作,歸為一類。
2.內(nèi)部保護(hù)方法,內(nèi)部處理數(shù)據(jù)中調(diào)用的方法,或只暴露給uitl包的方法,無法公開調(diào)用。
3.其余的,對于容器特性的操作,或數(shù)據(jù)轉(zhuǎn)化的操作,歸為一類。
?
增:add,addAll,分別對應(yīng)添加單個元素和添加多個元素,其中有對于index的指定參數(shù)時,
就是針對指定index后插入實參對象。位指定的時候默認(rèn)加在末尾。
add和addAll的時候有進(jìn)行擴(kuò)容判斷,擴(kuò)容倍數(shù)為1.5倍。(先擴(kuò)容,增加后再去掉空元素)
?
刪:remove,removeAll,removeIf,分別對應(yīng)刪除單個元素,刪除多個元素,按條件匹配刪除。
傳入?yún)?shù)有指定的index(按指定index刪除),雙index(按指定index范圍刪除),
Object(嘗試找到此元素并刪除,返回操作標(biāo)識),Collection(刪除指定集合內(nèi)容)
Predicate接口(作為filter來進(jìn)行是否刪除的過濾,功能類似于Compare接口)。
有些刪除的方法是帶有返回值的,為boolean,或被刪除的內(nèi)容,應(yīng)當(dāng)接收,作為是否成功,
或者操作可能需要回滾的判斷。
?
改:set,改指定index的值為實參對象。
?
查:get,根據(jù)index獲取元素。indexOf,lastIndexOf,分別正序或倒敘根據(jù)元素查index
內(nèi)部保護(hù)方法:
一點(diǎn)點(diǎn)去找內(nèi)部保護(hù)方法去看定義,比較麻煩,可以直接看結(jié)構(gòu)。
若該方法有紅色方框標(biāo)記,就是不對外公開調(diào)用的了。如下圖:
內(nèi)部保護(hù)方法,之所以不對外公開,是因為外部調(diào)用的時候,因為考慮不周,或調(diào)用方式錯誤,
或者其他原因吧,將導(dǎo)致有錯誤出現(xiàn),本身可能也并非是一個完整的操作鏈,所以保護(hù)起來。
?
如fastRemove(int)方法,util包下都可以調(diào)用,有和remove有區(qū)別在于,它省略掉了index校驗
還有rangeCheck(int),是專門用于index校驗的方法,也沒有必要對外公開,它屬于
其他方法,如add,addAll的一部分,這種方法抽取出來的原因,多數(shù)因為出現(xiàn)次數(shù)超過三次,
因此就有必要進(jìn)行重新封裝了。
?
特性操作:
特性操作細(xì)分下來,也可以按照功能進(jìn)行細(xì)分。如:
清空(clear),克隆(clone),容量(size),判空(isEmpty),判斷包含(cantains),
容量優(yōu)化(ensureCapacity),遍歷(forEach),迭代器(iterator等),拆分(subList),
拆分迭代(spliterator),比較排序(sort),轉(zhuǎn)數(shù)組(toArray),替換(replace),
去空(trimToSize),求交集(retainAll)
其中,清空,替換,排序,容量優(yōu)化,都是對ArrayList自身的操作。
克隆,容量,判斷,都是對ArrayList的一種特性或內(nèi)容查詢方式。
而遍歷,迭代,拆分,迭代拆分,就純粹是數(shù)據(jù)加工,而獲得其他對象了。
其中拆分,迭代拆分,Collection接口下的Stream(聚合)都是1.8新增的了。
?
2.1.4.成員方法調(diào)用的內(nèi)部類
ArrayList中一共有四個內(nèi)部類,都是要用成員方法來調(diào)用的。內(nèi)部類如下圖:
分別是ArrayListSpliterator,Itr,ListItr,SubList。調(diào)用的方法分別如下:
?
具體用法,下篇再研究吧我!!
?
3.其他關(guān)注點(diǎn)
?
發(fā)現(xiàn)了一些奇葩關(guān)注點(diǎn),不知道有用沒,說下而已。
?
內(nèi)部類的類名展示是使用$做連接符的,mybatis中的mapper.xml要使用內(nèi)部類來接收的話,該內(nèi)部類必須是靜態(tài)的。
貌似VM在編譯的時候是拆分編譯的,但是命名不是,還是按照內(nèi)部處理的,如下圖:
?
成員變量elementData前有關(guān)鍵字transient進(jìn)行修飾,表示該變量不參與實例化,應(yīng)該是作為緩存的意思了。
fastRemove不僅本類可用,util包下其他的類也可以調(diào)用還。
?
要使用Collection下的Stream(聚合)方法的話,必須要將ArrayList用Collection來做對象的類型來接收,然后才可以使用。
?
ArrayList是線程不安全的,那么modCount真的那么有用么,就不理解了,不會只用在序列化上吧。也沒見到有回滾方法。
?
官方介紹中,ArrayList在List接口下,List接口下的實例定義為隨機(jī)存取不支持,感覺應(yīng)該寫的出來吧,只是壓根沒寫。隨
機(jī)存有影響,隨機(jī)取還不容易咩?沒誰會考慮自己繼承ArrayList然后重新擴(kuò)展吧,估計也有可能,我沒見過而已。
?
ArrayList中對Arrays工具類,和System類都有應(yīng)用。
?
retainAll調(diào)用了內(nèi)部方法batchRemove,作為一個交集判斷操作,使用了緩沖區(qū)elementData。
如果要提供取交集操作該多好!!
?
?
以上!
?
轉(zhuǎn)載于:https://www.cnblogs.com/liuyuhangCastle/p/9643671.html
總結(jié)
以上是生活随笔為你收集整理的一点一点看JDK源码(四)java.util.ArrayList 中篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成为一名优秀的程序员基本要素
- 下一篇: js setTimeout 传递带参数的