程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

ArrayList和LinkedList究竟谁快

发布于2021-06-12 14:52     阅读(982)     评论(0)     点赞(8)     收藏(3)


目录

结果

循环Add

指定位置Get

指定位置Add


在Java中应该都知道ArrayList和LinkedList,

一直以来的概念呢是

ArrayList在get(index)这个应该比LinkedList快;

LinkedList比ArrayList在add(index,element)快;

两者共同遍历呢,应该是一样快的,毕竟都要循环遍历一遍。

直到我写了一个测试类

  1. package com.lw;
  2. import java.util.ArrayList;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. import org.junit.Test;
  6. public class TestJDKList {
  7. List<Integer> linkedList = new LinkedList<>();
  8. List<Integer> arrayList = new ArrayList<>();
  9. int length = 1000000;
  10. @Test
  11. public void testLinkedInsert(){
  12. for (int i = 0; i < length; i++) {
  13. linkedList.add(i);
  14. }
  15. long currentMi2 = System.currentTimeMillis();
  16. linkedList.add(length/2,3);
  17. long endTime2 = System.currentTimeMillis();
  18. System.out.println("testLinkedInsert:" + (endTime2 - currentMi2)); // 9
  19. }
  20. @Test
  21. public void testArrayInsert(){
  22. for (int i = 0; i < length; i++) {
  23. arrayList.add(i);
  24. }
  25. long currentMi2 = System.currentTimeMillis();
  26. arrayList.add(length/2,3);
  27. long endTime2 = System.currentTimeMillis();
  28. System.out.println("testArrayInsert:" + (endTime2 - currentMi2)); // 1
  29. }
  30. @Test
  31. public void testLinkedGet(){
  32. for (int i = 0; i < length; i++) {
  33. linkedList.add(i);
  34. }
  35. long currentMi2 = System.currentTimeMillis();
  36. linkedList.get(length/2);
  37. long endTime2 = System.currentTimeMillis();
  38. System.out.println("testLinkedGet:" + (endTime2 - currentMi2)); // 5
  39. }
  40. @Test
  41. public void testArrayGet(){
  42. for (int i = 0; i < length; i++) {
  43. arrayList.add(i);
  44. }
  45. long currentMi2 = System.currentTimeMillis();
  46. arrayList.get(length/2);
  47. long endTime2 = System.currentTimeMillis();
  48. System.out.println("testArrayGet:" + (endTime2 - currentMi2)); // 0
  49. }
  50. @Test
  51. public void testLinkedIter(){
  52. for (int i = 0; i < length; i++) {
  53. linkedList.add(i);
  54. }
  55. long currentMi2 = System.currentTimeMillis();
  56. for (Integer i : linkedList) {
  57. };
  58. long endTime2 = System.currentTimeMillis();
  59. System.out.println("testLinkedIter:" + (endTime2 - currentMi2)); // 26
  60. }
  61. @Test
  62. public void testArrayIter(){
  63. for (int i = 0; i < length; i++) {
  64. arrayList.add(i);
  65. }
  66. long currentMi2 = System.currentTimeMillis();
  67. for (Integer i : arrayList) {
  68. };
  69. long endTime2 = System.currentTimeMillis();
  70. System.out.println("testArrayIter:" + (endTime2 - currentMi2)); // 11
  71. }
  72. @Test
  73. public void testLinkedAdd() {
  74. long currentMi2 = System.currentTimeMillis();
  75. for (int i = 0; i < length; i++) {
  76. linkedList.add(i);
  77. }
  78. long endTime2 = System.currentTimeMillis();
  79. System.out.println("testLinkedAdd:" + (endTime2 - currentMi2)); // 53
  80. }
  81. @Test
  82. public void testArrayAdd(){
  83. long currentMi1 = System.currentTimeMillis();
  84. for (int i = 0; i < length; i++) {
  85. arrayList.add(i);
  86. }
  87. long endTime1 = System.currentTimeMillis();
  88. System.out.println("testArrayAdd:" + (endTime1 - currentMi1)); // 35
  89. }
  90. }

结果

运行了两遍结果如下:

testLinkedInsert:7
testArrayInsert:0

testLinkedAdd:218
testArrayAdd:23

testLinkedGet:4
testArrayGet:0

testLinkedIter:14
testArrayIter:11

----------------第二遍分割线---------------------------------

testLinkedInsert:12
testArrayInsert:0

testLinkedIter:13
testArrayIter:12

testLinkedGet:3
testArrayGet:0

testLinkedAdd:119
testArrayAdd:23

 

颠覆三观,ArrayList竟然无论怎样都比LinkedList快??

循环Add

ArrayList的add源码,它是把数据放在一个数组中

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

而LinkedList源码,是把数据放在Node对象中,有个前后指针。

  1. public boolean add(E e) {
  2. linkLast(e);
  3. return true;
  4. }
  5. void linkLast(E e) {
  6. final Node<E> l = last;
  7. final Node<E> newNode = new Node<>(l, e, null);
  8. last = newNode;
  9. if (l == null)
  10. first = newNode;
  11. else
  12. l.next = newNode;
  13. size++;
  14. modCount++;
  15. }
  1. private static class Node<E> {
  2. E item;
  3. Node<E> next;
  4. Node<E> prev;
  5. Node(Node<E> prev, E element, Node<E> next) {
  6. this.item = element;
  7. this.next = next;
  8. this.prev = prev;
  9. }
  10. }

难道是前后指针这里花时间了么?

指定位置Get

再看get方法,

ArrayList的get,因为是连续的内存,所以取数据很快。

  1. public E get(int index) {
  2. rangeCheck(index);
  3. return elementData(index);
  4. }
  5. E elementData(int index) {
  6. return (E) elementData[index];
  7. }

 再看LinkedList的get,是通过指针遍历的,直到是这个index为止。

这里还有判断size,如果是size的前一半,则通过first节点往后去找,如果在后一半则通过last节点往前找,这样会更快,所以LinkedList的查找其实也不慢。

  1. public E get(int index) {
  2. checkElementIndex(index);
  3. return node(index).item;
  4. }
  5. Node<E> node(int index) {
  6. // assert isElementIndex(index);
  7. if (index < (size >> 1)) {
  8. Node<E> x = first;
  9. for (int i = 0; i < index; i++)
  10. x = x.next;
  11. return x;
  12. } else {
  13. Node<E> x = last;
  14. for (int i = size - 1; i > index; i--)
  15. x = x.prev;
  16. return x;
  17. }
  18. }

指定位置Add

ArrayList的add(index,element)

这里是可以扩容的,将index后半段拷贝到index+1,然后在index插入一个新的,但没想到这么快。

其实也能想到System.arraycopy是native,所以快也能理解

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

然后是LinkedList的add(index,element)

无非是指针的指向变化而已,但没想到比上面的System.arraycopy还要慢,果然不愧为native方法。

  1. public void add(int index, E element) {
  2. checkPositionIndex(index);
  3. if (index == size)
  4. linkLast(element);
  5. else
  6. linkBefore(element, node(index));
  7. }
  8. void linkBefore(E e, Node<E> succ) {
  9. // assert succ != null;
  10. final Node<E> pred = succ.prev;
  11. final Node<E> newNode = new Node<>(pred, e, succ);
  12. succ.prev = newNode;
  13. if (pred == null)
  14. first = newNode;
  15. else
  16. pred.next = newNode;
  17. size++;
  18. modCount++;
  19. }
  20. void linkLast(E e) {
  21. final Node<E> l = last;
  22. final Node<E> newNode = new Node<>(l, e, null);
  23. last = newNode;
  24. if (l == null)
  25. first = newNode;
  26. else
  27. l.next = newNode;
  28. size++;
  29. modCount++;
  30. }

 

所以项目中大部分用ArrayList也就是可以理解。

不过ArrayList是连续的内存空间,在内存空间很紧张情况下,LinkedList内存利用率更高。

 

 

 

原文链接:https://blog.csdn.net/lw18751836671/article/details/117710230



所属网站分类: 技术文章 > 博客

作者:我很伤感

链接:http://www.javaheidong.com/blog/article/222407/27763fefbc19261e2698/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

8 0
收藏该文
已收藏

评论内容:(最多支持255个字符)