天天看點

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析

ArrayList集合源碼解析

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析
  • 所有集合類都位于java.util包下。Java的集合類主要由兩個接口派生而出:Collection和Map,Collection和Map是Java集合架構的根接口,這兩個接口又包含了一些子接口或實作類
JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析

今天我們了解下List 接口

  1. List集合代表一個有序集合,集合中每個元素都有其對應的順序索引。List集合允許使用重複元素,可以通過索引來通路指定位置的集合元素。
  2. List接口繼承于Collection接口,它可以定義一個允許重複的有序集合。因為List中的元素是有序的,是以我們可以通過使用索引(元素在List中的位置,類似于數組下标)來通路List中的元素,這類似于Java的數組。
  3. List接口為Collection直接接口。List所代表的是有序的Collection,即它用某種特定的插入順序來維護元素順序。使用者可以對清單中每個元素的插入位置進行精确地控制,同時可以根據元素的整數索引(在清單中的位置)通路元素,并搜尋清單中的元素。
  4. 實作List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

ArrayList底層實作原理

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析

ArrayList 底層基于數組實作的

/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
           

類中的屬性:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    /**
     *  版本号
     */
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 初始化長度
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空數組
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 空執行個體的共享空數組
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 元素數組
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 長度 預設為0
     * @serial
     */
    private int size;

  }           

構造解析:

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析
/**
     * 構造具有指定初始容量的空清單。
     */
    public ArrayList(int initialCapacity) {
         //初始容量 大于0則 elementData 如上設定成 定長數組
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
         // 如果等于 0 則預設空數組
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
          //如果為負數 抛出異常   java.lang.IllegalArgumentException: Illegal Capacity: -1
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }           
/**
     * 構造一個初始容量為10的空清單。如何配置設定10 下面介紹
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
    }           
/**
 * 構造一個包含指定元素的清單
 */
public ArrayList(Collection<? extends E> c) {
    // 是将c直接轉為Object[] 數組 指派給elementData
    elementData = c.toArray();
    //如果 elementData 等于0的情況初始化空數組
    if ((size = elementData.length) != 0) {
        // 每個集合的toarray()的實作方法不一樣,是以需要判斷一下,如果不是Object[].class類型,那麼久需要使用ArrayList中的方法去改造一下。
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}           

核心方法:

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析
add(E e)
/**
 * 将指定的元素追加到此清單的末尾。
 */
public boolean add(E e) {
        // 确定内部容量的方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }           
// 确定内部容量的方法
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //因為數組如果是空的話,minCapacity=size+1;其實就是等于1,空的數組沒有長度就存放不了,是以就将minCapacity變成10,也就是預設大小,
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //如上所訴,因為數組如果是空的話,minCapacity=size+1;其實就是等于1,空的數組沒有長度就存放不了,是以就将minCapacity變成10,第二次 elementData不是空數組了,也就是minCapacity代表着elementData中增加之後的實際資料個數,拿着它判斷elementData的length是否夠用,如果length不夠用,那麼肯定要擴大容量,不然增加的這個元素就會溢出。
        //好比我第一次進來我是需要10哥容量,你給我0個 我需要擴容
        if (minCapacity - elementData.length > 0)
           //arrayList能自動擴充大小的關鍵方法就在這裡了
            grow(minCapacity);
    }

 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; //擷取擴容前elementData的長度
        int newCapacity = oldCapacity + (oldCapacity >> 1); //新數組的大小=老數組大小+老數組大小的一半 相當于1.5倍
       // 判斷上面的擴容之後的大小newCapacity是否夠裝minCapacity個元素,不夠就将數組長度設定為需要的長度 
        if (newCapacity - minCapacity < 0) 
            newCapacity = minCapacity;
       //判斷新數組容量是否大于最大值  如果新數組容量比最大值(Integer.MAX_VALUE - 8)還大,那麼交給hugeCapacity()去處理
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            //copy數組,擴容機制的核心
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

//判斷是否抛異常  取最大值還是最大值-8
 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
           
void add(int,E)
/**
 * 在特定位置添加元素,也就是插入元素
 */
public void add(int index, E element) {
        rangeCheckForAdd(index);
        //跟上面的分析一樣
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //這個方法就是用來在插入元素之後,要将index之後的元素都往後移一位,
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //在目标位置上存放元素
        elementData[index] = element;
        size++;
    }           
//校驗下标合理 插入的位置肯定不能大于size 和小于0
//如果是,就報這個越界異常
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }           

斷點舉例:

無參構造

public static void main(String[] args) {

        List arrayList = new ArrayList();
        arrayList.add("13");
    }           
JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析
JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析

第二次

JDK1.8中ArrayList集合源碼解析ArrayList集合源碼解析
remove(int index)
// 通過删除指定位置上的元素
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        //通過索引直接找到該元素
        E oldValue = elementData(index);
        //計算要移動的位數。
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 用來移動元素
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将--size上的位置指派為null,讓gc(垃圾回收機制)更快的回收它。
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }           
// 通過删除指定位置上的元素
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        //通過索引直接找到該元素
        E oldValue = elementData(index);
        //計算要移動的位數。
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 用來移動元素
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将--size上的位置指派為null,讓gc(垃圾回收機制)更快的回收它。
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }           
remove(Object o)
//通過周遊元素删除,通過(o == null) 發現arrayList是可以存放null值得。
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }           
removeAll(Collection<?> c)
public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }           
//這個方法,用于兩處地方,如果complement為false,則用于removeAll如果為true,則給retainAll()用,retainAll()是用來檢測兩個集合是否有交集的。
   private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData; //将原集合,記名為A
        int r = 0, w = 0;   //r用來控制循環,w是記錄有多少個交集
        boolean modified = false;  
        try {
            for (; r < size; r++)
//參數中的集合C一次檢測集合A中的元素是否有,
                if (c.contains(elementData[r]) == complement)
//有的話,就給集合A
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
//如果contains方法使用過程報異常
            if (r != size) {
//将剩下的元素都指派給集合A,
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
//這裡有兩個用途,在removeAll()時,w一直為0,就直接跟clear一樣,全是為null。
//retainAll():沒有一個交集傳回true,有交集但不全交也傳回true,而兩個集合相等的時候,傳回false,是以不能根據傳回值來确認兩個集合是否有交集,而是通過原集合的大小是否發生改變來判斷,如果原集合中還有元素,則代表有交集,而元集合沒有元素了,說明兩個集合沒有交集。
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }           
clear():
//将elementData中每個元素都指派為null,等待垃圾回收将這個給回收掉,是以叫clear
public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }           
get(int index)
public E get(int index) {
        // 檢驗索引是否合法
        rangeCheck(index);

        return elementData(index);
    }           
set()
public E set(int index, E element) {
        // 檢驗索引是否合法
        rangeCheck(index);
        // 舊值
        E oldValue = elementData(index);
        // 賦新值
        elementData[index] = element;
        // 傳回舊值
        return oldValue;
    }           
indexOf()方法
public int indexOf(Object o) {
        if (o == null) {  //查找元素為空
            for (int i = 0; i < size; i++)  //循環,找到null傳回
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)  // 周遊數組,找到第一個和指定元素相等的元素,傳回下标
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }           

個人部落格:

http://blog.yanxiaolong.cn/