知不可为而为之
不了解ArrayList的同学出门右转:ArrayList
了解过ArrayList之后但不了解LinkedList的同学就进来吧(嘿嘿)
简单理解LinkedList
在Java集合类中,除了使用最多的ArrayList,便是LinkedList,而这两个也是面试的重点,那么LinkedList究竟是什么样的呢?
编程语言的每个语法都有它深层次的含义,这次,我们还是看看它的源码来深入了解LinkedList。
我们还是先来看一下它的继承关系:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
可以看到,LinkedList实现了Deque和list接口。
壹·相关变量
//容量
transient int size = 0;
/**
*头节点
*指向第一个节点的指针。
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
*尾节点
* 指向最后一个节点的指针。
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
我们 ctrl + 点击 找到Node< E >,可以发现它是一个双向链表,具体源码如下:
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
至于具体的方法,便是基本数据结构的链表操作实现的,这里就不做解释了,感兴趣的同学可以看看源码。
这里只列举一下对比二者性能的相关LinkedList源码:
//获得第index个节点的值
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//设置第index元素的值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
//在index个节点之前添加新的节点
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//删除第index个节点
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
//定位index处的节点
Node<E> node(int index) {
// assert isElementIndex(index);
//index<size/2时,从头开始找
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //index>=size/2时,从尾开始找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
可以看出,LinkedList通过for循环进行查找,同时LinkedList也在查找方法上做了优化,比如index < size / 2,则从左边开始查找,反之从右边开始查找。
二者异同
还是先看一下他们两个的关系
可以看到他们两个都实现了list接口,可以说是好兄弟了,之所以功能不同是因为实现list接口方法的方式不一样(从这里也可以看出接口的好处,面向不同的业务需求就会有不同的是实现方式),List同时拥有Collection和Iterable的特性。
虽然是好兄弟,但是像现实中一样,肯定也有各自的特性,那下面我们就来讨论一下他们两个的异同。
壹·相同点
ArrayList基于动态数组实现,LinkedList基于双向链表,数组和链表都是线性表,对比于线性表的数据结构便是图和树。
贰·不同点
- ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构
- ArrayList存储方式为连续的,而LinkedList在物理逻辑上是不连续的(这个就是链表的特性了)
- 对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针,而ArrayList有索引index,在源码中可以看到,ArrayList想要get(int index)元素时,直接返回index位置上的元素,而LinkedList需要通过for循环进行查找,虽然已经优化,但效率肯定不及前者
- 对于添加和删除操作add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。但是实际情况并非这样,对于添加或删除,LinkedList和ArrayList并不能明确说明谁快谁慢(实践是检验真理的唯一标准,感兴趣的同学可以自己写个程序体会一下)
- 当插入的数据量很小时,两者区别不太大
- 当插入的数据量大时,大约在容量的1/10之前,LinkedList会优于ArrayList
- 在其后就劣与ArrayList,且越靠近后面越差
综上:
所以一般首选用ArrayList,由于LinkedList可以实现栈、队列以及双端队列等数据结构,所以当特定需要时候,使用LinkedList,数据量小的时候,两者差不多,视具体情况去选择使用;当数据量大的时候,如果只需要在靠前的部分插入或删除数据,那也可以选用LinkedList,反之选择ArrayList效率更高。