文章系作者原創(chuàng),如有轉(zhuǎn)載請(qǐng)注明出處,如有雷同,那就雷同吧~(who care?。?/span>

一、寫在前面

這是源碼分析計(jì)劃的第一篇,博主準(zhǔn)備把一些常用的集合源碼過一遍,比如:ArrayList、HashMap及其對(duì)應(yīng)的線程安全實(shí)現(xiàn),此文章作為自己相關(guān)學(xué)習(xí)的一個(gè)小結(jié),記錄學(xué)習(xí)成果的同時(shí),也希望對(duì)有緣的朋友提供些許幫助。

當(dāng)然,能力所限,難免有紕漏,希望發(fā)現(xiàn)的朋友能夠予以指出,不勝感激,以免誤導(dǎo)了大家!

二、穩(wěn)扎穩(wěn)打過源碼

首先,是源碼內(nèi)部的成員變量定義以及構(gòu)造方法:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 參數(shù)項(xiàng)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 構(gòu)造方法

 進(jìn)階分析,集合常用操作,先從簡(jiǎn)單入手:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 按位次獲取單個(gè)集合元素

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 按位次修改單個(gè)集合元素

  如前所述,單個(gè)元素按位次獲取操作很簡(jiǎn)單,只要你了解到arrayList內(nèi)部是數(shù)組存放數(shù)據(jù),那上述操作完全可以想得到。

  下面繼續(xù),集合的添加和刪除操作,沒看過源碼的朋友可以思考下,添加和刪除操作同上述兩個(gè)操作主要的區(qū)別在哪里?為什么會(huì)復(fù)雜些?

  因?yàn)樘砑釉氐臅r(shí)候可能位置不夠嘛,那怎么辦?位置不夠就加位置,也就是所謂的“擴(kuò)容”!我們自己思考下實(shí)現(xiàn)思路,然后同作者的思路對(duì)比下:

  1.上面我們提到,默認(rèn)構(gòu)造方法使用的lazy_init模式,添加元素時(shí)才init。那第一步判斷是否需要init,需要?jiǎng)tinit。

  2.判斷是否需要擴(kuò)容(通俗點(diǎn)說就是當(dāng)前數(shù)組有沒有空位置?)需要就擴(kuò)容(創(chuàng)建個(gè)容量更大的新數(shù)組),但是擴(kuò)容也不是無限的,超過上限就報(bào)錯(cuò)(Integer.Max_Value),并把原數(shù)據(jù)copy到新數(shù)組,否則什么都不做

  3.在指定位置添加元素,并size++

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 單個(gè)添加集合元素

  對(duì)比后發(fā)現(xiàn),總體思路是沒問題的,但是作者為什么要加入下面這樣的容量上限處理邏輯?朋友們可以思考下,ArrayList的容量上限到底是Integer.MAX_VALUE還是MAX_ARRAY_SIZE?

  答案是:Integer.MAX_VALUE,依然還是它,并不是源碼中作者定義的MAX_ARRAY_SIZE,那就引申出一個(gè)問題了-MAX_ARRAY_SIZE存在的意義。

  我的理解是,這個(gè)上限的存在,減小了內(nèi)存溢出的概率。首先注釋中也提到了“Some VMs reserve some header words in an array”,意思就是有些虛擬機(jī)把頭信息保留在數(shù)組中,毫無疑問保存信息是要占空間的,也就是我實(shí)際數(shù)組空間可能是不足Integer.MAX_VALUE的,作者應(yīng)該是這樣的思路(我猜的~)“我可以基本確定實(shí)際空間不會(huì)小于MAX_ARRAY_SIZE(可能是分析了主流虛擬機(jī)的實(shí)現(xiàn)而得出的結(jié)論),因此設(shè)置個(gè)閾值,來確保不會(huì)內(nèi)存溢出,但如果這個(gè)容量還是不夠,那把剩下的風(fēng)險(xiǎn)很高的8也給你好了,至于是不是會(huì)溢出,天知道,反正我已經(jīng)盡力了!

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);private static int hugeCapacity(int minCapacity) {        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 單個(gè)刪除元素

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 批量刪除

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 序列化與反序列化

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 迭代器

三、 ArrayList使用建議

  1.當(dāng)你需要確定長(zhǎng)度的ArrayList時(shí),推薦使用該長(zhǎng)度初始化,理由是防止頻繁擴(kuò)容,擴(kuò)容相對(duì)而言是對(duì)性能影響相當(dāng)大的操作(請(qǐng)注意是相對(duì)而言,相對(duì)于常規(guī)的增刪改查等操作)。

  2.謹(jǐn)慎使用clone、toArray,對(duì)于源對(duì)象和方法返回對(duì)象,數(shù)據(jù)修改操作會(huì)相互影響。他們最終都執(zhí)行了System.arrayCopy(),都是“淺拷貝”:只copy了引用,所有對(duì)于其中一個(gè)引用的修改操作會(huì)傳遞到其他引用上。java的copy應(yīng)該都是“淺拷貝”,測(cè)試如下:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 測(cè)試clone

  3.謹(jǐn)慎使用subList,深入分析后會(huì)發(fā)現(xiàn),本質(zhì)原因同上面類似,都是相同的引用,所以數(shù)據(jù)修改操作會(huì)相互影響,如下例子:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 測(cè)試subList

  4.細(xì)心的朋友可能發(fā)現(xiàn)了,arrayList沒有提供對(duì)數(shù)組的構(gòu)造方法,但是我們知道array->arrayList是比較常見的需求,那如何做呢?辦法不止一種,選擇你喜歡的即可,如下:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) arrayToList

四、源碼相關(guān)擴(kuò)展(我能想到的可以深入思考的一些地方)

  1.快速失?。╢ail fast)機(jī)制:

  我們知道,ArrayList不是線程安全的集合類。意味著在并發(fā)操作時(shí),很容易發(fā)生錯(cuò)誤。按照常規(guī)思路,發(fā)現(xiàn)結(jié)果出錯(cuò),拋出異常即可。但在實(shí)際應(yīng)用中,我們并不滿足于此,有沒有一種檢測(cè)機(jī)制,在并發(fā)操作前進(jìn)行校驗(yàn),提前告訴我們可能發(fā)生的錯(cuò)誤呢?如你所料,就是我們提到的快速失敗機(jī)制。它是如何實(shí)現(xiàn)的呢?其實(shí)很簡(jiǎn)單,我們定義一個(gè)計(jì)數(shù)器modCount,這個(gè)計(jì)數(shù)器記錄所有集合結(jié)構(gòu)變動(dòng)的次數(shù),比如增加兩個(gè)元素就modCount+=2,再比如刪除3個(gè)元素就modCount+=3,這個(gè)計(jì)數(shù)器只能增加不能減少,然后我們?cè)趫?zhí)行一些需要快速失敗的操作時(shí)(比如:迭代器、序列化等等),執(zhí)行之前記錄下當(dāng)前modCount為expectedModCount,在適當(dāng)?shù)臅r(shí)候判斷expectedModCount、modCount是否相等就可以判斷這期間是否有過集合結(jié)構(gòu)的變動(dòng)。

  2.lambda表達(dá)式:讀源碼我們發(fā)現(xiàn),ArrayList中加入了許多支持lambda的方法,作為JDK8的亮點(diǎn)之一,應(yīng)該能夠熟練使用,然后再詳細(xì)分析lambda的實(shí)現(xiàn)機(jī)制我覺得也很有意思。

  3.jdk中常用JNI方法的實(shí)現(xiàn):上文我們?cè)趯?duì)clone方法做分析的時(shí)候,最終只分析到Object的native方法,我覺得有機(jī)會(huì)去看下常用的一些native方法的實(shí)現(xiàn)也是很有意思的,待研究。

五、總結(jié)一下

老實(shí)說,分析完ArrayList發(fā)現(xiàn),所花的時(shí)間大出我預(yù)計(jì),我一度認(rèn)為我已經(jīng)理解的很透徹了,但是在寫這篇文章的途中我又把差不多一半的源碼回顧了一遍(這真是個(gè)悲傷的故事),不過不管怎么說,這是個(gè)好的開始,后面一篇解析hashMap。