1. 程式人生 > >Java原始碼分析——java.util工具包解析(一)——ArrayList、LinkedList、Vector類解析

Java原始碼分析——java.util工具包解析(一)——ArrayList、LinkedList、Vector類解析

    Java中,List列表類與Set集合類的共同源頭是Collection介面,而Collection的父介面是Iterable介面,在Collection介面下又實現了三個常用的介面以及一個抽象方法,分別為Queue介面、List介面、Set介面以及AbstractCollection抽象類,它們之間的關係如圖:
在這裡插入圖片描述

    而今天所講的AbstractList抽象類及其實現類則是繼承自AbstractCollection抽象類,在AbstractList抽象類中封裝了對ArrayList類與LinkedList類以及Vector類的共同操作,來給三者重寫,下面來分別談談其實現類的區別與注意事項,先上圖:
在這裡插入圖片描述

ArrayList

    ArrayList類是列表的陣列表達形式,繼承了AbstracterList抽象類以及List介面、Clone介面以及序列化介面,其預設的到存貯大小是10:

private static final int DEFAULT_CAPACITY = 10;

    而且每次容量不夠擴容時,是擴充套件到原來的1.5倍的,假如不夠的話直接採用要求的容量大小,並呼叫Arrays的陣列複製的方法來複制:

private void grow(int minCapacity) {
        // overflow-conscious code
int oldCapacity = elementData.length; //新的容量 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; 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); }

    其實現的克隆方法是我們平常意義上的淺克隆,只是在堆區新建了空間用來一個新的ArrayList,但是並沒有新建空間來存放裡面的元素,裡面的元素還是指向的原來的地址,其原始碼如下:

public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    通過以下程式碼驗證:

		String s1=new String("1");
        String s2=new String("1");
        String s3=new String("1");
        ArrayList<String> list=new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        ArrayList<String> list2=(ArrayList<String>)list.clone();
        System.out.print(list2.get(0)==list.get(0));
        //列印為true

LinkedList

    LinkedList類是列表結構的鏈式表達方式,與ArrayList實現了相同的介面,它的內部節點的結構如下:

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

    很簡單的連結串列節點,也定義了first、last以及size來分別記錄頭結點、尾節點以及連結串列的節點大小:

transient int size = 0;
transient Node<E> first;
transient Node<E> last;

    同時可以把LinkedList當做棧來使用,因為其方法裡含有這兩個方法:

	public void push(E e) {
	    addFirst(e);
	 }
    public E pop() {
        return removeFirst();
    }

    也實現了佇列:

	public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }
    public E element() {
        return getFirst();
    }
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    push時從頭結點壓入,而pop時也從頭結點取出,也就是實現了棧的先進後出。與ArrayList一樣,LinkedList的克隆方法也是一個淺克隆。

Vector

    Vector類相當於是ArrayList的執行緒安全實現,它在需要對元素進行修改的方法加了synchronized關鍵字,實現了執行緒安全,比如縮小容量:

public synchronized void trimToSize() {
        modCount++;
        int oldCapacity = elementData.length;
        if (elementCount < oldCapacity) {
            elementData = Arrays.copyOf(elementData, elementCount);
        }
    }

    但是Vector的擴容是與ArrayList不一樣的,Vector內部多了一個capacityIncrement成員變數,這個變數是由呼叫者傳進來的,可以讓程式設計師自己來定義,Vector每次擴容都會增加capacityIncrement大小:

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    而Stack類是Vector的子類,在Vector類的基礎上實現了元素的先進後出,是一個執行緒安全類:

public class Stack<E> extends Vector<E> {
    public E push(E item) {
    	// addElement是個同步方法
        addElement(item);
        return item;
    }
    public synchronized E pop() {
        E  obj;
        int  len = size();
        obj = peek();
        removeElementAt(len - 1);
        return obj;
    }
}