java之list源碼淺析,list源碼淺析

來源:互聯網
上載者:User

java之list源碼淺析,list源碼淺析

三大資料結構鏈表、樹和圖,順序表作為其中的一種,可以說是平時編程中最長使用到的。List介面是順序表在java中的實現,它有很多子介面和實作類別,平時的編程中使用起來非常方便。但是更進一步,我們有必要對其實現和原理進行理解,並和資料結構中所學比較,並應用於平時的編程中,編寫出高效率的代碼。

首先看下list介面的層次關係,由本人根據jdk的類結構簡單畫的:


從可以看出,list介面有Collection介面衍生而出,那麼首先瞭解下Collection介面。

Collection

collection 是集合架構的根,他定義的集合操作的通用行為,如添加元素、大量新增、刪除、大量刪除、集合大小、包含、迭代器等。它的介面定義這裡不再貼,在關係上,Collection是繼承於Iterable介面的,其只有一個方法:

public interface Iterable<T> {     /**     * Returns an {@link Iterator} for the elements in this object.     *     * @return An {@code Iterator} instance.     */    Iterator<T> iterator();}

其中Iterator如下:

public interface Iterator<E> {      public boolean hasNext();        public E next();       public void remove();}

AbstractCollection

AbstractCollection 是Collection介面的抽象實現,實現了其中大部分的功能。如下所示,我們實現自己的Collection只需要實現三個方法即可:

Collection<String> c =           /**        *        *自訂實現樣本,在AbstractCollection 中的一些方法的實現中,如Clear,調用了        *iterator()方法,而在這些方法在子類中,實現這是典型的template模式。        *        */       new AbstractCollection<String>() {            /* *            * 迭代器            */           @Override           public Iterator<String> iterator() {              // TODO Auto-generated method stub              return null;           }            /*            * 大小            */           @Override           public int size() {              // TODO Auto-generated method stub              return 0;           }                     /*            * 抽象實現是拋出異常,我們需要自己實現            */           @Override           public boolean add(String e) {              return true;           }       };

如下程式碼片段摘自AbstractCollection,調用了iterator方法,其中有很多類似代碼,如addAll、removeAll、contains、toArray()等,這些實現只是基本的實現,子類中有更有效率的就會覆蓋它。典型的可見後面的Arraylist的toArray()。

public void clear() {    Iterator<E> e = iterator();    while (e.hasNext()) {        e.next();        e.remove();    }    }

List

List介面表示資料結構中的鏈表,其繼承collection介面,又往裡面添加了一些鏈表操作的方法,主要是隨機訪問、刪除、尋找、專用的迭代器等,如下所示:

/**   隨機擷取*/E get(int index);     /**     * 隨機設定值     */    E set(int index, E element);     /**     * 隨機添加     */    void add(int index, E element);     /**     *隨機移除     */    E remove(int index);      // Search Operations     /**     * 尋找     */    int indexOf(Object o);     /**     * 從後尋找     */    int lastIndexOf(Object o);      // List Iterators     /**     *專用迭代     */    ListIterator<E> listIterator();     /**     * 從某個位置迭代     */    ListIterator<E> listIterator(int index);     // View     /**     * 子列表     */    List<E> subList(int fromIndex, int toIndex);

AbstractList

這是List介面的抽象實現,和AbstractCollection類似,實現了基本的功能而把關鍵的方法延遲到子類中實現。如下所示一個示意子類:

List<String>  l = /**        *        *樣本實現        */       new AbstractList<String>() {            /*            * 隨機擷取            */           @Override           public String get(int index) {              return null;           }            /*            * 大小            */           @Override           public int size() {              return 0;           }                     /*            * 超類中實現拋出異常,表示不可變list            *            * 自己實現後變為可變            */           @Override           public String set(int index, String element) {              return null;           }                               /*            * 預設實現拋出異常            */                     @Override           public void add(int index, String element) {           }                     /**            * 預設實現拋出異常            */           @Override           public String remove(int index) {                           return null;           }                 };

ListIterator

List介面添加了新的方法,其中一個方法就是返回ListIterator,其擴充了iterator,增加了向前遍曆的方法。主要鏈表是有序的。

其實現不多說,需要注意的就是迭代器失效問題,在list實現中,維護了一個欄位modCount,當此list進行改變操作,如add/remove等的時候,此值會進行改變,當構造迭代器的時候,此值會在迭代器中保留一個副本,使用迭代器中的方法都會檢查副本和list中的modCount是否一致,如果不一致,拋出迭代器異常。需要注意的是,java中的for each文法,經過編譯後,是使用的迭代器。

如下部分源碼樣本:

private class Itr implements Iterator<E> {    /** 此類是個內部類,直接存取abstractList的欄位     * Index of element to be returned by subsequent call to next.     */    int cursor = 0;     /**     * Index of element returned by most recent call to next or     * previous.  Reset to -1 if this element is deleted by a call     * to remove.     */    int lastRet = -1;     /**     * 注意這點     */    int expectedModCount = modCount;    /**     *迭代器的next方法     */     public E next() { /**     *操作前進行檢查     */            checkForComodification();        try {       E next = get(cursor);       lastRet = cursor++;       return next;        } catch (IndexOutOfBoundsException e) {       checkForComodification();       throw new NoSuchElementException();        }    }/**     *檢查迭代器失效問題。     */    final void checkForComodification() {        if (modCount != expectedModCount)       throw new ConcurrentModificationException();    }    }

ArrayList

這是一般編程中最長使用的資料結構,基於數組實現的順序表,但是數組可自動擴容。它直接繼承於abstractList,故只研究其關鍵實現和方法。

數組儲存

ArrayList是基於數組的儲存,預設構造初始大小是10,後面我們會看到這個初始大小會一定程度上影響其效能:

/**     * The array buffer into which the elements of the ArrayList are stored.     * The capacity of the ArrayList is the length of this array buffer.     */private transient Object[] elementData;public ArrayList(int initialCapacity) {    super();        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal Capacity: "+                                               initialCapacity);    this.elementData = new Object[initialCapacity];    }     /**     * Constructs an empty list with an initial capacity of ten.     */    public ArrayList() {    this(10);    }

Add方法

  List介面有兩個add的重載方法,第一個是在list的末尾添加元素,第二個是隨機位置添加元素,其自動擴充資料容量的方法是ensureCapacity(int),保證大小:

/**     * 此方法在父類中已經實現,但是arralylist覆蓋了其實現,採用更加有效率的實現     */    public boolean add(E e) {       ensureCapacity(size + 1);  // Increments modCount!!       elementData[size++] = e;       return true;        }         /**         * 任意位置添加元素,相應的元素後移,保持鏈表的特性。         * 其中System.arrayCopy效率較高。         *         */        public void add(int index, E element) {       if (index > size || index < 0)           throw new IndexOutOfBoundsException(           "Index: "+index+", Size: "+size);        ensureCapacity(size+1);  // Increments modCount!!       System.arraycopy(elementData, index, elementData, index + 1,               size - index);       elementData[index] = element;       size++;        }                      /**         * 保證list鏈表的大小,如果不足則擴容。         * 這個方法比較消耗效能,因此,如果實現可以知道或者預估AyyayList的大小,那麼         * 可以在構造的時候設定合適的初始容量。         */        public void ensureCapacity(int minCapacity) {       modCount++;       //擷取舊的數組大小       int oldCapacity = elementData.length;       //比較,如果不足則擴容       if (minCapacity > oldCapacity) {           Object oldData[] = elementData;           int newCapacity = (oldCapacity * 3)/2 + 1;            if (newCapacity < minCapacity)           newCapacity = minCapacity;                // 調用效率較高的arraycopy                elementData = Arrays.copyOf(elementData, newCapacity);       }        }

Vector

實現和ArraaylIst基本一致,只是在方法上加了同步操作,但需要注意其安全執行緒是相對的,比如一個線程進行add操作,另外一個線程迭代,肯定會出異常。

另外有個Stack繼承Vector,添加了棧的相關方法,如push,和pop。

LinkedList

基於鏈表的順序表,和ArrayList相比,其隨機插入和刪除的效率較高,因為其不需要擴容和移動元素操作,但是隨機訪問效率較低(隨機訪問需要從前端節點遍曆)。

AbstractSequentialList

LinkedList其繼承於AbstractSequentialList,而其中AbstractSequentialList實現了abstractlist中的抽象方法,都是基於迭代器實現的,它同時把返回迭代器的方法覆蓋為抽象方法。故LinkedList實現的關鍵在於迭代器,如下所示:

public abstract class AbstractSequentialList<E> extends AbstractList<E> {        /**          *基於迭代器實現的get     */    public E get(int index) {        try {            return listIterator(index).next();        } catch (NoSuchElementException exc) {            throw new IndexOutOfBoundsException("Index: "+index);        }    }     /**     *基於迭代器實現的set     */    public E set(int index, E element) {    try {        ListIterator<E> e = listIterator(index);        E oldVal = e.next();        e.set(element);        return oldVal;    } catch (NoSuchElementException exc) {        throw new IndexOutOfBoundsException("Index: "+index);    }    } /*基於迭代器實現的add      */    public void add(int index, E element) {    try {        listIterator(index).add(element);    } catch (NoSuchElementException exc) {        throw new IndexOutOfBoundsException("Index: "+index);    }    }     /**     * 基於迭代器實現的remove     *     */    public E remove(int index) {    try {        ListIterator<E> e = listIterator(index);        E outCast = e.next();        e.remove();        return outCast;    } catch (NoSuchElementException exc) {        throw new IndexOutOfBoundsException("Index: "+index);    }}     public Iterator<E> iterator() {        return listIterator();    }     /**     * Returns a list iterator over the elements in this list (in proper     * sequence).     *     */    public abstract ListIterator<E> listIterator(int index);}

LinkedList

Linkedlist除了實現了List介面,還實現了隊列的相關介面,這裡略過不提。由listiterator介面,可知linkedList也是一個可雙向遍曆的順序表。

只需研究每個鏈表節點的結構和迭代器實現。

鏈表節點如下所示,是一個靜態內部類:

private static class Entry<E> {    E element;    Entry<E> next;    Entry<E> previous;     Entry(E element, Entry<E> next, Entry<E> previous) {        this.element = element;        this.next = next;        this.previous = previous;    }    }

其迭代器實現是一個內部類,接下來以其add操作說明,其他類似:

private class ListItr implements ListIterator<E> {           private Entry<E> lastReturned = header;           private Entry<E> next;           private int nextIndex;           private int expectedModCount = modCount;            /**            * 構造的時候採用一個最佳化技巧,根據index決定從前還是從後遍曆。            */           ListItr(int index) {               if (index < 0 || index > size)              throw new IndexOutOfBoundsException("Index: "+index+                                ", Size: "+size);               if (index < (size >> 1)) {              next = header.next;               for (nextIndex=0; nextIndex<index; nextIndex++)                  next = next.next;               } else {              next = header;              for (nextIndex=size; nextIndex>index; nextIndex--)                  next = next.previous;               }           }             /*            * 鏈表插入,在構造此迭代器的時候,index就是插入的位置,故直接插入元素即可            *            * addbefor是ArrayList的方法,就是鏈表插入,指標改變的過程,這裡不贅述。            */           public void add(E e) {               checkForComodification();               lastReturned = header;               addBefore(e, next);               nextIndex++;               expectedModCount++;           }            final void checkForComodification() {               if (modCount != expectedModCount)              throw new ConcurrentModificationException();           }           }                                     /**         *AbstractSequentialList的方法,隨機插入。首先構造從index開始的迭代器         */        public void add(int index, E element) {           try {               listIterator(index).add(element);           } catch (NoSuchElementException exc) {               throw new IndexOutOfBoundsException("Index: "+index);           }           }

CopyOnWriteArrayList

CopyOnWriteArrayList是java並發包中的一個工具類,在jdk1.5的時候被引入,單獨一個它的內容足以寫一篇文章,故這裡不再展開,只是簡要對其說明。

其基本思想從名稱中可以看出,當進行寫操作的時候,先複製複製出一個副本,寫操作對副本進行。

讀和遍曆操作發生在操作發生的那一刻存在的副本上。

寫操作的時候枷鎖,讀操作的時候不加鎖。並通過java記憶體模型的語義,進行優雅的設計。

CopyOnWrite並發容器用於讀多寫少的並發情境。CopyOnWrite容器只能保證資料的最終一致性,不能保證資料的即時一致性。所以如果你希望寫入的的資料,馬上能讀到,請不要使用CopyOnWrite容器。

具體可以參考一下文章:

聊聊並發-Java中的Copy-On-Write容器 

Java記憶體模型happens-before俗解  

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.