Java集合-ArrayList

先上圖:


ArrayList.png

ArrayList應(yīng)該是經(jīng)常用到的一個(gè)集合容器了,可以說(shuō)java集合的其他容器都沒(méi)用過(guò)也一定用過(guò)ArrayList笨农。ArrayList內(nèi)部是維護(hù)一個(gè)數(shù)組來(lái)存儲(chǔ)元素就缆。

ArrayList_grow.png

1.5倍擴(kuò)容,默認(rèn)10個(gè)元素谒亦,在添加第一個(gè)元素時(shí)才創(chuàng)建一個(gè)大小為10的數(shù)組竭宰。

源碼不多做介紹了空郊。下面說(shuō)一個(gè)重點(diǎn):

遍歷問(wèn)題

先拋出4個(gè)遍歷,如代碼所示:

      ArrayList<String> arrayList = new ArrayList<>();
        resetArrayList(arrayList);
        //1.正序刪除
        for (int i = 0; i < arrayList.size(); i++) {
            String re = arrayList.remove(i);
            System.out.println("re=" + re);
        }
        System.out.println("1.arrayList.size=" + arrayList.size());
        resetArrayList(arrayList);
        //2.后序刪除
        for (int i = arrayList.size() - 1; i >= 0; i--) {
            arrayList.remove(i);
        }
        System.out.println("2.arrayList.size=" + arrayList.size());
        resetArrayList(arrayList);
        //3.增強(qiáng)for循環(huán)刪除
//        for (String s : arrayList) {
//            arrayList.remove(s);
//        }
        System.out.println("3.arrayList.size=" + arrayList.size());
        //4.使用迭代器
        resetArrayList(arrayList);
        Iterator<String> it = arrayList.iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
        System.out.println("4.arrayList.size=" + arrayList.size());
//      Iterator<String> listIt = arrayList.listIterator(1);

ArrayList元素重置

 public static void resetArrayList(ArrayList<String> arrayList) {
        arrayList.clear();
        for (int i = 0; i < 3; i++) {
            arrayList.add("(" + i);
        }
    }

打印結(jié)果:

re=(0
re=(2
1.arrayList.size=1
2.arrayList.size=0
3.arrayList.size=3
4.arrayList.size=0

分析1:
現(xiàn)象:循環(huán)跑完后arrayList中還有一個(gè)元素切揭,“ (1 ”元素沒(méi)有被刪除
原因:刪除第一個(gè)元素狞甚,就是arrayList內(nèi)部的數(shù)組的第0下標(biāo)的元素,后續(xù)的元素整體向前移動(dòng)廓旬,也就是“(1”哼审、“(2”,移動(dòng)到0、1的位置孕豹,當(dāng)?shù)诙窝h(huán)到來(lái)時(shí)涩盾,i已經(jīng)是1了,此時(shí)對(duì)應(yīng)的值就是“(2”,所以“(2”被移除励背,移除元素的同時(shí)春霍,arrayList.size值也會(huì)跟著變化,因此不會(huì)發(fā)生越界異常叶眉,循環(huán)終止址儒。

顯然,實(shí)際上很少有這種需求竟闪。

分析2:
現(xiàn)象: 打印正常
原因: 從后向前刪除杖狼,不會(huì)使數(shù)組發(fā)生整體位移炼蛤,arrayList內(nèi)部移除元素時(shí)理朋,如果是最后一位元素直接置空,不需要移動(dòng)元素嗽上。i--也能對(duì)得上最后一位元素。

分析3:
現(xiàn)象: 直接拋出異常ConcurrentModificationException
原因: 看下方源碼兽愤,曾強(qiáng)for循環(huán)就是迭代器循環(huán),當(dāng)取得第一個(gè)元素并移除浅萧,去下一個(gè)元素就會(huì)檢查出有改動(dòng)哲思,因此拋出異常。

 public Iterator<E> iterator() {
// 使用增強(qiáng)for循環(huán)就是獲得一個(gè)迭代器
  // 這里直接new一個(gè)返回
        return new Itr();
    }
    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
 //這記錄操作數(shù)棚赔,modCount是記錄操作數(shù)徘郭,ArrayList添加丧肴、移除元素都++
        int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        public E next() {
   //s就是從這個(gè)方法返回的,這里檢查如果操作數(shù)不等于自己的記錄芋浮,說(shuō)明元素有添加或移除,
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        final void checkForComodification() {
  //操作數(shù)不一致江醇,拋出ConcurrentModificationException
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

分析4:
現(xiàn)象: 打印正常
原因: 看下方代碼分析,
1>hasNext方法判斷是否有下一個(gè)元素
2>next取當(dāng)前元素
這里沒(méi)有出ConcurrentModificationException是remove方法中重新記錄了modCount
3>remove移除

 public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
          //游標(biāo)向后移動(dòng)
            cursor = i + 1;
          //lastRet記錄當(dāng)前元素的下標(biāo)
            return (E) elementData[lastRet = i];
        }

        public void remove() {
        //這里沒(méi)有走next方法會(huì)直接拋出狀態(tài)不合法的異常
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
              //移除當(dāng)前元素
                ArrayList.this.remove(lastRet);
              //因?yàn)楫?dāng)前元素被移除陶夜,后續(xù)的元素會(huì)占用這個(gè)位置裆站,游標(biāo)要移回來(lái)
                cursor = lastRet;
            //lastRet重新賦值-1,假如這里沒(méi)有賦值-1宏胯,remove這里調(diào)用多次的話(huà)就會(huì)刪除多個(gè)元素
                lastRet = -1;
          //移除元素后modCount有變,重新記錄
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

如果想從指定下標(biāo)開(kāi)始做移除操作可以使用:

//從下標(biāo)1開(kāi)始
Iterator<String> listIt = arrayList.listIterator(1);

ConcurrentModificationException肩袍,這個(gè)異常叫并發(fā)修改異常,既然叫并發(fā)氛赐,那和多線(xiàn)程有關(guān)系么?

個(gè)人認(rèn)為是無(wú)直接關(guān)系

從代碼設(shè)計(jì)角度看滓侍,是為了數(shù)組的唯一,防止數(shù)組正在使用時(shí)被修改從而取到的元素不正確撩笆,如果不能保證數(shù)組的正確,直接拋個(gè)異常夕冲〔图茫看著辦吧。

間接關(guān)系分析:拋出異常的判斷是迭代器記錄與ArrayList記錄的不一致絮姆,多線(xiàn)程大概率是多個(gè)迭代器在操作秩霍,比如其中一個(gè)迭代器移除蚁阳,那modCount改變,那另一個(gè)迭代器操作時(shí)就會(huì)發(fā)現(xiàn)自己記錄的不一致螺捐,直接拋異常。

還有定血,ArrayList本就線(xiàn)程不安全的。

總結(jié):ArrayList就是維護(hù)一個(gè)數(shù)組的容器澜沟,包括如何擴(kuò)容、如何迭代遍歷以及增刪改查茫虽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市正什,隨后出現(xiàn)的幾起案子号杏,更是在濱河造成了極大的恐慌仿粹,老刑警劉巖场斑,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戳寸,死亡現(xiàn)場(chǎng)離奇詭異析珊,居然都是意外死亡渠驼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)百揭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蜓席,“玉大人,你說(shuō)我怎么就攤上這事厨内∶煜停” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵志鞍,是天一觀的道長(zhǎng)方仿。 經(jīng)常有香客問(wèn)我,道長(zhǎng)仙蚜,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任委粉,我火速辦了婚禮,結(jié)果婚禮上艳丛,老公的妹妹穿的比我還像新娘。我一直安慰自己氮双,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布送爸。 她就那樣靜靜地躺著,像睡著了一般袭厂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纹磺,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天亮曹,我揣著相機(jī)與錄音,去河邊找鬼照卦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛役耕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞬痘,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼板熊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惯悠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起克婶,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸭蛙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體娶视,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睁宰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柒傻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡青柄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出致开,到底是詐尸還是另有隱情,我是刑警寧澤双戳,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布校坑,位于F島的核電站,受9級(jí)特大地震影響耍目,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邪驮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一傲茄、第九天 我趴在偏房一處隱蔽的房頂上張望沮榜。 院中可真熱鬧,春花似錦蟆融、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至玛迄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓖议,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工勒虾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人从撼。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像婆翔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啃奴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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

  • 1.ArrayList的基本介紹 官方API:java.util.ArrayList 官方解釋永遠(yuǎn)是最權(quán)威的解釋 ...
    Thomas_Vader閱讀 729評(píng)論 0 5
  • 繼承關(guān)系 1. ArrayList概述 ArrayList是List接口的可變數(shù)組的實(shí)現(xiàn)最蕾。實(shí)現(xiàn)了所有可選列表操作,...
    Serializable_dx閱讀 229評(píng)論 0 1
  • 以下翻譯自JDK8中的ArrayList源碼 Java集合中的動(dòng)態(tài)數(shù)組ArrayList是頂層接口List的實(shí)現(xiàn),...
    Briseis閱讀 183評(píng)論 0 0
  • ArrayList 繼承結(jié)構(gòu) ArrayList的本質(zhì)是一個(gè)數(shù)組 這是ArrayList的屬性瘟则,包括默認(rèn)長(zhǎng)度枝秤,空數(shù)...
    Jiek閱讀 177評(píng)論 0 0
  • Array源碼分析 首先分析new ArrayList<>() new ArrayList()時(shí),會(huì)創(chuàng)建一個(gè)Obj...
    不給起這個(gè)名字閱讀 195評(píng)論 0 0