天天看點

Java|ArrayList源碼分析

本文結構:

1.介紹特點

2.基本方法

3.重點源碼分析

1.介紹特點

ArrayList:

是List的一個具體實作子類,是List接口的一個數組實作 (裡面必定維護了一個數組)。

預設初始容量10, 擴容機制1.5倍。(數組必然有初始容量和擴容機制)

有序。

允許null元素。

允許重複元素。

線程不安全。

2.基本方法

關鍵字 簡介

add

增加

contains

判斷是否存在

get

擷取指定位置的對象

indexOf

擷取對象所處的位置

remove

删除

set

替換

size

擷取大小

toArray

轉換為數組

addAll

把另一個容器所有對象都加進來

clear

清空

3.重點源碼分析

源碼解析:

import java.util.Arrays;
import java.util.ConcurrentModificationException;

public class MyArrayList<E>{

    private static final int DEFAULT_CAPACITY = 10;// 預設容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量

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


    private Object[] elements;//底層維護的數組
    private int size;//容器内對象的個數
    private int modCount;//記錄ArrayList這個對象被修改的次數
    //構造方法
    public MyArrayList() {
        this.elements = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }



    //擷取清單存的資料量
    public int getSize() {
        return size;
    }

    /**
     *增加内容
     * @param e
     * @return
     */
    public boolean add(E e) {
        //建立一個預設為10的數組,若小則擴容



        ensureCapacityInternal(size+1);
        //確定容量夠,則添加資料,并講資料大小加1.
        elements[size]=e;
        size++;
        return true;
    }

    //該方法來確定數組的容量夠用,若為建立則建立數組并賦予預設值。
    private void ensureCapacityInternal(int minCapacity) {
        //若數組為空(還未添加資料)
        if (elements==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            //則選出預設值和最小容量的最大值
            minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);
            //隻add方法其實沒必要比較,主要是addAll()這個方法需要比較
        }

        modCount++;//修改次數加一

        //判斷一下是否需要擴容,若資料+1大于目前數組,則需要擴容
        if(minCapacity-elements.length>0){
            grow(minCapacity);//調用擴容方法
        }
    }

    /**
     * 具體的擴容方法
     * @param minCapacity
     */
    private void grow(int minCapacity){
        int oldCapacity=elements.length;//擷取原始數組的長度
        int newCapacity=oldCapacity+(oldCapacity>>1);//擴容 1+0.5 倍
        //若擴容後的長度比所需要的最低長度還要小,則直接把擴容的長度更改為最低所需長度
        if (newCapacity-minCapacity<0){
            newCapacity=minCapacity;
        }
        //若擴容完的新長度比規定的最大容量還大,則要進一步判斷,并進一步修改數組大小
        if (newCapacity-MAX_ARRAY_SIZE>0){
            //若所需的容量竟然小于0,說明超過Int最大值,越界了,抛出異常
           if (minCapacity<0){
               throw new OutOfMemoryError();
           }
           //若所需的容量不超過Int最大值,則再判斷
            if (minCapacity>MAX_ARRAY_SIZE){//若所需的大于數組要求的而小于Int最大值
                newCapacity=Integer.MAX_VALUE;//直接指派Int最大值
            }else{//否則隻有小于數組最大值這一種情況了,賦予數組最大值即可
                newCapacity=MAX_ARRAY_SIZE;
            }
        }
        elements = new Object[newCapacity];

    }

    /**
     * 根據下标添加元素
     * @param index 下标
     * @param element 要替換的元素
     */
    public void add(int index,E element){
        //先檢查一下下标是否越界
        rangeCheck(index);
        //確定數組長度夠用
        ensureCapacityInternal(size+1);
        //這裡調用System裡面一個靜态的本地方法(效率更高),用于數組的複制。這裡把下标後面的都向後移了。
       /* public static native void arraycopy(Object src,  int srcPos, Object dest, int destPos, int length);
            Object src:the source array. 源數組
            int srcPos:starting position in the source array. 在源數組中,開始複制的位置
            Object dest:the destination array. 目标數組
            int destPos:starting position in the destination data. 在目标數組中,開始指派的位置
            int length:the number of array elements to be copied. 被複制的數組元素的數量*/
        System.arraycopy(element, index, element, index + 1, size - index);//等于說把下标後面的都向後移了。
                        //原數組 開始的位置 目标數組   目标數組中開始複制的位置     被複制的數量

        //由于下标後的都往後移動了一位,是以index的位置為空,插進去即可
        elements[index]=element;
        size++;
    }

    /**
     * 檢查下标是否越界
     * @param index
     */
    private void rangeCheck(int index) {
        if (index>size||index<0){//若插入的下标比資料實際數量大或者輸入的是個不合常理的負數
            throw new IndexOutOfBoundsException((outOfBoundsMsg(index)));//抛出異常并傳回要插入的下标數和實際資料的數量
        }
    }

    /**
     * 用于越界異常的資訊抛出
     * @param index 輸入下标
     * @return 傳回下标和實際資料的數量大小
     */
    private String outOfBoundsMsg(int index) {
        return "Index:"+index+",Size:"+size;
    }

    /**
     * 清空清單所有元素
     * 由垃圾回收器(GC)進行回收
     */
    public void clear(){
        //記錄對象修改次數
        modCount++;
        //讓數組所有的資料都變為null, clear to let GC do its work
        for (int i=0;i<size;i++){
            elements[i]=null;
        }
        //這時長度都為0了
        size=0;
    }

    /**
     * 根據下标更改資料
     * @param index 下标
     * @param element 想要更改的資料
     * @return 傳回原始資料
     */
    public E set(int index, E element) {
        //首先判斷是否越界
        rangeCheck(index);

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

    /**
     * 檢查操作數是否一緻,防止并發修改錯誤
     *//*
    private void checkForComodification() {
        if (MyArrayList.this.modCount != this.modCount)//線程中的和儲存的資料不一樣
            throw new ConcurrentModificationException();//不一緻則抛出異常
    }*/

    /**
     * 簡簡單單的根據下标查詢資料操作
     * @param index 下标
     * @return 傳回資料資訊
     */
    public E get(int index) {
        rangeCheck(index);
        return (E) elements[index];
    }

    /**
     * 根據下标删除元素
     * @param index  要删除的下标
     * @return 傳回要删除的資料
     */
    public E remove (int index){
        rangeCheck(index);//檢查一下下标是否合适

        modCount++;//操作數加一
        E oldValue= (E) elements[index];

        //定義numMoved,記錄一下index後面的數,等會要把他們都移動了
        int numMoved=size-index-1;
        if (numMoved>0){
            System.arraycopy(elements,index+1,elements,index,numMoved);//通過本地靜态的複制方法,來實作資料的前移。
        }
        elements[size]=null;//都前移了以後原本最後一位數的記憶體設為null,讓GC回收了 clear to let GC do its work
        size--;
        return oldValue;
    }

    /**
     * 找到相應的元素并對其進行删除
     * @param o
     * @return 傳回true表示删除完成,false表示删除失敗
     */
    public boolean remove(Object o) {
        if (o == null) {//删除null資料
            for (int index = 0; index < size; index++)
                if (elements[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {//删除正常的資料
            for (int index = 0; index < size; index++)
                if (o.equals(elements[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;//若沒找到該資料則傳回false
    }

    /**
     * 無需進行判斷下标是否合适的快速删除方法
     * @param index
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elements, index+1, elements, index,
                    numMoved);
        elements[--size] = null; // clear to let GC do its work
    }

    /**
     * 判斷資料是否為空(清單裡沒資料)
     * @return 若為空則傳回true
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 查詢是否有該資料
     * @param o
     * @return 若有傳回true
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;//若該索引為正,則表明有該資料,且第一次出現的位置在indexOf
    }

    /**
     *傳回指定元素的第一次出現的索引
     * @param o
     * @return 若存在則傳回第一次出現的位置,若沒有則傳回-1
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elements[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     *傳回指定元素的最後一次出現的索引
     * @param o
     * @return 若存在則傳回最後一次出現的位置,若沒有則傳回-1
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)//從後向前周遊即可
                if (elements[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "MyArrayList{" +
                "elements=" + Arrays.toString(elements) +
                '}';
    }
}