java集合類(四)ArrayList與LinkedList比較

概述

  ArrayList與LinkedList均實現了List接口,所以從用戶使用的角度來看是區別不大的。但是由於其底層實現的不同,對用戶來講無差異的操作(如:get,add,remove)底層所做的事情完全不一樣,從而使得他們有着各自的應用場景。

ArrayList與LinkedList類的聲明

  • 1 ArrayList
public class ArrayList<E> extends AbstractList<E> 
       implements List<E>, RandomAccess, Cloneable, Serializable {
    //具體代碼省略
}
  • 2 LinkedList
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, Serializable{
    //具體代碼省略
}

  從上面兩個類的聲明可以看到,他們均實現了List,Cloneable, Serializable 接口,他們都具有List接口規定的行爲操作。AbstractListAbstractSequentialList兩個抽象類是對List接口的簡化,這裏不做詳細探討,此外我們可以發現ArrayList實現了RandomAccess接口,而LinkedList卻沒有實現此接口。RandomAccess到底有什麼用呢?我們後續會講到。

底層存儲與存取性能

  • 1.我們知道ArrayList是實現了基於動態數組的數據結構,而LinkedList是基於鏈表的數據結構。
  • 2.就是由於底層存儲的不同導致對於隨機訪問getsetArrayList覺得優於LinkedList,因爲LinkedList要移動指針。
  • 3.而對於新增和刪除操作addremoveLinedList比較佔優勢,因爲ArrayList要移動數據。
  • 4.當容量不足時,ArrayList需要進行擴容操作,實際上就是創建出一個更大的數組,然後將舊數組中的值拷貝到新數組中。
      以上幾點導致他們兩者之間各有所長,所以我們在使用時要根據具體場景作出正確的選擇,我們簡單總結如下:
  • 1) 如果在使用的過程中需要頻繁的做插入、刪除元素的操作應該使用LinkedList
  • 2) 如果在使用的過程中需要快速隨機訪問元素,而插入刪除操作較少,我們應該使用ArrayList

      上面我們提到了RandomAccess接口,這個接口有什麼用呢?我們來看下它的聲明:
      

package java.util;

/**
 * Marker interface used by <tt>List</tt> implementations to indicate that
 * they support fast (generally constant time) random access.  The primary
 * purpose of this interface is to allow generic algorithms to alter their
 * behavior to provide good performance when applied to either random or
 * sequential access lists.
 *
 * <p>The best algorithms for manipulating random access lists (such as
 * <tt>ArrayList</tt>) can produce quadratic behavior when applied to
 * sequential access lists (such as <tt>LinkedList</tt>).  Generic list
 * algorithms are encouraged to check whether the given list is an
 * <tt>instanceof</tt> this interface before applying an algorithm that would
 * provide poor performance if it were applied to a sequential access list,
 * and to alter their behavior if necessary to guarantee acceptable
 * performance.
 *
 * <p>It is recognized that the distinction between random and sequential
 * access is often fuzzy.  For example, some <tt>List</tt> implementations
 * provide asymptotically linear access times if they get huge, but constant
 * access times in practice.  Such a <tt>List</tt> implementation
 * should generally implement this interface.  As a rule of thumb, a
 * <tt>List</tt> implementation should implement this interface if,
 * for typical instances of the class, this loop:
 * <pre>
 *     for (int i=0, n=list.size(); i &lt; n; i++)
 *         list.get(i);
 * </pre>
 * runs faster than this loop:
 * <pre>
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * </pre>
 *
 * @since 1.4
 */
public interface RandomAccess {
}

我們可以看到它是一個空接口,那麼它的作用是什麼呢?其實它僅僅起到了標識的作用。我們可以看上面的說明第一句話:

 Marker interface used by <tt>List</tt> implementations to indicate that they support fast (generally constant time) random access.The primary purpose of this interface is to allow generic algorithms to alter their behavior to provide good performance when applied to either random or sequential access lists.

什麼意思呢?其實就是:

用於標識List的實現類,表明其可以進行快速隨機訪問。主要目的是能夠在使用的時候爲了提供好的性能(隨機訪問或順序訪問)而更改他們的行爲。

說起來有點拗口,其實就是我們在使用的過程中,可以用instanceof來判斷是否實現了RandomAccess 接口,從而選擇不同的方式進行隨機訪問或者順序訪問。舉個例子:
  
  假如有一個100000000長度的List(我們不知道它的具體類型ArrayList or LinkedList or其他),我們要遍歷裏面的值,並且效率達到最佳,我們該如何進行遍歷?看實現代碼:

        if (list instanceof RandomAccess) {
            for (int m = 0; m < list.size(); m++) {
                System.out.println(list.get(m));
            }
        } else {
            Iterator iter = list.iterator();
            while (iter.hasNext()) {
                System.out.println(iter.next());
            }
        }

通過上面的代碼我們可以使得遍歷操作的性能最好。因爲假如一個List實現了RandomAccess,那麼它必定支持快速隨機訪問(例如ArrayList),通過get()、set()就可以達到非常好的性能,反之,我們通過Iterator 來遍歷是最佳選擇。相同的應用在java.util.Collections中就有體現,隨便舉其中一個例子:

    public static <T> void fill(List<? super T> list, T obj) {
        int size = list.size();
        if (size < FILL_THRESHOLD || list instanceof RandomAccess) {
            for (int i=0; i<size; i++)
                list.set(i, obj);
        } else {
            ListIterator<? super T> itr = list.listIterator();
            for (int i=0; i<size; i++) {
                itr.next();
                itr.set(obj);
            }
        }
    }

這個方法的作用是將list中的每個值全部填充爲obj,也就相當於遍歷操作。這裏的FILL_THRESHOLD 等於25,意思就是當list的大小小於25或者list實現了RandomAccess接口,就使用:for (int i=0; i<size; i++) 這種方式,否則使用:Iterator

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章