ArrayList源碼分析

1、核心變量

// 序列化ID
    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
    // 當前arraylist集合的大小,也就是elementData數組中數據元素的個數
    private int size;
複製代碼

2、構造函數

/**
     * 一:無參構造方法
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
	
    /**
     * 二:攜帶一個int類型的參數,指定arraylist的初始容量
     */
    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);
        }
    }
複製代碼
一、無參構造

能夠看到,在構造方法中直接將 elementData 指向 DEFAULTCAPACITY_EMPTY_ELEMENTDATA空數組,這個時候該ArrayList的size爲初始值0。java

一、有參構造

進行參數校驗:
參數大於0,則指定數組長度;
參數等於0,則爲空數組;
參數小於0,則拋異常。數組

3、add方法

/**
     * 一:直接添加數據元素到arraylist的尾部
     */
    public boolean add(E e) {
	//是否擴容、記錄modCount
        ensureCapacityInternal(size + 1);  // Increments modCount!!
	//把值添加到數組尾部
        elementData[size++] = e;
        return true;
    }
----------------------------------------------------------------------

private void ensureCapacityInternal(int minCapacity) {
	//minCapacity=size+1;
	//minCapacity表示若是添加成功後,數組的最小長度
	//若是爲無參構造
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
	    //取默認長度和minCapacity的最大值,即10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
	//是否擴容
        ensureExplicitCapacity(minCapacity);
    }
----------------------------------------------------------------------

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

        // 若是添加後最小長度大於 數組長度
        if (minCapacity - elementData.length > 0)
	    //擴容
            grow(minCapacity);
    }
----------------------------------------------------------------------
 private void grow(int minCapacity) {
        //獲取數組長度
        int oldCapacity = elementData.length;
	//1.5倍擴容
        int newCapacity = oldCapacity + (oldCapacity >> 1);
	//1.5倍擴容也不夠用
        if (newCapacity - minCapacity < 0)
	    //擴容後長度=minCapacity
            newCapacity = minCapacity;
	//簡直最大長度
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 複製
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
複製代碼

代碼中已經有註釋了,很清晰。bash

4、remove方法

/*
    * 一:根據角標進行remove操做
    */
    public E remove(int index) {
        // 1. 對角標越界進行判斷
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        // 2.modCount自增1
        modCount++;
        // 3.獲取到指定下角標位置的數據
        E oldValue = (E) elementData[index];
        // 4.計算須要移動的元素個數
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 5. 指定角標位置後的元素前移一位,效率低
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 6.將size自減1,並將數組末尾置爲null,便於垃圾回收
        elementData[--size] = null; // clear to let GC do its work
        // 7.最後將所要刪除的數據元素returnreturn oldValue;
    }

    /*
     * 二:根據數據元素進行remove操做
     */
    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;
    }
----------------------------------------------------------------------
private void fastRemove(int index) {
        // 1.modCount的值自增1
        modCount++;
        // 2.計算須要移動的元素個數
        int numMoved = size - index - 1;
        if (numMoved > 0)
             // 3. 指定角標位置後的元素前移一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 4.將size自減1,並將數組末尾置爲null,便於垃圾回收
        elementData[--size] = null; // clear to let GC do its work
    }

複製代碼

5、set方法

public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
	    //檢查modCount
            checkForComodification();

            try {
                ArrayList.this.set(lastRet, e);
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
----------------------------------------------------------------------
public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
----------------------------------------------------------------------
final void checkForComodification() {
	if (expectedModCount != ArrayList.this.modCount)
		throw new ConcurrentModificationException();
	}
複製代碼

6、get方法

public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset + index);
        }
----------------------------------------------------------------------
E elementData(int index) {
        return (E) elementData[index];
    }
複製代碼

7、clear方法

public void clear() {
        modCount++;

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

        size = 0;
    }
複製代碼

8、contains方法

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
----------------------------------------------------------------------
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;
    }
複製代碼

未命名文件.jpg併發

9、fail-fast機制

ail-fast機制是集合中的一種錯誤檢測機制,咱們在操做集合中常常會遇到 java.util.ConcurrentModificationException異常,產生該異常的緣由就是fail-fast機制。函數

實現:若是在迭代期間計數器被修改,那麼hasNext或next將拋出concurrentModificationException
缺點:這種檢查是沒有同步的狀況下進行的,所以可能會看到失效的計數值,而迭代器可能並無意識到已經發生了修改。這是一種設計上的權衡,從而下降了併發修改操做的檢測代碼對程序性能帶來的影響。性能

10、可能的併發問題

  1. add()
    EX:a、100個元素,可能最後數組長度不到100。
    兩個線程併發add,對索引位置5的地方几乎同時賦值,第二個線程會覆蓋第一個線程的值,而且size少了1個。
    b、假設有兩個線程在操做同一個ArrayList,線程一執行step1(容量足夠)後被掛起,線程二執行add()方法後,線程一被喚醒,這時線程一由於已經再也不判斷容量是否足夠(已經判斷過),執行step2就會出現數組越界
  2. 數組容量檢測的併發問題
  3. remove 兩個線程有可能會想要刪除同一個內容,一個線程先完成的時候第二個線程再刪,就找不到這個內容了
相關文章
相關標籤/搜索