不要輕易使用LinkList------ArrayList與LinkList速度測試

在Java中,ArrayList的數據結構爲數組,LinkList數據結構爲鏈表。如果我們學過算法與數據結構,我們可以很快得到以下結論:

對於數組,隨機元素訪問的時間複雜度是 O(1),元素插入操作是 O(n);

對於鏈表,隨機元素訪問的時間複雜度是 O(n),元素插入操作是 O(1)。

因此,我們會認爲在大量的元素插入、很少的隨機訪問的業務場景下,是不是就應該使用 LinkedList 呢。其實不對,通過下面的例子我們來演示一下在不同數據量下,ArrayList和LinkList的隨機訪問和插入的性能。


//LinkedList訪問
private static void linkedListGet(int elementCount, int loopCount) {
    List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
    IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount)));
}

//ArrayList訪問
private static void arrayListGet(int elementCount, int loopCount) {
    List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
    IntStream.rangeClosed(1, loopCount).forEach(i -> list.get(ThreadLocalRandom.current().nextInt(elementCount)));
}

//LinkedList插入
private static void linkedListAdd(int elementCount, int loopCount) {
    List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
    IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount),1));
}

//ArrayList插入
private static void arrayListAdd(int elementCount, int loopCount) {
    List<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
    IntStream.rangeClosed(1, loopCount).forEach(i -> list.add(ThreadLocalRandom.current().nextInt(elementCount),1));
}

測試代碼如下,10萬個元素,循環10萬次


int elementCount = 100000;
int loopCount = 100000;
StopWatch stopWatch = new StopWatch();
stopWatch.start("linkedListGet");
linkedListGet(elementCount, loopCount);
stopWatch.stop();
stopWatch.start("arrayListGet");
arrayListGet(elementCount, loopCount);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());


StopWatch stopWatch2 = new StopWatch();
stopWatch2.start("linkedListAdd");
linkedListAdd(elementCount, loopCount);
stopWatch2.stop();
stopWatch2.start("arrayListAdd");
arrayListAdd(elementCount, loopCount);
stopWatch2.stop();
System.out.println(stopWatch2.prettyPrint());

對於結果,你可能會很奇怪。在隨機訪問方面,我們看到了ArrayList的絕對優勢,而在隨機插入方面,ArrayList也佔據了巨大的優勢:

10W:
StopWatch '': running time (millis) = 4520
-----------------------------------------
ms     %     Task name
-----------------------------------------
04509  100%  linkedListGet
00011  000%  arrayListGet

StopWatch '': running time (millis) = 28404
-----------------------------------------
ms     %     Task name
-----------------------------------------
26570  094%  linkedListAdd
01834  006%  arrayListAdd

我還測試了20W,50W,100W的數據量和操作量下的結果


20W:
StopWatch '': running time (millis) = 23972
-----------------------------------------
ms     %     Task name
-----------------------------------------
23955  100%  linkedListGet
00017  000%  arrayListGet

StopWatch '': running time (millis) = 157245
-----------------------------------------
ms     %     Task name
-----------------------------------------
149343  095%  linkedListAdd
07902  005%  arrayListAdd


50W :StopWatch '': running time (millis) = 175061
-----------------------------------------
ms     %     Task name
-----------------------------------------
175020  100%  linkedListGet
00041  000%  arrayListGet

StopWatch '': running time (millis) = 2164864
-----------------------------------------
ms     %     Task name
-----------------------------------------
2115619  098%  linkedListAdd
49245  002%  arrayListAdd

100W:

StopWatch '': running time (millis) = 721593
-----------------------------------------
ms     %     Task name
-----------------------------------------
721508  100%  linkedListGet
00085  000%  arrayListGet

StopWatch '': running time (millis) = 8900864
-----------------------------------------
ms     %     Task name
-----------------------------------------
8646038  097%  linkedListAdd
254826  003%  arrayListAdd

爲什麼會出現這種情況呢?我們查看源碼可以發現,雖然你插入的時間複雜度爲O(1),但是在插入之前你要先找到這個節點,這個操作是通過遍歷來實現的。因此,對於插入,LinkList的時間複雜度也是O(n),不能只考慮插入操作的成本。而且,由於CPU緩存和內存連續性等問題,鏈表這種數據結構的實現方式對性能並不友好,即使在它最擅長的場景都不一定可以發揮威力。

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