天天看點

java 集合LinkedList與ArrayList源碼分析

一、LinkedList

1、結構,廢話不多說吧,直接看條件應該就大概懂了。

transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
           

主要有三個元素,容量,第一節點,最後節點。每一個node中 又包含了前節點跟下一個節點。這個node的構造器很重要,第一個參數是prev節點我個人就叫他前置節點,next我就叫他後置節點。雙向連結清單由此展現。

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;
        }
    }
           

2、新增

public boolean add(E e) {
        linkLast(e);
        return true;
    }
           
void linkLast(E e) {
        final Node<E> l = last;
        //上面node的構造器,重複一遍參數,前置節點,元素,後置節點。
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
           

首先擷取最後一個節點,然後在最後節點上新增節點,判斷最後節點是否為空,如果為空設定為第一節點,不為空增加到最後節點後面稱為最後節點。size加1,更改次數加1。是以說預設的就是加到末尾。

2.1在指定下标進行新增

public void add(int index, E element) {
 	//檢查是否越界等等不截圖了很簡單
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
	//如果新增index 與size相等 就直接加到後面
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    //succ為之前下标元素,擷取之前下标元素的前置節點pred。
    //然後通過構造器建立pred為前置節點,succ(之前下标對應元素)為後置節點的新node。
    //把新node設定為succ的前置節點pred的後置節點。
        void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
           

3、删除,先判斷删除下标是否越界很簡單的就不截圖了,擷取要删除的元素節點,

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
           
//擷取要的元素 這個地方get也會用到
    Node<E> node(int index) {
        // assert isElementIndex(index);
		//與size一半進行比較 
		//雖然取了一半但是還有大大的for循環
        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;
        }
    }
           

移除元素,擷取此元素的pre節點跟next節點。判斷pre節點是否為空,為空next節點設定為第一節點,否則此節點的next節點設定為pre節點的next節點。判斷next節點是否為空,為空pre節點設定為最後節點,否則pre節點設定為next節點的pre節點。

E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

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

        x.item = null;
        size--;
        modCount++;
        return element;
    }
           

4、擷取制定下标元素

public E get(int index) {
  	//同樣的先校驗
        checkElementIndex(index);
        //上面删除指點元素有哈
        return node(index).item;
    }
           

二、ArrayList

1、結構 是一個Object數組

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
        //Object 數組麼這不是
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        //喲這個也是長度為空的數組
        //private static final Object[] EMPTY_ELEMENTDATA = {}
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
           

2、新增

public boolean add(E e) {
    	//數組長度跟新增之後長度進行比較
    	//size+1 就是後面minCapacity 可以先有個印象
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //如果初始長度滿足新增
        elementData[size++] = e;
        return true;
    }
           

首先咱們先看下,這個方法在指定下标新增也會用到喲。

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //我直接把calculateCapacity方法粘過來
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
	//進行比較看看是不是預設的預設的就是10
	// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         //private static final int DEFAULT_CAPACITY = 10;
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

	
    private void ensureExplicitCapacity(int minCapacity) {
    //更改次數自增,不重要忽略~
        modCount++;

        // overflow-conscious code
        //數組長度跟新增之後長度進行比較
        if (minCapacity - elementData.length > 0)
        //
            grow(minCapacity);
    }
           
//擴容集合長度
	//minCapacity 為更新之後長度
    private void grow(int minCapacity) {
        // overflow-conscious code
        //擷取原來集合的長度
        int oldCapacity = elementData.length;
        //新長度為原來長度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
 //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        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);
    }
        private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
           

應該是很簡單吧看着,最後回到數組下标指派。

public boolean add(E e) {
    	//數組長度跟新增之後長度進行比較
    	//size+1 就是後面minCapacity 可以先有個印象
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //如果初始長度滿足新增
        elementData[size++] = e;
        return true;
    }
           

2.1 在指定下标新增

public void add(int index, E element) {
        rangeCheckForAdd(index);
		//似曾相識 就是上面的那個
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //好了這個 System 已經說明一切了
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
    
      /*
        從指定的源數組複制數組,從指定位置,到目标數組的指定位置。
        從源中複制陣列元件的子序列由<code>src</code>引用到目标數組的數組由<code>dest</code>引用。
        複制的元件數為等于<code>長度</code>參數。
        元件位于位置<code>srcPos</code>到将源數組中的<code>srcPos+length-1</code>
        複制到位置<code>destPos</code>到目的地的<code>destPos+length-1數組。
        */
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

           

3、指定下标删除

public E remove(int index) {
    //同樣的先校驗
        rangeCheck(index);

        modCount++;
        //直接把下面的方法粘貼過來
        /*   
         E elementData(int index) {
        return (E) elementData[index]; }
        */
        E oldValue = elementData(index);

        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

        return oldValue;
    }

           

4、擷取指點下标元素

public E get(int index) {
        rangeCheck(index);
 		/*   也是直接粘貼過來
         E elementData(int index) {
        return (E) elementData[index]; }
        */
        return elementData(index);
    }
           

list 小總結:

1、LinkedList

  1. 沒有容量的限制,采用的雙向連結清單的方式。
  2. 新增隻需要建構新node,更新原來下标元素前置節點的後置節點,跟原來下标元素的前置節點。其實新增節點,更改引用。是以說會快一點。
  3. 删除隻需要更改原來下标元素前置節點的next,跟原來下标元素後置節點的pre。其實就是更改引用。是以說會快一點。
  4. 擷取元素需要先取size的一半然後進行周遊。

2、ArrayList

  1. 會有容量擴容的機制,采用的Object數組的方式。
  2. 新增要判斷數組是否需要擴容,然後調用System的方法進行對資料遷移。
  3. 删除 調用System的方法進行對資料遷移。
  4. 直接通過數組下标進行擷取。