ArrayList解析

今天回頭研究一下 java 中的列表 ArrayList ,它表示用數(shù)組來存儲(chǔ)一系列的對(duì)象,并且支持 ?List 的特性.

我們今天研究一下它的基礎(chǔ)屬性, 添加方法, 以及與 lambda 相關(guān)的部分.

屬性

默認(rèn)容量

表示新建一個(gè) ArrayList 的初始化空間.

    private static final int DEFAULT_CAPACITY = 10;

空列表

提供兩個(gè)空的數(shù)組列表,供代碼中使用以便于給參數(shù)賦值.

    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

存儲(chǔ)數(shù)組

在數(shù)組泛型中存儲(chǔ)的數(shù)據(jù),用數(shù)組來實(shí)現(xiàn).

    transient Object[] elementData; // non-private to simplify nested class access

列表大小

size 表示列表所包含的元素個(gè)數(shù),默認(rèn)是0

    private int size;

擴(kuò)容

其實(shí)如果提到了 ArrayList 我們很多時(shí)候都會(huì)最先提到 ArrayList 的擴(kuò)容方法,那么我們這邊就先研究一下有關(guān)擴(kuò)容方法的一串方法鏈

ArrayList 有一個(gè)名為 grow 的匿名方法,顧名思義,就是擴(kuò)容,讓我們來一起看一下(核心):

    private void grow(int minCapacity) {
        //我們?nèi)〉搅嗽械娜萘?        int oldCapacity = elementData.length;
        //新的容量暫時(shí)等于原有的容量的 大約 一點(diǎn)五 倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        //取 新容量 和 傳入的最小容量 中的較小者(為了更小的內(nèi)存占用)
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;

        //MAX_ARRAY_SIZE的值是最大的 Integer - 8 (2147483639)
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            /*
            綜合起來我們可以知道 minCapacity 是大于 2147483639 的
            下面方法會(huì)返回一個(gè) 合適 的較大數(shù)
            */
            newCapacity = hugeCapacity(minCapacity);

        //方法獲取了一個(gè)新的數(shù)組,長(zhǎng)度為新的長(zhǎng)度,前面的元素不變, 延長(zhǎng)的部分使用默認(rèn)值
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

在這其中我們調(diào)用了 hugeCapacity 方法:

    private static int hugeCapacity(int minCapacity) {

        //負(fù)數(shù)拋出內(nèi)存溢出異常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();

        //返回 MAX_VALUE 或者 MAX_VALUE-8
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

?所以我們得出了,在列表長(zhǎng)度沒有變的特別巨大的情況下,新的數(shù)組長(zhǎng)度一般情況下會(huì)變成原有長(zhǎng)度的1.5倍

所以當(dāng)我們?cè)谡{(diào)用 add 方法的時(shí)候,會(huì)發(fā)生如下事情:

    public boolean add(E e) {
        //調(diào)用了增加長(zhǎng)度的方法,具體增加到多少其實(shí)不重要
        ensureCapacityInternal(size + 1);
        //給 size 的下一個(gè)標(biāo)記位賦值
        elementData[size++] = e;?
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        //空集合的時(shí)候的處理,其實(shí)不怎么重要
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        //核心就是接著往下走了
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        //長(zhǎng)度不對(duì)的時(shí)候有用,但是在 add 環(huán)境下只是傳值給 grow
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

所以我們會(huì)發(fā)現(xiàn)其實(shí)核心代碼還是 grow 方法,其它的都是處理一些特殊情況的.

查找

ArrayList 中有幾個(gè)類似的方法,他們都有一個(gè)特性,就是能夠從列表中找到一個(gè)特定的元素并且執(zhí)行某些操作, 例如:

    public boolean contains(Object o);
    public int indexOf(Object o);
    public int lastIndexOf(Object o);
    public boolean remove(Object o);

這些方法都非常類似,所以我們現(xiàn)在研究一下 indexOf 方法:

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

小結(jié)

很顯然我們能夠得到結(jié)論:

  • 我們?cè)趯ふ覍?duì)象的時(shí)候會(huì)用 for 循環(huán)遍歷數(shù)組中的每一個(gè)元素
  • 尋找對(duì)象的時(shí)候我們使用的是 equals 方法

函數(shù)方法

ArrayList 中有幾個(gè)方法,它以函數(shù)接口作為參數(shù), 分別是如下方法:

    public void forEach(Consumer<? super E> action);
    public boolean removeIf(Predicate<? super E> filter);
    public void replaceAll(UnaryOperator<E> operator);
    public void sort(Comparator<? super E> c);

以下我們會(huì)對(duì)上面幾個(gè)方法舉例使用或者進(jìn)行代碼分析:

forEach

forEach 方法要求傳入一個(gè) consumer, 然后依據(jù)我們傳入的 lambda 去對(duì)列表的每一個(gè)元素進(jìn)行一次消費(fèi)行為.

源碼

    public void forEach(Consumer<? super E> action) {
        //函數(shù)的非空校驗(yàn)
        Objects.requireNonNull(action);
        //記錄當(dāng)前操作次數(shù),獲取當(dāng)前的數(shù)據(jù)和大小
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;

        //對(duì)每一個(gè)元素進(jìn)行循環(huán),并且執(zhí)行對(duì)應(yīng)的消費(fèi)函數(shù)
        //modCount 是用來防止多線程的時(shí)候出現(xiàn)問題的, 這個(gè)是一個(gè)?確保相對(duì)線程安全的方案
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        //一旦在遍歷結(jié)束之后發(fā)現(xiàn)中途列表發(fā)生了變化,那么拋出異常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

栗子

    @Test
    public void base05(){
        List<Integer> arrayList = Arrays.asList(1,2,3,4,5);
        arrayList.forEach(i -> {
            System.out.println(i);
        });
    }

removeIf

removeIf 方法要求傳入一個(gè)? predicate 并且將返回值為 true 的移除方法.和 stream 的 filter 有些類似.

栗子

    @Test
    public void base06(){
        List<Integer> arrayList = Arrays.asList(1,2,3,4,5);
        ArrayList<Integer> newList = new ArrayList<Integer>(arrayList);
        newList.removeIf(i -> {
            return i % 2 == 0;
        });
        newList.forEach(i -> {
            System.out.println(i);
        });
    }

replaceAll

replaceAll 方法接受一個(gè) UnaryOperator 函數(shù),返回相同類型的數(shù)據(jù)來替換原有數(shù)據(jù)

源碼

    public void replaceAll(UnaryOperator<E> operator) {
        //非空校驗(yàn)
        Objects.requireNonNull(operator);
        //記錄當(dāng)前操作次數(shù),獲取當(dāng)前的數(shù)據(jù)和大小
        final int expectedModCount = modCount;
        final int size = this.size;

        //對(duì)每一個(gè)元素進(jìn)行循環(huán),并且執(zhí)行對(duì)應(yīng)的函數(shù)
        //modCount 是用來防止多線程的時(shí)候出現(xiàn)問題的, 這個(gè)是一個(gè)?確保相對(duì)線程安全的方案
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            elementData[i] = operator.apply((E) elementData[i]);
        }
        //一旦在遍歷結(jié)束之后發(fā)現(xiàn)中途列表發(fā)生了變化,那么拋出異常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

栗子

    @Test
    public void base07(){
        List<Integer> arrayList = Arrays.asList(1,2,3,4,5);
        ArrayList<Integer> newList = new ArrayList<Integer>(arrayList);
        newList.replaceAll(i -> {
            return 10 - i;
        });
        newList.forEach(i -> {
            System.out.println(i);
        });
    }

sort

sort 方法需要傳入一個(gè) comparator 接口對(duì)象的函數(shù),表示用來比較兩個(gè)數(shù)的大小的函數(shù)

源碼

    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

栗子

    @Test
    public void base08(){
        List<Integer> arrayList = Arrays.asList(934,2,13,43,5);
        ArrayList<Integer> newList = new ArrayList<Integer>(arrayList);
        newList.sort((a,b) -> {
            if(a > b){ return 1;}
            else {return -1;}
        });
        newList.forEach(i -> {
            System.out.println(i);
        });
    

有關(guān) ArrayList 的學(xué)習(xí)就到這里.
之后我們會(huì)繼續(xù)學(xué)習(xí) LinkedList 的相關(guān)內(nèi)容.

歡迎關(guān)注我的博客: 既然來了就坐坐吧
小站剛開始起步,歡迎您的駕到.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • List是Java中非常常用的數(shù)據(jù)結(jié)構(gòu),而ArrayList是其中最常用的實(shí)現(xiàn),ArrayList正如它的名稱一樣...
    whthomas閱讀 450評(píng)論 1 6
  • 一、概要 ArrayList是一個(gè) 動(dòng)態(tài)數(shù)組,線程不安全的,允許值為null 底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,采用默認(rèn)構(gòu)造方法...
    史云龍閱讀 556評(píng)論 0 0
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,518評(píng)論 0 3
  • 基于JDK1.8只列出關(guān)鍵方法,主要關(guān)注默認(rèn)情況、初始化、擴(kuò)容、add、remove。 get(int index...
    風(fēng)風(fēng)風(fēng)箏閱讀 635評(píng)論 0 5
  • 前幾天看到一句話,內(nèi)容是我上班就是為了掙錢,別跟我談什么理想,我最大的理想就是不上班。應(yīng)該說是我最大的理想就是不上...
    我的夢(mèng)想就是不上班閱讀 2,594評(píng)論 1 1