Java集合源碼分析之ArrayList

前言

ArrayList可以說(shuō)是我們?nèi)粘i_(kāi)發(fā)中特別經(jīng)常使用到的集合類了跨蟹,本文將結(jié)合JDK1.8源碼從線程安全近刘、數(shù)據(jù)結(jié)構(gòu)擒贸、初始化、擴(kuò)容觉渴、增刪改查介劫、特性總結(jié)等幾個(gè)部分去分析ArrayList

線程安全

ArrayList是非線程安全的,不支持并發(fā)案淋。我們可以從它的數(shù)據(jù)迭代器ArrayList$Itr中可以得知蜕猫,當(dāng)產(chǎn)生線程安全問(wèn)題時(shí)會(huì)拋出拋出ConcurrentModificationException異常。內(nèi)部是通過(guò)一個(gè)modCount變量記錄集合的變化哎迄,在擴(kuò)容與刪除及清空等操作都會(huì)將modCount自增回右,以此來(lái)標(biāo)記集合的改變隆圆。

如何實(shí)現(xiàn)線程安全

1.通過(guò)Colletions.synchronizedCollection獲取線程安全的集合對(duì)象
2.使用并發(fā)庫(kù)下的CopyOnWriteArrayList(讀寫分離)

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; // 記錄初始modCount

        ......

        @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();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        // 在next與remove前都會(huì)檢查modCount是否改變
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

數(shù)據(jù)結(jié)構(gòu)

ArrayList采用數(shù)組對(duì)數(shù)據(jù)進(jìn)行管理,大量采用了數(shù)組的copy實(shí)現(xiàn)增刪翔烁。因此對(duì)于性能上渺氧,ArrayList的增刪操作較慢,由于數(shù)組是連續(xù)的內(nèi)存空間所以改查較快蹬屹。

初始化

提供了三個(gè)重載構(gòu)造方法侣背,除了默認(rèn)的無(wú)參構(gòu)造器外還可以指定初始容量以及初始集合內(nèi)容。需要注意的是在參數(shù)無(wú)效時(shí)(初始容量指定為0或者集合)都跟默認(rèn)構(gòu)造器結(jié)果一致慨默,將數(shù)據(jù)指定為默認(rèn)的空數(shù)組贩耐,這樣的好處是當(dāng)該list沒(méi)有真正去使用時(shí)不會(huì)浪費(fèi)內(nèi)存。

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { // 構(gòu)造數(shù)據(jù)
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { // 指定為空數(shù)組
            this.elementData = EMPTY_ELEMENTDATA;
        } else { // 拋出異常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    public ArrayList() { // 空構(gòu)造器直接指定數(shù)組為空數(shù)組
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray(); // 將集合轉(zhuǎn)為數(shù)組
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                // 通過(guò)Arrays.copyOf方法進(jìn)行數(shù)組復(fù)制厦取,得到新數(shù)組潮太。底層是通過(guò)System.arraycopy進(jìn)行的,本質(zhì)是native
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

擴(kuò)容

ArrayList的擴(kuò)容其實(shí)就是數(shù)組的擴(kuò)容虾攻,而實(shí)際上數(shù)組是沒(méi)辦法擴(kuò)容的铡买。所以只能夠構(gòu)造新的數(shù)組,然后把原數(shù)組的數(shù)據(jù)進(jìn)行copy到新數(shù)組霎箍,以此完成擴(kuò)容操作奇钞。默認(rèn)為原數(shù)組的1.5倍

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // modCount之前提到了,用于并發(fā)異称担控制
        // overflow-conscious code 需要擴(kuò)容(需要的容量>=當(dāng)前容量)
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 默認(rèn)擴(kuò)容為之前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果1.5倍仍然無(wú)法滿足景埃,則直接擴(kuò)容為minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 邊界判斷
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 數(shù)組復(fù)制完成擴(kuò)容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

增操作

增操作首先進(jìn)行一些基本條件判斷,包括是否擴(kuò)容顶别、指定的index是否越界谷徙。在add(int index, E element)方法中也需要進(jìn)行擴(kuò)容,因?yàn)榭赡芴砑有略睾蠼钕模聅ize可能剛好等于當(dāng)前容量。賦值就很簡(jiǎn)單了图呢,因?yàn)槭菙?shù)組結(jié)構(gòu)条篷,直接通過(guò)index指定即可,唯一需要注意的是在賦值之前的數(shù)組copy

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 將數(shù)組進(jìn)行拷貝蛤织,留出index位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 對(duì)index位置進(jìn)行賦值
        elementData[index] = element;
        size++;
    }

刪操作

刪除操作有兩種形式赴叹,指定對(duì)象或者指定位置,注意此處也修改modCount變量了指蚜。指定位置:首先越界檢查乞巧,接著通過(guò)數(shù)組copy將指定index的位置覆蓋,最后指定--size為null摊鸡。指定對(duì)象:分兩種情況绽媒,一種是對(duì)象為null蚕冬,一種是對(duì)象非空。

    public E remove(int index) {
        rangeCheck(index); // 越界檢查

        modCount++;
        E oldValue = elementData(index); // 刪除的目標(biāo)對(duì)象

        int numMoved = size - index - 1; // 數(shù)據(jù)需要移動(dòng)的數(shù)量
        if (numMoved > 0) // copy
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 最后一個(gè)賦值null
        elementData[--size] = null; // clear to let GC do its work
        // 返回目標(biāo)對(duì)象
        return oldValue;
    }

    public boolean remove(Object o) {
        // 指定對(duì)象的remove操作是通過(guò)循環(huán)比較
        if (o == null) { // null
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true; // 返回的boolean表示是否移除成功
                }
        } else { // 非空
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

改操作

很簡(jiǎn)單是辕,越界檢查 - > 取原值 - > 修改 - > 返回原值

    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

查操作

很簡(jiǎn)單囤热,越界檢查 - > 返回對(duì)象

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

特性總結(jié)

1.數(shù)據(jù)結(jié)構(gòu)為數(shù)據(jù),增刪慢获三、改查快
2.非線程安全旁蔼,通過(guò)modCount控制
3.ArrayList與Vector的區(qū)別:Vector是線程安全的,通過(guò)synchronized關(guān)鍵字疙教;Vector擴(kuò)容是容量翻倍棺聊,而ArrayList是原容量1.5倍

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贞谓,隨后出現(xiàn)的幾起案子限佩,更是在濱河造成了極大的恐慌,老刑警劉巖经宏,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犀暑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡烁兰,警方通過(guò)查閱死者的電腦和手機(jī)耐亏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沪斟,“玉大人广辰,你說(shuō)我怎么就攤上這事≈髦” “怎么了择吊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)槽奕。 經(jīng)常有香客問(wèn)我几睛,道長(zhǎng),這世上最難降的妖魔是什么粤攒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任所森,我火速辦了婚禮,結(jié)果婚禮上夯接,老公的妹妹穿的比我還像新娘焕济。我一直安慰自己,他們只是感情好盔几,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布晴弃。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪上鞠。 梳的紋絲不亂的頭發(fā)上际邻,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音旗国,去河邊找鬼枯怖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛能曾,可吹牛的內(nèi)容都是我干的度硝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼寿冕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蕊程!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起驼唱,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤藻茂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后玫恳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體辨赐,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年京办,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掀序。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惭婿,死狀恐怖不恭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情财饥,我是刑警寧澤换吧,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站钥星,受9級(jí)特大地震影響沾瓦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谦炒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一贯莺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧编饺,春花似錦乖篷、人聲如沸响驴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至秽誊,卻和暖如春鲸沮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锅论。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工讼溺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人最易。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓怒坯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親藻懒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剔猿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 數(shù)據(jù)結(jié)構(gòu)與算法移步數(shù)據(jù)結(jié)構(gòu)--容器匯總(java & Android)內(nèi)容: ArrayList 概述 Array...
    凱玲之戀閱讀 843評(píng)論 0 4
  • ArrayList 源碼分析 不知道各位朋友,還記得開(kāi)工前制定的學(xué)習(xí)目標(biāo)么嬉荆? 有沒(méi)有一直為了那個(gè)目標(biāo)廢寢忘食呢归敬?繼...
    醒著的碼者閱讀 1,363評(píng)論 5 11
  • 首先看一下集合體系繼承樹(shù) Collection接口 Collection是最基本的集合接口,一個(gè)Collectio...
    SnowDragonYY閱讀 939評(píng)論 0 2
  • ArrayList是在Java中最常用的集合之一鄙早,其本質(zhì)上可以當(dāng)做是一個(gè)可擴(kuò)容的數(shù)組汪茧,可以添加重復(fù)的數(shù)據(jù),也支持隨...
    ShawnIsACoder閱讀 570評(píng)論 4 7
  • 江南煙雨霧朦朧限番, 昔日風(fēng)景無(wú)蹤影舱污。 眾里尋她千百度, 一...
    情愛(ài)千秋閱讀 385評(píng)論 39 43