Java提高篇(三二)-----List總結


前面LZ已經充分介紹了有關於List接口的大部分知識,如ArrayList、LinkedList、Vector、Stack,通過這幾個知識點可以對List接口有了比較深的了解了。只有通過歸納總結的知識才是你的知識。所以下面LZ就List接口做一個總結。推薦閱讀:

java提高篇(二一)-----ArrayList

java提高篇(二二)-----LinkedList

java提高篇(二九)-----Vector

Java提高篇(三一)-----Stack

一、List接口概述

List接口,成為有序的Collection也就是序列。該接口可以對列表中的每一個元素的插入位置進行精確的控制,同時用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。 下圖是List接口的框架圖:

20140712000001_thumb19

通過上面的框架圖,可以對List的結構了然於心,其各個類、接口如下:

Collection:Collection 層次結構 中的根接口。它表示一組對象,這些對象也稱為 collection 的元素。對於Collection而言,它不提供任何直接的實現,所有的實現全部由它的子類負責。

AbstractCollection:提供 Collection 接口的骨干實現,以最大限度地減少了實現此接口所需的工作。對於我們而言要實現一個不可修改的 collection,只需擴展此類,並提供 iterator 和 size 方法的實現。但要實現可修改的 collection,就必須另外重寫此類的 add 方法(否則,會拋出 UnsupportedOperationException),iterator 方法返回的迭代器還必須另外實現其 remove 方法。

Iterator:迭代器。

ListIterator:系列表迭代器,允許程序員按任一方向遍歷列表、迭代期間修改列表,並獲得迭代器在列表中的當前位置。

List:繼承於Collection的接口。它代表着有序的隊列。

AbstractList:List 接口的骨干實現,以最大限度地減少實現“隨機訪問”數據存儲(如數組)支持的該接口所需的工作。

Queue:隊列。提供隊列基本的插入、獲取、檢查操作。

Deque:一個線性 collection,支持在兩端插入和移除元素。大多數 Deque 實現對於它們能夠包含的元素數沒有固定限制,但此接口既支持有容量限制的雙端隊列,也支持沒有固定大小限制的雙端隊列。

AbstractSequentialList:提供了 List 接口的骨干實現,從而最大限度地減少了實現受“連續訪問”數據存儲(如鏈接列表)支持的此接口所需的工作。從某種意義上說,此類與在列表的列表迭代器上實現“隨機訪問”方法。

LinkedList:List 接口的鏈接列表實現。它實現所有可選的列表操作。

ArrayList:List 接口的大小可變數組的實現。它實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 接口外,此類還提供一些方法來操作內部用來存儲列表的數組的大小。

Vector:實現可增長的對象數組。與數組一樣,它包含可以使用整數索引進行訪問的組件。

Stack:后進先出(LIFO)的對象堆棧。它通過五個操作對類 Vector 進行了擴展 ,允許將向量視為堆棧。

Enumeration:枚舉,實現了該接口的對象,它生成一系列元素,一次生成一個。連續調用 nextElement 方法將返回一系列的連續元素。

  20140712000002_thumb

二、使用場景

學習知識的根本目的就是使用它。每個知識點都有它的使用范圍。集合也是如此,在Java中集合的家族非常龐大,每個成員都有最適合的使用場景。在剛剛接觸List時,LZ就說過如果涉及到“棧”、“隊列”、“鏈表”等操作,請優先考慮用List。至於是那個List則分如下:

1、對於需要快速插入、刪除元素,則需使用LinkedList。

2、對於需要快速訪問元素,則需使用ArrayList。

3、對於“單線程環境”或者“多線程環境,但是List僅被一個線程操作”,需要考慮使用非同步的類,如果是“多線程環境,切List可能同時被多個線程操作”,考慮使用同步的類(如Vector)。

2.1ArrayList、LinkedList性能分析

在List中我們使用最普遍的就是LinkedList和ArrayList,同時我們也了解了他們兩者之間的使用場景和區別。

public class ListTest {
    private static final int COUNT = 100000;
</span><span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> ArrayList arrayList = <span style="color: rgb(0,0,255)">new</span> ArrayList&lt;&gt;<span style="color: rgb(0,0,0)">();
</span><span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> LinkedList linkedList = <span style="color: rgb(0,0,255)">new</span> LinkedList&lt;&gt;<span style="color: rgb(0,0,0)">();
</span><span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> Vector vector = <span style="color: rgb(0,0,255)">new</span> Vector&lt;&gt;<span style="color: rgb(0,0,0)">();

</span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)"> insertToList(List list){
    </span><span style="color: rgb(0,0,255)">long</span> startTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();

    </span><span style="color: rgb(0,0,255)">for</span>(<span style="color: rgb(0,0,255)">int</span> i = 0 ; i &lt; COUNT ; i++<span style="color: rgb(0,0,0)">){
        list.add(</span>0<span style="color: rgb(0,0,0)">,i);
    }
    
    </span><span style="color: rgb(0,0,255)">long</span> endTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();
    System.out.println(</span>&quot;插入 &quot; + COUNT + &quot;元素&quot; + getName(list) + &quot;花費 &quot; + (endTime - startTime) + &quot; 毫秒&quot;<span style="color: rgb(0,0,0)">);
}

</span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)"> deleteFromList(List list){
    </span><span style="color: rgb(0,0,255)">long</span> startTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();
    
    </span><span style="color: rgb(0,0,255)">for</span>(<span style="color: rgb(0,0,255)">int</span> i = 0 ; i &lt; COUNT ; i++<span style="color: rgb(0,0,0)">){
        list.remove(</span>0<span style="color: rgb(0,0,0)">);
    }
    
    </span><span style="color: rgb(0,0,255)">long</span> endTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();
    System.out.println(</span>&quot;刪除&quot; + COUNT + &quot;元素&quot; + getName(list) + &quot;花費 &quot; + (endTime - startTime) + &quot; 毫秒&quot;<span style="color: rgb(0,0,0)">);
}

</span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)"> readList(List list){
    </span><span style="color: rgb(0,0,255)">long</span> startTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();
    
    </span><span style="color: rgb(0,0,255)">for</span>(<span style="color: rgb(0,0,255)">int</span> i = 0 ; i &lt; COUNT ; i++<span style="color: rgb(0,0,0)">){
        list.get(i);
    }
    
    </span><span style="color: rgb(0,0,255)">long</span> endTime =<span style="color: rgb(0,0,0)"> System.currentTimeMillis();
    System.out.println(</span>&quot;讀取&quot; + COUNT + &quot;元素&quot; + getName(list) + &quot;花費 &quot; + (endTime - startTime) + &quot; 毫秒&quot;<span style="color: rgb(0,0,0)">);
}

</span><span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span><span style="color: rgb(0,0,0)"> String getName(List list) {
    String name </span>= &quot;&quot;<span style="color: rgb(0,0,0)">;
    </span><span style="color: rgb(0,0,255)">if</span>(list <span style="color: rgb(0,0,255)">instanceof</span><span style="color: rgb(0,0,0)"> ArrayList){
        name </span>= &quot;ArrayList&quot;<span style="color: rgb(0,0,0)">;
    }
    </span><span style="color: rgb(0,0,255)">else</span> <span style="color: rgb(0,0,255)">if</span>(list <span style="color: rgb(0,0,255)">instanceof</span><span style="color: rgb(0,0,0)"> LinkedList){
        name </span>= &quot;LinkedList&quot;<span style="color: rgb(0,0,0)">;
    }
    </span><span style="color: rgb(0,0,255)">else</span> <span style="color: rgb(0,0,255)">if</span>(list <span style="color: rgb(0,0,255)">instanceof</span><span style="color: rgb(0,0,0)"> Vector){
        name </span>= &quot;Vector&quot;<span style="color: rgb(0,0,0)">;
    }
    </span><span style="color: rgb(0,0,255)">return</span><span style="color: rgb(0,0,0)"> name;
}

</span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span><span style="color: rgb(0,0,0)"> main(String[] args) {
    insertToList(arrayList);
    insertToList(linkedList);
    insertToList(vector);
    
    System.out.println(</span>&quot;--------------------------------------&quot;<span style="color: rgb(0,0,0)">);
    
    readList(arrayList);
    readList(linkedList);
    readList(vector);
    
    System.out.println(</span>&quot;--------------------------------------&quot;<span style="color: rgb(0,0,0)">);
    
    deleteFromList(arrayList);
    deleteFromList(linkedList);
    deleteFromList(vector);
}

}

運行結果:

插入 100000元素ArrayList花費 3900 毫秒
插入 100000元素LinkedList花費 15 毫秒
插入 100000元素Vector花費 3933 毫秒
--------------------------------------
讀取100000元素ArrayList花費 0 毫秒
讀取100000元素LinkedList花費 8877 毫秒
讀取100000元素Vector花費 16 毫秒
--------------------------------------
刪除100000元素ArrayList花費 4618 毫秒
刪除100000元素LinkedList花費 16 毫秒
刪除100000元素Vector花費 4759 毫秒

從上面的運行結果我們可以清晰的看出ArrayList、LinkedList、Vector增加、刪除、遍歷的效率問題。下面我就插入方法add(int index, E element),delete、get方法各位如有興趣可以研究研究。

首先我們先看三者之間的源碼:

ArrayList

public void add(int index, E element) {
        rangeCheckForAdd(index);   //檢查是否index是否合法

        ensureCapacityInternal(size + 1);  //擴容操作
        System.arraycopy(elementData, index, elementData, index + 1, size - index);    //數組拷貝
        elementData[index] = element;   //插入
        size++;
    }

rangeCheckForAdd、ensureCapacityInternal兩個方法沒有什么影響,真正產生影響的是System.arraycopy方法,該方法是個JNI函數,是在JVM中實現的。聲明如下:

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

目前LZ無法看到源碼,具體的實現不是很清楚,不過System.arraycopy源碼分析對其進行了比較清晰的分析。但事實上我們只需要了解該方法會移動index后面的所有元素即可,這就意味着ArrayList的add(int index, E element)方法會引起index位置之后所有元素的改變,這真是牽一處而動全身。

LinkedList 

public void add(int index, E element) {
        checkPositionIndex(index);
    </span><span style="color: rgb(0,0,255)">if</span> (index == size)     <span style="color: rgb(0,128,0)">//</span><span style="color: rgb(0,128,0)">插入位置在末尾</span>

linkLast(element);
else
linkBefore(element, node(index));
}

該方法比較簡單,插入位置在末尾則調用linkLast方法,否則調用linkBefore方法,其實linkLast、linkBefore都是非常簡單的實現,就是在index位置插入元素,至於index具體為知則有node方法來解決,同時node對index位置檢索還有一個加速作用,如下:

Node<E> node(int index) {
        if (index < (size >> 1)) {    //如果index 小於 size/2 則從頭開始查找
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {   //如果index 大於 size/2 則從尾部開始查找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
所以linkedList的插入動作比ArrayList動作快就在於兩個方面。1:linkedList不需要執行元素拷貝動作,沒有牽一發而動全身的大動作。2:查找插入位置有加速動作即:若index < 雙向鏈表長度的1/2,則從前向后查找; 否則,從后向前查找。

Vector

Vector的實現機制和ArrayList一樣,同樣是使用動態數組來實現的,所以他們兩者之間的效率差不多,add的源碼也一樣,如下:

public void add(int index, E element) {
        insertElementAt(element, index);
    }
</span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">synchronized</span> <span style="color: rgb(0,0,255)">void</span> insertElementAt(E obj, <span style="color: rgb(0,0,255)">int</span><span style="color: rgb(0,0,0)"> index) {
    modCount</span>++<span style="color: rgb(0,0,0)">;
    </span><span style="color: rgb(0,0,255)">if</span> (index &gt;<span style="color: rgb(0,0,0)"> elementCount) {
        </span><span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span><span style="color: rgb(0,0,0)"> ArrayIndexOutOfBoundsException(index
                                                 </span>+ &quot; &gt; &quot; +<span style="color: rgb(0,0,0)"> elementCount);
    }
    ensureCapacityHelper(elementCount </span>+ 1<span style="color: rgb(0,0,0)">);
    System.arraycopy(elementData, index, elementData, index </span>+ 1, elementCount -<span style="color: rgb(0,0,0)"> index);
    elementData[index] </span>=<span style="color: rgb(0,0,0)"> obj;
    elementCount</span>++<span style="color: rgb(0,0,0)">;
}</span></pre>

上面是針對ArrayList、LinkedList、Vector三者之間的add(int index,E element)方法的解釋,解釋了LinkedList的插入動作要比ArrayList、Vector的插入動作效率為什么要高出這么多!至於delete、get兩個方法LZ就不多解釋了。

同時LZ在寫上面那個例子時發現了一個非常有趣的現象,就是linkedList在某些時候執行add方法時比ArrayList方法會更慢!至於在什么情況?為什么會慢LZ下篇博客解釋,當然不知道這個情況各位是否也遇到過??

2.2、Vector和ArrayList的區別

20140712000003_thumb

四、更多

java提高篇(二一)-----ArrayList

java提高篇(二二)-----LinkedList

java提高篇(二九)-----Vector

Java提高篇(三一)-----Stack


-----原文出自: http://cmsblogs.com/?p=1201 ,請尊重作者辛勤勞動成果,轉載說明出處.

-----個人站點:http://cmsblogs.com


免責聲明!

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



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