ArrayList源碼分析(JDK1.8)

  1. ArrayList的成員變量分析。
    private static final Object[] EMPTY_ELEMENTDATA = {}; 

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  //空構(gòu)造器時(shí),默認(rèn)為容量為0的空數(shù)組

    transient Object[] elementData;//集合實(shí)際 存放數(shù)據(jù)的數(shù)組,不需要序列化,故用transient來修飾

    private int size;// list實(shí)際大小

    private static final int DEFAULT_CAPACITY = 10;// 默認(rèn)list容量

    protected transient int modCount = 0;// 這個(gè)在迭代遍歷并發(fā)刪除的的時(shí)候就可以顯露問題

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  1. ArrayList的常見創(chuàng)建方式。
    2.1 空構(gòu)造函數(shù)
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;// 空構(gòu)造器時(shí),默認(rèn)為容量為0的空數(shù)組
    }

2.2 初始化集合大小的構(gòu)造函數(shù)(如果已知集合需要容納元素大小,建議此方法創(chuàng)建)

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "
                    + initialCapacity);
        }
    }

3 集合操作:增加元素
3.1 添加元素(依次在數(shù)組最后一個(gè)元素添加)

public boolean add(E e) {
        // 第一步:確認(rèn)list是否已滿,如果未滿,直接添加,如果已滿則看是否需要擴(kuò)容
        ensureCapacityInternal(size + 1);
        // 第二步:添加元素
        elementData[size++] = e;// 添加元素
        return true;
    }

我們知道集合優(yōu)于數(shù)組的其中一個(gè)原因就是數(shù)組在聲明的時(shí)候大小就確定了,不可變。而集合在添加元素可以自動(dòng)擴(kuò)容的。那么究竟集合是如何實(shí)現(xiàn)自動(dòng)擴(kuò)容的呢?
第一步:先來確定一下當(dāng)前集合容量是否滿足添加一個(gè)元素需求。

/**
     * 開始擴(kuò)容,擴(kuò)容的時(shí)候就需要考慮到什么時(shí)候開始擴(kuò)容,擴(kuò)容多大比較合適。實(shí)際上問題歸根于解決擴(kuò)容多大的問題。
     * 
     * @param minCapacity
     */
    public void ensureCapacityInternal(int minCapacity) {
        // 解決第一個(gè)問題:什么時(shí)候需要擴(kuò)容?
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

第二步:什么時(shí)候需要擴(kuò)容?

    /**
     * 開始擴(kuò)容
     * 
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        if (minCapacity > elementData.length) {
            // 如果擴(kuò)容大于新建數(shù)組的長度(數(shù)組的長度,非集合中元素的大?。?,這個(gè)時(shí)候需要擴(kuò)容。
            grow(minCapacity);
        }
    }

第三步:如何擴(kuò)容?

/**
     * 實(shí)際擴(kuò)容操作
     * 
     * @param minCapacity
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length; // 原來數(shù)組長度
        int newCapacity = oldCapacity + (oldCapacity >> 1);// 擴(kuò)容新數(shù)組大小,為什么選擇擴(kuò)容原來的1.5倍?難道是這是為了節(jié)省空間最佳方案。
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity; // 從這里可以看出來,實(shí)際擴(kuò)容大小,有可能不止1.5倍大小。
        }
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            // 如果MAX_ARRAY_SIZE大小都不能滿足擴(kuò)容之后的新容量大小,則需要進(jìn)一步擴(kuò)容
            newCapacity = hugeCapacity(minCapacity);
        }

        elementData = Arrays.copyOf(elementData, newCapacity);// 最后一步最重要的是,把原來數(shù)組復(fù)制到擴(kuò)容之后的數(shù)組中。這里需要使用到j(luò)dk本地庫,
                                                                // 不可避免需要IO操作,如果頻繁的擴(kuò)容,會(huì)影響ArrayList的性能,因此如果我們知道list大小,可以直接構(gòu)造出指定容量的數(shù)組
    }

/**
     * 當(dāng)擴(kuò)容MAX_ARRAY_SIZE都不能滿足新的容量時(shí)
     * 
     * @param minCapacity
     * @return
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // 溢出
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE
                : MAX_ARRAY_SIZE;// 從這里可以看出list能夠存儲(chǔ)的最大容量是整型范圍內(nèi)的最大容量。
    }

3.1 指定位置添加元素

@Override
    public void add(int index, E element) {
        //第一步:查看index是否越界
        rangeAddCheck(index);
        //第二步:擴(kuò)容
        ensureCapacityInternal(size + 1);
        //第三步:移動(dòng)index位置(包含index)之后的元素,本質(zhì)是將index往index+1的元素開始往后移動(dòng),然后將新值賦予到index位置處。
        System.arraycopy(elementData, index, elementData, index+1, size-index);
        //第四步:添加index新值
        elementData[index] = element;
        size ++;
    }

總結(jié)一下添加的思路:檢查ArrayList是否有足夠的容量來存儲(chǔ)待添加元素,如果容量不夠需要擴(kuò)容操作。然后創(chuàng)建一個(gè)擴(kuò)容之后新容量的數(shù)組,并將原來數(shù)組復(fù)制到新數(shù)組中,原來數(shù)組引用指向新的數(shù)組。
3.2 刪除指定位置元素

@Override
    public E remove(int index) {
        rangeCheck(index);
             
        modCount ++;
        
        int movedNum = size - index -1;//需要移動(dòng)多少個(gè)元素
        E oldValue = elementData(index);
        if (movedNum > 0) {
            System.arraycopy(elementData, index+1, elementData, index, movedNum);//從index+1開始復(fù)制elementData,然后放入elementData的index位置,總共復(fù)制movedNum個(gè)元素
        }
        
        elementData[size--] = null;//把最后一個(gè)元素置空,便于垃圾回收
        return oldValue;
    }

主要思路就是,將index之后的位置統(tǒng)一向前移動(dòng)一位,并將數(shù)組最后一個(gè)元素置null,以便于垃圾回收器回收。
3.3 修改指定位置元素

@Override
    public E set(int index, E element) {
        rangeCheck(index);
        
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;//返回的是原來index位置上的值
    }

3.4 獲取元素

@Override
    public E get(int index) {
        rangeCheck(index);
        
        return elementData(index);
    }

@SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

/**
     * 檢查是否越界
     */
    private void rangeCheck(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("越界啦!");
        }
    }
    
    private void rangeAddCheck(int index){
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("越界啦!");
        }
    }

3.5 查找元素

    @Override
    public int indexOf(Object o) {
        //由于ArrayList可以放置null元素,因此在查找的時(shí)候需要分情況討論
        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])) {//這里使用的object的equals()方法,如果又必須要需重寫
                    return i;
                }
            }
        }
        
        return -1;
    }

@Override
    public int lastIndexOf(Object o) {
        // ArrayList允許null,因此通過循環(huán)遍歷查找元素時(shí),需要分為null,非null值,主要區(qū)別就是根據(jù)相等判斷方法不一致。
        //如何才能這個(gè)元素最后出現(xiàn)的位置呢?那么我們遍歷該數(shù)組的時(shí)候,需要從后面往前面遍歷,這樣第一次遇見的這個(gè)元素位置就是該元素最后出現(xiàn)的位置
        if (o == null) {
            for (int i = size-1; i >= 0; i--) {
                if (elementData[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = size-1; i >= 0; i--) {
                if (o.equals(elementData[i])) {
                    return i;
                }
            }
        }
        return -1;
    }

其實(shí),分析到這里我們就可以看出ArrayList的特點(diǎn)了:

  • 自動(dòng)擴(kuò)容機(jī)制保證操作ArrayList時(shí)容量大小可變。
  • 可通過數(shù)組下標(biāo)訪問元素,查詢快,增刪操作慢,涉及擴(kuò)容,數(shù)組復(fù)制等操作。
  • 線程不安全,效率高。
  • 元素有序,且可為null。


    圖片.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ArrayList感覺是在做項(xiàng)目中遇到最多的,先從添加元素開始講起,請(qǐng)跟隨我的思路1.add(E e),最常用的,...
    何甜甜在嗎閱讀 448評(píng)論 0 0
  • ArrayList是在Java中最常用的集合之一,其本質(zhì)上可以當(dāng)做是一個(gè)可擴(kuò)容的數(shù)組,可以添加重復(fù)的數(shù)據(jù),也支持隨...
    ShawnIsACoder閱讀 624評(píng)論 4 7
  • 一.線性表 定義:零個(gè)或者多個(gè)元素的有限序列。也就是說它得滿足以下幾個(gè)條件:??①該序列的數(shù)據(jù)元素是有限的。??②...
    Geeks_Liu閱讀 2,768評(píng)論 1 12
  • 前言 今天來介紹下ArrayList,在集合框架整體框架一章中,我們介紹了List接口,ArrayList繼承了A...
    嘟爺MD閱讀 4,549評(píng)論 6 31
  • 佯裝堅(jiān)強(qiáng) 拼命忙碌 可是作為一個(gè)人 終究會(huì)有那一刻 所有疲勞如洪水般決堤 負(fù)能量充斥全身 多想能有個(gè)人說說話 可不...
    涂抹土閱讀 216評(píng)論 1 3

友情鏈接更多精彩內(nèi)容