最近秋招面試時經常碰到這道題,雖然自己也答出來了,但總感覺不是那麼完善,特此總結一下。
ArrayList 和 LinkedList 都實現了 List 接口。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
我們可以看到 LinkedList 還實現了 Deque 這個隊列接口,可以作爲雙向隊列的實現。
ArrayList 的底層是動態數組,可以實現動態擴容,每次擴容成原來的 1.5 倍,相對於原來的基本數組,它封裝了數組插入、刪除、擴容這些數組操作的細節。
另外,它支持隨機訪問,但是刪除或者插入元素效率很低,例如在數組中間插入一個元素,必須把這個元素以後的元素全部向後搬移一個位置。
LinkedList 訪問一個元素時一般是通過 for 循環獲取到這個元素所在位置,然後返回這個元素,時間複雜度爲 O(n)。刪除或者插入一個給定指針指向的結點時間複雜度爲 O(1)。
我們下面寫個程序來模擬兩種數據結構的隨機查找過程。
/**
* ArrayList 底層是動態數組,利用數組下標隨機訪問元素很高效。
*/
public class ArrayList {
public static void main(String[] args) {
List<Integer> arrayList = new java.util.ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
for(int i = 0; i< 100000; i++) {
arrayList.add(i);
linkedList.add(i);
}
long arraylisttime = getTime(arrayList);
System.out.println(arraylisttime + " ms");
long linkedListTime = getTime(linkedList);
System.out.println(linkedListTime + " ms");
}
private static long getTime(List<Integer> list) {
long curTime = System.currentTimeMillis();
for(int i = 0; i< list.size(); i++) {
// ArrayList 通過下標訪問元素。
list.get(i);
}
long wasteTime = System.currentTimeMillis();
return wasteTime - curTime;
}
}
/** 結果:
* 8 ms
* 7892 ms
*/
可以看出數組隨機訪問的性能確實非常高,特別是在數據量大的時候。
原理就是數組在內存中佔用連續的存儲空間並且存儲相同的元素。所以根據下標訪問某一個元素時可以直接計算得到元素所在位置,大大加快了訪問速度。