關於ArrayList的兩個問題

最近在研究ArrayList,開始就發現了兩個問題:數組

  1. ArrayList默認的初始容量大小?ui

  2. ArrayList的插入速度比LinkedList的慢?this


背景:spa

  JDK 1.8code


 

1. ArrayList默認的初始容量大小?blog

看源碼索引

/**
  * Constructs an empty list with an initial capacity of ten.
  */
public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
  * Shared empty array instance used for default sized empty instances. We
  * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
  * first element is added.
  */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}

第一張圖片註釋的意思是:圖片

  Constructs an empty list with an initial capacity of ten.ci

  構造一個初始容量爲10的空列表。element

可是這裏,默認構造方法只幹了一件事,就是將elementData初始化爲一個空數組。

那麼,爲何說它的默認初始容量是10呢?

ArrayList有三個構造方法:

1. public ArrayList(int initialCapacity) {...}

2. public ArrayList() {...}

3. public ArrayList(Collection<? extends E> c) {...}

第三個暫且不說。第一個是指定ArrayList的初始化容量,第二個則是默認的構造方法。

若是不指定ArrayList的初始化容量,而使用默認構造方法去實例化ArrayList,則ArrayList會提供一個默認的容量大小,即爲10。

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

可是,上面也說了 -- 默認構造方法只幹了一件事,就是將elementData初始化爲一個空數組。

那麼,何時纔會用到這個 DEFAULT_CAPACITY  常量呢?

答案是 -- 第一次添加元素時

執行add()方法最終會調用到這個grow()方法,也就是數組擴容的方法。

也就是說,第一次添加元素時,newCapacity的大小其實就是DEFAULT_CAPACITY (10)

 1 private void grow(int minCapacity) {
 2   // overflow-conscious code
 3     int oldCapacity = elementData.length;
 4     int newCapacity = oldCapacity + (oldCapacity >> 1);
 5     if (newCapacity - minCapacity < 0)
 6         newCapacity = minCapacity;
 7     if (newCapacity - MAX_ARRAY_SIZE > 0)
 8         newCapacity = hugeCapacity(minCapacity);
 9     // minCapacity is usually close to size, so this is a win:
10     elementData = Arrays.copyOf(elementData, newCapacity); 11 }

這個和JDK 1.6 的版本是不同的。1.6 的版本會直接初始化一個數組容量爲10的數組 。

仍是看看源碼吧。

JDK 1.6 的

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this(10);
}
/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param   initialCapacity   the initial capacity of the list
 * @exception IllegalArgumentException if the specified initial capacity
 *            is negative
 */
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
}

 

JDK 1.7 的

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}
/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

小結:

  1. ArrayList的默認初始容量的確是10;

  2. 使用默認構造方法實例化ArrayList,只初始化了一個空數組;

  3. 第一次調用add()方法時,數組纔會被擴容,數組大小爲默認的初始容量--10(2是前提)

補充:

  個人考證僅限於1.6 到 1.8,其餘版本可能還會有所不一樣


 

2. ArrayList的插入速度比LinkedList的慢?

add方法有兩種:

(1)一種是直接在數組最後面插入

1 public boolean add(E e) {
2   ensureCapacityInternal(size + 1);  // Increments modCount!!
3     elementData[size++] = e;
4     return true;
5 }

 

(2)另外一種是指定位置插入

1 public void add(int index, E element) {
2     rangeCheckForAdd(index);
3 
4     ensureCapacityInternal(size + 1);  // Increments modCount!!
5     System.arraycopy(elementData, index, elementData, index + 1, size - index);
6     elementData[index] = element;
7     size++;
8 }

 

說ArrayList的插入速度比LinkedList的慢是由於:

  ArrayList按序索引元素,因此插入新元素時涉及到其餘已存在元素在數組中的先後移動,致使速度慢。

 

此刻,給一個條件,只在數組末尾插入元素,即僅使用add(E e)方法,不使用add(int index, E element)方法,那麼結果會如何?

分析一下:

  只在末尾插入元素 == 不會致使中間已存在元素的變更。

也就是說:

  這個時候,使用ArrayList的插入速度應該更快。

 

固然,也不能胡說,來一個小實驗驗證一下: 

 1   public static void main(String[] args) {
 2         long before = System.currentTimeMillis();
 3         List list  = new ArrayList();
 4         //List list  = new LinkedList();
 5         for (int i = 0; i < 1000000; i++) {
 6             list.add(i);
 7         }
 8         long after = System.currentTimeMillis();
 9         System.out.println("插入一百萬個元素,ArrayList須要時間:" + (after - before));
10         //System.out.println("插入一百萬個元素,LinkedList須要時間:" + (after - before));
11     }

 

各重複5次,結果:

ArrayList:
  插入一百萬個元素,ArrayList須要時間:28
  插入一百萬個元素,ArrayList須要時間:32
  插入一百萬個元素,ArrayList須要時間:63
  插入一百萬個元素,ArrayList須要時間:31
  插入一百萬個元素,ArrayList須要時間:32

LinkedList
  插入一百萬個元素,LinkedList須要時間:132
  插入一百萬個元素,LinkedList須要時間:128
   插入一百萬個元素,LinkedList須要時間:133
   插入一百萬個元素,LinkedList須要時間:149
   插入一百萬個元素,LinkedList須要時間:149   

 

因此:

  應該改成 -- 隨機插入元素時,ArrayList的速度比LinkedList的慢。

 

而且:

  刪除元素的道理也是同樣的,因此應該是 -- 隨機增刪元素時,ArrayList的速度比LinkedList的慢。

相關文章
相關標籤/搜索