LinkedList插入元素一定比ArrayList快吗
在選擇數據結構的時候,我們通常會考慮每種數據結構不同操作的時間復雜度,以及使用場景兩個因素。
對于數組,隨機元素訪問的時間復雜度是 O(1),元素插入操作是 O(n);
對于鏈表,隨機元素訪問的時間復雜度是 O(n),元素插入操作是 O(1)。
那么,在大量的元素插入、很少的隨機訪問的業務場景下,是不是就應該使用 LinkedList 呢?接下來,我們寫一段代碼測試下兩者隨機訪問和插入的性能吧。
我們定義下面幾個方法,分別為隨機訪問LinkedList數組和ArrayList數組,往LinkedList和ArrayList中隨機插入元素。
? ?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)));} ?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)));} ?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));} ?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萬元素的數組,然后測試訪問時間和插入時間,結果如下:
StopWatch '': running time = 4827155600 ns --------------------------------------------- ns ? ? ? ? % ? ? Task name --------------------------------------------- 4808820500 100% linkedListGet 018335100 000% arrayListGet ? StopWatch '': running time = 28181287000 ns --------------------------------------------- ns ? ? ? ? % ? ? Task name --------------------------------------------- 27238759200 097% linkedListAdd 942527800 003% arrayListAdd可以很明顯的看到ArrayList無論是隨機訪問,還是隨機插入,所需的時間都遙遙領先于LinkedList。
翻看 LinkedList 源碼發現,插入操作的時間復雜度是 O(1) 的前提是,你已經有了那個要插入節點的指針。但,在實現的時候,我們需要先通過循環獲取到那個節點的 Node,然后再執行插入操作。前者也是有開銷的,不可能只考慮插入操作本身的代價:
? public void add(int index, E element) {checkPositionIndex(index); ?if (index == size)linkLast(element);elselinkBefore(element, node(index)); } ? Node<E> node(int index) {// assert isElementIndex(index); ?if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;} }所以,對于插入操作,LinkedList 的時間復雜度其實也是 O(n)。繼續做更多實驗的話你會發現,在各種常用場景下,LinkedList 幾乎都不能在性能上勝出 ArrayList。
?
我們繼續測試一下在尾部插入元素:
? ?private static void linkedListAddLast(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)));} ?private static void arrayListAddLast(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)));}測試參數還是同上,測試結果如下:
StopWatch '': running time = 95097500 ns --------------------------------------------- ns ? ? ? ? % ? ? Task name --------------------------------------------- 078869800 083% linkedListAddLast 016227700 017% arrayListAddLast我們繼續測試一下在頭部插入元素:
? ?private static void linkedListAddFirst(int elementCount, int loopCount) {LinkedList<Integer> list = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));IntStream.rangeClosed(1, loopCount).forEach(i -> list.addFirst(ThreadLocalRandom.current().nextInt(elementCount)));} ?private static void arrayListAddFirst(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(0, ThreadLocalRandom.current().nextInt(elementCount)));}這次的測試結果如下:
StopWatch '': running time = 1832579700 ns --------------------------------------------- ns ? ? ? ? % ? ? Task name --------------------------------------------- 087754700 005% linkedListAddFirst 1744825000 095% arrayListAddFirst總結:
ArrayList在大部分時候的性能都明顯要強于LinkedList。即使是對于增刪少查詢多這一場景。LinkedList只有在頭部插入元素的時候,性能會明顯優于ArrayList。
LinkedList并沒有想象中的那么好用,使用需謹慎。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的LinkedList插入元素一定比ArrayList快吗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring 声明式事务在业务开发中容易
- 下一篇: Spring如何实现统一的基于请求头he