ArrayList和LikendList

本文详细对比了Java中ArrayList与LinkedList两种数据结构的特性与应用场景,包括添加、删除、查询等操作的效率分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

集合类Collenction是java中十分重要的类。

在java中集合类主要包含了List和Set。我们知道List和Set之间有如下的区别:

1:List,有序但是可以有重复的元素。

2:Set,无序但是不可有重复的元素。

现在先把List拎出来剖析一番,List包含两个使用频率颇高的子类,ArrayList和LinkedList。

对于ArrayList和LikedList,我们在实际运用中要学会去选择,选择就要根据它们的特性和某一方面的优势进行依据和判断。

对于ArrayList,在查询和更新方面有优势;对于LinkedList,在删除和添加有优势

原因就在于ArrayList是一个基于有序数组的数据结构,而对于LinkedList,它是基于链表的数据结构。

数据结构分析:

ArrayList源码

[java] view plain copy
  1. /** 
  2.  * The array buffer into which the elements of the ArrayList are stored. 
  3.  * The capacity of the ArrayList is the length of this array buffer. Any 
  4.  * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to 
  5.  * DEFAULT_CAPACITY when the first element is added. 
  6.  */  
  7. private transient Object[] elementData;  
一目了然,底层就是一个Object数组。

LinkedList源码

[java] view plain copy
  1. private static class Node<E> {  
  2.     E item;  
  3.     Node<E> next;  
  4.     Node<E> prev;  
  5.   
  6.     Node(Node<E> prev, E element, Node<E> next) {  
  7.         this.item = element;  
  8.         this.next = next;  
  9.         this.prev = prev;  
  10.     }  
  11. }  
这是在源码中定义的一个内部类,相当于定义了链表结构的工具类。

next是item的后指针,prev是item的前指针。对于item来说,知道了它的前后的数据,按照顺序排列好,那么这三个元素就构成了LinkedList的链表结构了。

通过构造方法,实现了链表功能。

特性分析:


首先以事实比较下两者添加数据的效率:


[java] view plain copy
  1. public static void main(String[] args) {  
  2.     int num = 1000000;  
  3.     ArrayList<Integer> arrayList = new ArrayList<Integer>();  
  4.     long arrayStart = System.currentTimeMillis();  
  5.     for (int i = 0; i < num; i++) {  
  6.         arrayList.add(i);  
  7.     }  
  8.     long arrayEnd = System.currentTimeMillis();  
  9.     long arrayTime = arrayEnd - arrayStart;  
  10.     LinkedList<Integer> linkedList = new LinkedList<Integer>();  
  11.     long linkedStart = System.currentTimeMillis();  
  12.     for (int i = 0; i < num; i++) {  
  13.         linkedList.add(i);  
  14.     }  
  15.     long linkedEnd = System.currentTimeMillis();  
  16.     long linkedTime = linkedEnd - linkedStart;  
  17.     System.out.println("ArrayList  time:" + arrayTime);  
  18.     System.out.println("LinkedList time:" + linkedTime);  
  19. }  
结果如下
[plain] view plain copy
  1. ArrayList  time:49  
  2. LinkedList time:520  
十分明了地验证了在添加数据这一块,ArrayList的效率是远远优于LinkedList的。

分析add():首先是ArrayList,

[java] view plain copy
  1. public boolean add(E e) {  
  2.     ensureCapacityInternal(size + 1);  // Increments modCount!!  
  3.     elementData[size++] = e;  
  4.     return true;  
  5. }  
做了两步工作,

1.将ArrayList的长度加1.

2.在ArrayList(其实通过源码,发现ArrayList就是一Object个数组)最后赋上我们所添加的值。

再来看看LinkedList,

[java] view plain copy
  1. public boolean add(E e) {  
  2.     linkLast(e);  
  3.     return true;  
  4. }  
这里就要调用LlinkLast这个方法了,
[java] view plain copy
  1. /** 
  2.  * Links e as last element. 
  3.  */  
  4. void linkLast(E e) {  
  5.     final Node<E> l = last;  
  6.     final Node<E> newNode = new Node<>(l, e, null);  
  7.     last = newNode;  
  8.     if (l == null)  
  9.         first = newNode;  
  10.     else  
  11.         l.next = newNode;  
  12.     size++;  
  13.     modCount++;  
  14. }  
通过Node这个工具类的构造方法,通过链表的形式,将元素添加到LinkedList的最后面。

再看下例

[java] view plain copy
  1. public static void main(String[] args) {  
  2. <span style="white-space:pre">        </span>int num = 1000000;  
  3. <span style="white-space:pre">        </span>ArrayList<Integer> arrayList = new ArrayList<Integer>();  
  4. <span style="white-space:pre">        </span>for (int i = 0; i < num; i++) {  
  5. <span style="white-space:pre">            </span>arrayList.add(i);  
  6. <span style="white-space:pre">        </span>}  
  7. <span style="white-space:pre">        </span>long arrayStart = System.currentTimeMillis();  
  8. <span style="white-space:pre">        </span>for (int i = 0; i < 100; i++) {  
  9. <span style="white-space:pre">            </span>arrayList.add(new Random().nextInt(100), i);  
  10. <span style="white-space:pre">        </span>}  
  11. <span style="white-space:pre">        </span>long arrayEnd = System.currentTimeMillis();  
  12. <span style="white-space:pre">        </span>long arrayTime = arrayEnd - arrayStart;  
  13. <span style="white-space:pre">        </span>LinkedList<Integer> linkedList = new LinkedList<Integer>();  
  14. <span style="white-space:pre">        </span>for (int i = 0; i < num; i++) {  
  15. <span style="white-space:pre">            </span>linkedList.add(i);  
  16. <span style="white-space:pre">        </span>}  
  17. <span style="white-space:pre">        </span>long linkedStart = System.currentTimeMillis();  
  18. <span style="white-space:pre">        </span>for (int i = 0; i < 100; i++) {  
  19. <span style="white-space:pre">            </span>linkedList.add(new Random().nextInt(100), i);  
  20. <span style="white-space:pre">        </span>}  
  21. <span style="white-space:pre">        </span>long linkedEnd = System.currentTimeMillis();  
  22. <span style="white-space:pre">        </span>long linkedTime = linkedEnd - linkedStart;  
  23. <span style="white-space:pre">        </span>System.out.println("ArrayList  time:" + arrayTime);  
  24. <span style="white-space:pre">        </span>System.out.println("LinkedList time:" + linkedTime);  
  25. <span style="white-space:pre">    </span>}  
几乎和上例一样,只是我添加的方式不同,不添加到list的尾部,而是在list中间随机插入,结果如下
[java] view plain copy
  1. ArrayList  time:274  
  2. LinkedList time:1  
这时,LinkedList的效率远远超过了ArrayList,从事实来看,我们不能说ArrayList添加元素的效率超过LinkedList这一结论。

源码分析,ArrayList

[java] view plain copy
  1. /** 
  2.  * Inserts the specified element at the specified position in this 
  3.  * list. Shifts the element currently at that position (if any) and 
  4.  * any subsequent elements to the right (adds one to their indices). 
  5.  * 
  6.  * @param index index at which the specified element is to be inserted 
  7.  * @param element element to be inserted 
  8.  * @throws IndexOutOfBoundsException {@inheritDoc} 
  9.  */  
  10. public void add(int index, E element) {  
  11.     rangeCheckForAdd(index);  
  12.     ensureCapacityInternal(size + 1);  // Increments modCount!!  
  13.     System.arraycopy(elementData, index, elementData, index + 1,  
  14.                      size - index);  
  15.     elementData[index] = element;  
  16.     size++;  
  17. }  
我们观察到的System.arraycopy(elementData, index, elementData, index + 1,size - index);出现,将新元素添加到指定的位置,然后原数组指定位置之后的对象统一往后移一位。

LinkedList:

[java] view plain copy
  1. /** 
  2.  * Inserts the specified element at the specified position in this list. 
  3.  * Shifts the element currently at that position (if any) and any 
  4.  * subsequent elements to the right (adds one to their indices). 
  5.  * 
  6.  * @param index index at which the specified element is to be inserted 
  7.  * @param element element to be inserted 
  8.  * @throws IndexOutOfBoundsException {@inheritDoc} 
  9.  */  
  10. public void add(int index, E element) {  
  11.     checkPositionIndex(index);  
  12.   
  13.     if (index == size)  
  14.         linkLast(element);  
  15.     else  
  16.         linkBefore(element, node(index));  
  17. }  
如果添加的是在末尾,和add(E)一样调用linkedLast方法,如果是在链表中间,则调用linkedBefore
[java] view plain copy
  1. /** 
  2.  * Inserts element e before non-null Node succ. 
  3.  */  
  4. void linkBefore(E e, Node<E> succ) {  
  5.     // assert succ != null;  
  6.     final Node<E> pred = succ.prev;  
  7.     final Node<E> newNode = new Node<>(pred, e, succ);  
  8.     succ.prev = newNode;  
  9.     if (pred == null)  
  10.         first = newNode;  
  11.     else  
  12.         pred.next = newNode;  
  13.     size++;  
  14.     modCount++;  
  15. }  
我们观察到,也是很简单的通过Node工具类,遵循其制定的链表结构的规则,在链表中间插入新对象。

通过这种情况的比较,通过arrayCopy明显要对内存的操作更多,开销更大。

光看一个数据添加,不一样的结果。

相比于添加,其余的数据操作也会有不同的效率,通过源码分析,我们可以看出不同的情况下,应该对List有不同的选择。

1. 快速插入,删除元素,队列模型应该使用LinkedList。
2.而对于随机访问元素,此时有下标的数组模型更占优,应该使用ArrayList。

感觉总体思路不是那么清晰,权当作自己学习的笔记,有问题和错误的地方,还请拍砖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值