《超詳細开仰!ArrayList源碼圖文解析》

不詩意的女程序媛不是好廚師~
轉(zhuǎn)載請注明出處诡蜓,F(xiàn)rom李詩雨---[https://blog.csdn.net/cjm2484836553/article/details/104329665]

昨天玩了很久的arraycopy捺球,今天讓我們來看看ArrayList的源碼吧粟耻。

是的,又到了發(fā)揮我拙劣的畫技的時候了~

先預(yù)覽一下本篇文章的大綱:

在這里插入圖片描述

下面我們就開始ArrayList的源碼圖解之旅吧锨咙,先從增刪改查講起语卤,然后再講ArrayListIterator的源碼,當(dāng)然這之間我們還會穿插講一下幾個大坑及注意事項~

1.增

ArrayList添加元素的操作酪刀,涉及到2個方法 add(E object)add(int index, E object) 粹舵。

1.1 add(E object) 直接在尾部添加一個元素

add(E object)這個方法,是直接添加一個元素骂倘,是在尾部進行插入眼滤。

如果原數(shù)組的大小不夠會先進行擴容。

最終將數(shù)據(jù)添加在尾部历涝,同時大小加1.

源碼如下:


在這里插入圖片描述

1.2 add(int index, E object) 在指定位置添加一個元素

add(int index, E object)這個函數(shù)是指在 index的位置 诅需,插入一個新元素漾唉。

  • 我們先看插入操作的核心步驟:

    從要插入的位置開始的所有數(shù)據(jù),都要往先后挪一位堰塌;然后再把要插入的數(shù)據(jù)放進去赵刑。

    畫了個圖 方便大家理解:


    在這里插入圖片描述
  • 了解了插入的核心步驟之后,我們就來看看 add(int index, E object)的源碼中场刑,插入一個元素到指定位置具體是怎么實現(xiàn)的吧般此。

    一、要先檢測要插入的位置index是否合法牵现。

    二恤煞、判斷數(shù)組是否夠用:

    ? a. 如果數(shù)組夠用,即 s<a.length時施籍,直接調(diào)用arraycopy()居扒,將從index開始的所有數(shù)據(jù)都往后挪一位。(關(guān)于arraycopy的使用我上一篇已經(jīng)詳細講過了丑慎,所以這里我們應(yīng)該知道他是從后往前依次往后挪一位過去的)喜喂。

    ? b. 如果數(shù)組滿了,即s>=a.length時,這里就要

    ? (1)先將原數(shù)組進行擴容,生成新的數(shù)組身隐;

    ? (2)將原數(shù)組中index之前的數(shù)據(jù)復(fù)制到新數(shù)組對應(yīng)的位置中去话浇。

    ? (3)將原數(shù)組中index之后的數(shù)據(jù)往后挪一位的移動到新的數(shù)組中去夷野。

    ? (4)將新數(shù)組賦給array。

    三、將要添加的元素放在index的位置,同時有效數(shù)據(jù)個數(shù)size+1影斑。

    源代碼見下圖:

    在這里插入圖片描述

補充:擴容規(guī)則

我們都知道,數(shù)組的大小是不可變的机打,而ArrayList的大小是可變的矫户。而ArrayList底層也是用到了數(shù)組,那ArrayList是如何做到大小可以動態(tài)變化的呢残邀?

答案就是通過擴容的方式皆辽。

也就是Object[] newArray = new Object[newCapacity(s)];這句代碼。

現(xiàn)在我們來具體看看newCapacity(s)的實現(xiàn):

在這里插入圖片描述

在這里插入圖片描述

這里我們做一下解釋:

當(dāng)目前的容量currentCapacity<6 時芥挣,increment=12;

否則的話 increment等于currentCapacity的一半驱闷。

最終返回的大小是 currentCapacity+increment

也就是說空免,擴容之后要么是在原有的基礎(chǔ)上 +12空另,要么就是擴大為原來的1.5倍。

2.刪

ArrayList的刪除操作鼓蜒,我們也來看兩個 remove(Object object)remove(int index)痹换。

2.1 remove(int index) 刪除指定位置的元素

  • 同樣,我們先來看看刪除的核心操作:

    對應(yīng)的代碼就是System.arraycopy(a, index + 1, a, index, --s - index);這句代碼都弹,

    即 :如果要刪除【index】位置的元素娇豫,那就要把【index】之后的所有元素都往前挪一位,覆蓋掉index原本的位置.

    同樣來畫個圖來幫助大家理解:


    在這里插入圖片描述
  • 了解了核心的操作之后畅厢,我們就來看看 remove(int index) 的源代碼吧:

    在這里插入圖片描述

2.2 remove(Object object) 刪除某個已知元素

remove(Object object) 刪除某個已知元素冯痢。

上面我們已經(jīng)知道了刪除指定位置元素的操作,那如果要刪除某個已知元素的話框杜,我們是不是也應(yīng)該先找到它對應(yīng)的位置浦楣,然后再進行刪除呢。

  • 那問題來了咪辱,怎么找到元素對應(yīng)的位置呢振劳?

    對,通過循環(huán)遍歷油狂,并進行比較 找到對應(yīng)的index.


    在這里插入圖片描述

▲有個坑历恐!

▲【注意】:

調(diào)用remove方法, 會专筷, 且只會 刪除第一個與傳入對象通過equals方法判斷相等的元素弱贼。

如果傳入null, 則刪除掉第一個null元素磷蛹。

所以吮旅, 如果自定義類想要使用remove方法從列表刪除某個指定值對象, 還需要實現(xiàn)該類型自己的equals方法才行味咳!

▲還有個坑庇勃!

ArrayList是可以順序刪除節(jié)點的,但是槽驶!如果使用普通for循環(huán)匪凉,必須是從后往前刪。不能從前往后刪捺檬。

我們先來看一下【錯誤示范】:

ArrayList list=new ArrayList();
list.add("a");
list.add("b");
list.add("c");

System.out.println("刪除前:"+list.toString());

//順序刪除節(jié)點錯誤示范:從前往后刪----會刪不干凈
for (int i=0;i<list.size();i++){
    list.remove(i);
}
System.out.println("刪除后:"+list.toString());

【錯誤結(jié)果展示】:

在這里插入圖片描述

【出錯原因分析】:

要順序刪除ArrayList的全部節(jié)點再层,如果我們從前往后的順序刪除,先刪除【0】位置的數(shù)據(jù)堡纬,但是由于刪除的時候是從后往前挪一位進行刪除的聂受,所以【0】的位置又會被下一個位置的數(shù)據(jù)覆蓋上,實際上【0】還是有數(shù)據(jù)的烤镐。再畫一張圖來方便大家理解:


在這里插入圖片描述

【正確的做法】:

要想順序刪除ArrayList的所有節(jié)點蛋济,且采用普通的for循環(huán),那只能從后往前刪炮叶,這樣就不會出問題碗旅。

在這里插入圖片描述

3.改渡处、查

ArrayList修改數(shù)據(jù)很簡單,調(diào)用的是set(int index, E object)方法祟辟,直接修改對應(yīng)位置的數(shù)據(jù)即可医瘫。

在這里插入圖片描述

ArrayList獲取數(shù)據(jù)就跟簡單了,由于是順序表有下標(biāo)旧困,直接取出對應(yīng)下標(biāo)數(shù)據(jù)就可以了醇份。


在這里插入圖片描述

4.ArrayList的3種遍歷方式

ArrayList的遍歷我們有三種方式:for循環(huán)增強for循環(huán)迭代器三種方式吼具。

(當(dāng)然僚纷,增強for循環(huán)其實還是用迭代器實現(xiàn)的,這一點我們可以通過反編譯來進行驗證拗盒。)

ArrayList arrayList = new ArrayList();
arrayList.add("情人節(jié)");
arrayList.add("快樂");
arrayList.add("我");
arrayList.add("對");
arrayList.add("自己說~");

System.out.println("for循環(huán)的方式遍歷:");
for (int i = 0; i < arrayList.size(); i++) {
    System.out.print(arrayList.get(i));
}

System.out.println();
System.out.println("---------------------------------");
System.out.println("增強for循環(huán)的方式遍歷:");
for (Object s : arrayList) {
    System.out.print((String) s);
}

System.out.println();
System.out.println("---------------------------------");
System.out.println("迭代器的方式遍歷:");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next());
}

我們來看一下打印結(jié)果:


在這里插入圖片描述

5.迭代器 ArrayListIterator源碼解讀

好的怖竭,上面我們知道了ArrayList可以使用迭代器進行遍歷。

  • 那為什么它可以使用迭代器這種方式呢陡蝇?

    這個我們又要去看源碼了侵状,跟進arrayList.iterator()的 iterator()方法,我們會發(fā)現(xiàn)ArrayList有一個內(nèi)部類ArrayListIterator毅整。而 ArrayListIterator 實現(xiàn)了 Iterator 接口趣兄,所以才可以使用迭代器這種方式進行迭代。

在這里插入圖片描述

現(xiàn)在我們就來具體看看ArrayListIterator的相關(guān)源代碼和注意事項吧悼嫉。

首先艇潭,我們可以看到ArrayListIterator實現(xiàn)了Iterator這個接口。

5.1 提一下什么是 Iterator (迭代器)戏蔑?

我們都知道在Java中蹋凝,有很多的數(shù)據(jù)容器,這些的操作又有很多的共性总棵。而迭代器就是給各種容器提供了公共的操作接口鳍寂。這樣就使得對容器的操作有了規(guī)范性。

在Iterator接口中定義了三個方法:

  • hasNext(): 如果仍有元素可以迭代情龄,就返回true.

  • next(): 返回迭代的下一個元素迄汛。

  • remove(): 從集合中移除返回的最后一個對象。(可選操作)

源碼如下:


在這里插入圖片描述

5.2 ArrayListIterator中的坑▲▲▲

ArrayListIterator 的源碼其實并不難理解骤视,就是實現(xiàn)了 Iterator 中的三個方法鞍爱。

但是這里有一個▲坑▲大家需要注意,那就是:

每當(dāng)我們使用迭代器遍歷元素時专酗,如果使用迭代器以外的方法修改了元素內(nèi)容(如刪除元素)睹逃,那就會拋出ConcurrentModificationException的異常。

讓我先看一下現(xiàn)象祷肯,然后再從源碼角度找原因沉填。

錯誤代碼示例:

        ArrayList arrayList = new ArrayList();
        arrayList.add("a");
        arrayList.add("b");
        arrayList.add("c");

        System.out.println("移除前:" + arrayList);
        Iterator<String> iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            if ("c".equals(iterator.next())) {
                arrayList.remove("c");
            }
        }
        System.out.println("移除后:" + arrayList);

        //注意增強for使用的也是迭代器
        //所以一下這種操作也會報ConcurrentModificationException
        //for (Object o : arrayList) {
        //    arrayList.remove(o);
        //}
        //System.out.println("移除后2:" + arrayList);

報錯顯示:


在這里插入圖片描述

好的疗隶,現(xiàn)象我們已經(jīng)看到了,那現(xiàn)在我們就來看看錯誤的原因吧翼闹。

我們要先來了解一下這幾個變量的含義:


在這里插入圖片描述

然后我們來看一下何種情況下會報錯:


在這里插入圖片描述

先分析一下報錯原因:

在我們使用 ArrayLis 的 iterator() 方法獲取到迭代器進行遍歷時斑鼻,會把 ArrayList 當(dāng)前狀態(tài)下的 modCount 賦值給 ArrayListIterator類的 expectedModCount 屬性。

如果我們在迭代過程中橄碾,使用了 ArrayList 的 remove()方法卵沉,這時 modCount 就會加 1 颠锉,但是迭代器中的expectedModCount 并沒有變化法牲,當(dāng)我們再使用迭代器的next()方法時,它就會報ConcurrentModificationException的錯琼掠。

最后我們再來比較一下 ArrayListIterator中的 remove()方法和ArrayList自己的remove()方法的不同之處拒垃,驗證一下錯誤發(fā)生的原因:

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

?所以我們得到的啟示是:

每當(dāng)我們使用迭代器遍歷元素時,要使用迭代器自己的刪除方法瓷蛙,而不能使用迭代器以外的方法修改了元素內(nèi)容悼瓮,否則會造成expectedModCount和modCount的值不一致,從而拋出ConcurrentModificationException的異常艰猬。

此外横堡,我們還要注意一下,增強for循環(huán)其實也是使用的迭代器冠桃,所以也要注意同樣的問題命贴。

積累點滴,做好自己~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末食听,一起剝皮案震驚了整個濱河市胸蛛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌樱报,老刑警劉巖葬项,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迹蛤,居然都是意外死亡民珍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門盗飒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穷缤,“玉大人,你說我怎么就攤上這事箩兽〗蚋兀” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵汗贫,是天一觀的道長身坐。 經(jīng)常有香客問我秸脱,道長,這世上最難降的妖魔是什么部蛇? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任摊唇,我火速辦了婚禮,結(jié)果婚禮上涯鲁,老公的妹妹穿的比我還像新娘巷查。我一直安慰自己,他們只是感情好抹腿,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布岛请。 她就那樣靜靜地躺著,像睡著了一般警绩。 火紅的嫁衣襯著肌膚如雪崇败。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天肩祥,我揣著相機與錄音后室,去河邊找鬼。 笑死混狠,一個胖子當(dāng)著我的面吹牛岸霹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播将饺,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贡避,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俯逾?” 一聲冷哼從身側(cè)響起贸桶,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桌肴,沒想到半個月后皇筛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡坠七,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年水醋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彪置。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拄踪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拳魁,到底是詐尸還是另有隱情惶桐,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站姚糊,受9級特大地震影響贿衍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜救恨,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一贸辈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肠槽,春花似錦擎淤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至筋栋,卻和暖如春炊汤,著一層夾襖步出監(jiān)牢的瞬間正驻,已是汗流浹背弊攘。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留姑曙,地道東北人襟交。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像伤靠,于是被迫代替她去往敵國和親捣域。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容