ArrayList & LinkedList源碼解析


本文記錄ArrayList & LinkedList源碼解析 基於JDK1.8

ArrayList

ArrayList實現了List接口 所有擁有List接口所有方法 可以看成可'調節'的數組 可以包含任何類型數據(包括null,可重復)ArrayList線程不是安全的

類結構

ArrayList類主要成員變量:

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

  	// 初始化容量(數組初始長度) 10
    private static final int DEFAULT_CAPACITY = 10;

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

  	// 也是空數組,和EMPTY_ELEMENTDATA區分開
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 裝ArrayList的數組
    transient Object[] elementData; // non-private to simplify nested class access

	// 數組長度
    private int size;
}

方法解析

構造函數

public ArrayList()

public ArrayList() {
		// elementData為DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


public ArrayList(int initialCapacity)

public ArrayList(int initialCapacity) {
		// initialCapacity初始化容量 由調用者傳入
	
		// 如果initialCapacity大於0
        if (initialCapacity > 0) {
			// elementData為 Object類型的  initialCapacity大小的數組
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
			// 如果初始值為0 elementData為 EMPTY_ELEMENTDATA 空數組
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
			// initialCapacity 小於0  拋錯
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}

public ArrayList(Collection<? extends E> c)

public ArrayList(Collection<? extends E> c) {
		
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
}

創建一個包含指定集合c數據的ArrayList 上面為什么要多此一舉使用
Arrays.copyOf(elementData, size, Object[].class)復制一遍數組呢?這是因為在某些情況下
調用集合的toArray()方法返回的類型並不是Object[].class 比如:

Long[] array1 = {1L, 2L};
List<Long> list1 = Arrays.asList(array1);
Object[] array2 = list1.toArray();
System.out.println(array2.getClass() == Object[].class); // false

List<Long> list2 = new ArrayList<>();
System.out.println(list2.toArray().getClass() == Object[].class); // true

add方法
add(E e)向數組尾部添加元素

public boolean add(E e) {
		// 確定數組容量 size 類型為int 默認值(即初始值)為 0 
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}


private void ensureCapacityInternal(int minCapacity) {
		// minCapacity = size + 1 = 1    
		// elementData = {}
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}


private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
			// Math.max返回最大的數組  DEFAULT_CAPACITY = 10   minCapacity = 1 返回 10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}


private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
		
        // minCapacity = 10  
		// elementData = {}
  		// 10 - 0 > 0
        if (minCapacity - elementData.length > 0)
			// 擴容
            grow(minCapacity);
}


private void grow(int minCapacity) {
        // oldCapacity = 0
        int oldCapacity = elementData.length;
		// >> 右移運算符 相當於newCapacity為oldCapacity的1.5倍 
		// oldCapacity / 2 此處 0 + 0 / 2 還是等於 0 
        int newCapacity = oldCapacity + (oldCapacity >> 1);
		// newCapacity = 0  minCapacity = 10  0 - 10 < 0 條件成立
        if (newCapacity - minCapacity < 0)
			// newCapacity = 10
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
		// // 復制到新數組 數組容量為10
        elementData = Arrays.copyOf(elementData, newCapacity);
 }

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
		// MAX_ARRAY_SIZE常量值為Integer.MAX_VALUE - 8 通過
    	// 這段邏輯我們可以知道,ArrayList最大容量為Integer.MAX_VALUE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}    

綜述 可以知道

  1. 任何一個空的數組 第一次添加元素的時候 內部數組容量將被擴容為10
  2. 擴容時,newCapacity為oldCapacity的1.5倍
  3. 容量最大值為Integer.MAX_VALUE - 8
  4. 尾部添加元素不用移動任何元素 所以速度快

add方法

add(int index, E element)用於在指定位置添加元素

public void add(int index, E element) {
		// 數組下標檢查
        rangeCheckForAdd(index);

		// 跟上面add(E e) 一樣  確定數組容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
		// 將原來index后面的所有元素往后面移動一個位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
		// index位置放入新元素
        elementData[index] = element;
		// 數組長度 + 1
        size++;
}


private void rangeCheckForAdd(int index) {
		// 如果下標大於數組長度 或者 小於0 拋錯
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

這里涉及到元素移動 所以速度較慢

get方法
public E get(int index) 獲取指定索引位置

public E get(int index) {
		// 檢查index
        rangeCheck(index);
		// 直接返回數組指定位置元素
        return elementData(index);
}

private void rangeCheck(int index) {
		// 如果index 大於 數組長度  拋錯
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

直接返回索引所在元素 速度較快

set方法
set(int index, E element)向指定位置添加元素

public E set(int index, E element) {
		// 檢查index 跟get方法一樣
        rangeCheck(index);
		
		// 獲取老的index所在位置元素
        E oldValue = elementData(index);
		// 設置新值
        elementData[index] = element;
		// 返回老值
        return oldValue;
}

set方法不涉及元素移動和遍歷 所以速度快

remove方法
remove(int index)

public E remove(int index) {
		// 檢查index 跟get方法一樣
        rangeCheck(index);
		
        modCount++;
		// 獲取指定位置需要被刪除的元素
        E oldValue = elementData(index);
		
        int numMoved = size - index - 1;
        if (numMoved > 0)
			// elementData數組 index + 1的位置為復制起始點
			// 從index位置開始 復制 numMoved個元素
			// 即被刪除元素去除后 其后面的其他元素(如果有)都要向前移一位  覆蓋index處的元素
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
		// 設置elementData最后一位元素為 null
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;



public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

# 參數解釋
# src 原數組 顧名思義  從哪個數組復制
# srcPos 從原數組哪個位置開始復制
# dest 目標數組
# destPos 目標數組起始位置
# length 復制的長度
}

remove(Object o)


// 區分 null和非null的元素 都是通過fastRemove方法來刪除
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;
}


// 刪除邏輯和remove(int index)一樣  覆蓋index位置元素
private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
}

LinkedList

類結構

LinkedList底層采用雙向鏈表結構存儲數據 允許重復數據和null值 長度沒有限制
image

每個節點用內部類Node表示

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
}

其中包含 item(存儲數據)、next(后繼節點)、prev(前繼節點) Node可以分布在各個內存位置 通過prev 和 next來維護

LinkedList包含如下成員變量

// 元素個數 默認為 0 
transient int size = 0;

// 頭節點 該節點必須滿足(first == null && last == null) || (first.prev == null && first.item != null)
// 即頭節點滿足 前繼節點為 null 且item(存儲數據)不為空
transient Node<E> first;

// 尾節點 該節點必須滿足(first == null && last == null) || (last.next == null && last.item != null)
// 即尾節點滿足 后繼節點為 null 且item(存儲數據)不為空
transient Node<E> last;

方法解析

構造函數

public LinkedList()

public LinkedList() {}

空參構造函數 size 默認 為 0 每次添加新元素都需要創建Node類型節點

public LinkedList(Collection<? extends E> c)

public LinkedList(Collection<? extends E> c) {
		// 調用空參構造函數
        this();
        addAll(c);
}


public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
}


public boolean addAll(int index, Collection<? extends E> c) {
		// 檢查index 必須要大於 0 且小於 size 否則拋錯
        checkPositionIndex(index);

		// 將c轉成一個數組
        Object[] a = c.toArray();
		// 獲取數組的長度
        int numNew = a.length;
        if (numNew == 0)
            return false;

		// 定義Node類型節點 pred  succ
        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
		
		// 循環創建節點,設置prev,next指向
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
}


private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}


private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
}

add方法

add(int index, E element)

public void add(int index, E element) {
		// 檢查index 同構造函數
        checkPositionIndex(index);
		
		// 如果index 和 鏈表大小相同
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
}


void linkLast(E e) {
		// 定義一個Node類型變量   獲取最后一個節點元素
        final Node<E> l = last;

		// 實例化Node類  prev為原鏈表最后一個節點 next為null
        final Node<E> newNode = new Node<>(l, e, null);

		// 將新節點賦值給last  成為 最后一個節點
        last = newNode;

        if (l == null)
			// 如果老的鏈表中 最后一個節點為null 證明是一個空鏈表
			// 此時 第一個節點和最后一個節點是相同的
            first = newNode;
        else
			// l作為老的最后一個節點 在新節點生成后 需要將newNode 賦值給 老的next(后繼節點)
            l.next = newNode;

		// 元素個數 + 1
        size++;
        modCount++;
}


// 采用二分法遍歷每個Node節點 直到找到index位置的節點
Node<E> node(int index) {
		// size >> 1 相當於 size / 2
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
}


void linkBefore(E e, Node<E> succ) {
        // succ為原鏈表指定index位置的節點 獲取其prev節點
        final Node<E> pred = succ.prev;

		// 創建新節點 prev 為原鏈表指定index位置節點的prev  next為原鏈表指定index位置節點
        final Node<E> newNode = new Node<>(pred, e, succ);

		// 將原鏈表指定index位置的節點的prev更新為新節點
        succ.prev = newNode;

		// 如果succ為第一個節點
        if (pred == null)
			// 將新節點賦值給第一個節點
            first = newNode;
        else
			// 將新節點賦值給原鏈表指定index節點的prev(上一個節點)的next
            pred.next = newNode;

		// 元素個數 + 1
        size++;
        modCount++;
}

get方法

get(int index)

public E get(int index) {
		// 檢查索引
        checkElementIndex(index);

		// 利用二分法查詢index位置所在節點  返回其item值
        return node(index).item;
}

set方法

set(int index, E element)

public E set(int index, E element) {
		// 檢查索引
        checkElementIndex(index);
		
		// 獲取index位置所在節點
        Node<E> x = node(index);

		// 獲取節點的item值
        E oldVal = x.item;

		// 將item賦值為新值
        x.item = element;

		// 返回舊的值
        return oldVal;
}

remove方法

remove(int index)

public E remove(int index) {
		// 檢查索引
        checkElementIndex(index);

		// node(index)返回當前index對應的節點
        return unlink(node(index));
}


E unlink(Node<E> x) {
        // 獲取index位置所在節點item值
        final E element = x.item;

		// 獲取index位置所在節點 后繼節點
        final Node<E> next = x.next;

		//  獲取index位置所在節點 前繼節點
        final Node<E> prev = x.prev;

		// 刪除的是否是頭節點
        if (prev == null) {
			// 頭節點賦值為 index位置所在節點的下一個節點(index所在節點要被刪了)
            first = next;
        } else {
			// 不是頭節點 需要將index位置所在節點的上一個節點的next指向 下一個節點
			// index位置所在節點prev置為 null
            prev.next = next;
            x.prev = null;
        }

		// 刪除的是否是最后一個節點
        if (next == null) {
			// 最后一個節點賦值為index位置所在節點的上一個節點
            last = prev;
        } else {
			// 不是刪除最后一個節點
			// index位置所在節點的下一個節點的prev指向上一個節點
            next.prev = prev;
            x.next = null;
        }
			
		// item置為
        x.item = null;
		// 元素個數 - 1
        size--;
        modCount++;
		// 返回被刪除元素
        return element;
}

RandomAccess接口

RandomAccess接口是一個空接口即標記接口

public interface RandomAccess {
}

實現該接口的類說明其支持快速隨機訪問 比如ArrayList實現了該接口 說明ArrayList支持快速隨機訪問 所謂快速隨機訪問指的是通過元素的下標即可快速獲取元素對象 無需遍歷 而LinkedList則沒有這個特性 元素獲取必須遍歷鏈表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM