ArrayList源碼解析

在這裏插入圖片描述
可以看到ArrayList 繼承了 AbstractList
實現List,RandomAccess,Cloneable,序列化接口

怎麼理解capacity和size
可以看到我們的初始容量爲15
但是它的size爲4
這就說明size指的是 實際存儲了多少。
在這裏插入圖片描述
elementData: Object[]類型的動態數組

變量

	// 序列化ID
	private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 初始容量 10 
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空的元素數據  且是Obejct數組
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默認容量 空的數組
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * elementData 裏面存放的  Object數組
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 實際大小
     */
    private int size;
    /**
     * 數組最大 大小爲  2^31-9
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

構造函數

   /**
     * 指定初始容量
     */
    public ArrayList(int initialCapacity) {
    	// 指定初始容量 > 0
        if (initialCapacity > 0) {
        	// 創建初始容量大小的元素
            this.elementData = new Object[initialCapacity];
            // 指定容量大小爲0
        } else if (initialCapacity == 0) {
        	// 空的數據
            this.elementData = EMPTY_ELEMENTDATA;
            // 否則拋出異常
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 無參構造器  數組中爲空
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
	* 1)將collection對象轉換成數組,然後將數組的地址的賦給elementData。
	* 2)更新size的值,同時判斷size的大小,如果是size等於0,直接將空對象EMPTY_ELEMENTDATA的地址賦給elementData
	* 3)如果size的值大於0,則執行Arrays.copy方法,把collection對象的內容(可以理解爲深拷貝)copy到elementData中。
	* 注意:this.elementData = arg0.toArray(); 這裏執行的簡單賦值時淺拷貝,所以要執行Arrays,copy 做深拷貝
    */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

前面一直對.length的概念有點混淆
特定做了一個測試:
發現length就是數組new出來的大小
如果在其中沒有添加

在這裏插入圖片描述
那麼Object類型裏面 未添加元素的下表默認爲null

在這裏插入圖片描述

很多方法的形參裏面有minCapacity:
minCapacity:直譯過來就是最小容量,即指我們添加元素後,或者自我指定容量的最小容量值。

擴容

 /**
  * trim實際大小:修整  實際大小
  * ArrayList的容量調整爲實際元素的大小
  */
 public void trimToSize() {
 		// 記錄List結構的修改次數 用於fast-fail
        modCount++
        //實際大小 < elementData數組長度
        if (size < elementData.length) {
        	// 實際大小爲0?
        	// 爲0  定義爲空的elementData數組
        	// 不爲0 我們就定義size長的elementData數組
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

    /**
     * 方法名:確定容量
     * 判斷所需的ro
     * Explicit : 明確的,清晰的
     * minCapacity: 所需長度
	 * 總結:
	 * elementData數組裏面爲空  那麼就執行ensureExplicitCapacity方法
	 * 裏面不爲空 但所需的長度大於默認容量10  執行ensureExplicitCapacity方法
     * 
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

	/**
	 *  方法名:計算容量
	 *  數組默認爲空 返回默認容量和我們所需容量中的最大值
	 *  數組不爲null  返回所需容量
	 */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }


	/**
	 * 確定容量的內部
	 * internal: 內部的
	 * 
	 */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
	/**
	 * 確定擴容?
	 * explicit:明確的,清晰的
	 */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 所需容量 > 數組的長度 擴容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }



    /**
     *  真正的擴容
     */
    private void grow(int minCapacity) {
        // 舊容量
        int oldCapacity = elementData.length;
        // 新容量 = 舊容量 +舊容量*0.5 = 舊容量*1.5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 新容量小於所需容量
        // 新容量 = 所需容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 新容量 》 最大數組大小  
        // 所需容量大於最大數組大小 新容量 = Integer的最大值
        // 所需容量小於最大數組大小 新容量 = 最大數組大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 拷貝一個新數組 大小爲新容量
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
	/**
	 * huge:巨大的
	 */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

添加元素

在這裏插入圖片描述
這個方法其實和上面的add類似,該方法可以按照元素的位置,指定位置插入元素,具體的執行邏輯如下:
1)確保數插入的位置小於等於當前數組長度,並且不小於0,否則拋出異常
2)確保數組已使用長度(size)加1之後足夠存下 下一個數據
3)修改次數(modCount)標識自增1,如果當前數組已使用長度(size)加1後的大於當前的數組長度,則調用grow方法,增長數組
4)grow方法會將當前數組的長度變爲原來容量的1.5倍。
5)確保有足夠的容量之後,使用System.arraycopy 將需要插入的位置(index)後面的元素統統往後移動一位。
6)將新的數據內容存放到數組的指定位置(index)上

    /**
     *  確認元素內部容量大小 所需容量爲size+1
     *  e 賦值給 數組後一個下標
     *  返回true值
     */
    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!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

ArrayList總結

底層數組實現,使用默認構造方法初始化出來的容量是10
擴容的長度是原長度的1.5倍
實現了RandomAccess接口,底層又是數組,get讀取元素性能很好
線程不安全,所有的方法均不是同步方法也沒有加鎖,因此多線程下慎用
順序添加很方便
刪除和插入需要複製數組 性能很差(可以使用LinkindList)

爲什麼ArrayList的elementData是用transient修飾的?
transient修飾的屬性意味着不會被序列化,也就是說在序列化ArrayList的時候,不序列化elementData。
爲什麼要這麼做呢?

elementData不總是滿的,每次都序列化,會浪費時間和空間
重寫了writeObject 保證序列化的時候雖然不序列化全部 但是有的元素都序列化

Int和Integer的區別

Integer是一個包裝類,而int是一個基礎數據類型
在Integer有最大值和最小值

	// 二進制結果爲0
    @Native public static final int   MIN_VALUE = 0x80000000;
	// 二進制結果爲2147483647   2^31 -1 
    @Native public static final int   MAX_VALUE = 0x7fffffff;

==參考 原文:https://blog.csdn.net/fighterandknight/article/details/61240861 ==

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章