最近在研究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的慢。