源码均以JDK8作为参考
LinkedList在JDK1.2中引入,继承自AbstractSequentialList<E>抽象类和实现了List<E>接口,JDK1.5进行Collection框架重构时,由于其链表的特性与Queue及其相似,
LinkedList实现了Queue<E>接口,
在JDK1.6中由于新引入了Deque<E>双端队列(Deque<E>继承自Queue<E>),LinkedList放弃Queue<E>接口,通过实现Deque<E>接口,间接的实现了Queue<E>接
口。由于LinkedList在JDK版本上升过程中的多次升级,使得LinkedList本身的方法存在部分的重叠部分,这些部分都是针对不同的数据结构的实现,但是在LinkedList中,他
们的作用是相同的,而且存在着相互引用的关系。
1.LinkedList是一个Doubly-linked list, 即双向链表,实现了自前自后的索引方式。
描述LinkedList是一个双向链表,是因为LinkedList通过特有的私有内部类实现了一个链,链上的每个节点知晓前后节点引用,因此可以向前向后索引。
3.LinkedList提供两个构造方法初始化
public LinkedList()
: 构建一个空的LinkedList对象
public LinkedList(Collection<? extends E> c)
: 构建一个空的LinkedList对象,将集合c加入到链表中
3.LinkedList类内部只有三个用于记录结构信息的变量
size:当前LinkedList内部元素个数
first:链表头部Node<E>元素
last:链表尾部Node<E>元素
2.LinkedList链节点实现Node<E>:
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;
}
}
item:当前元素的值
next:下一个Node<E>节点的索引
prev:上一个Node<E>节点的索引
LinkedList持有First和Last节点的索引,而每个节点都有item、next、prev的值,因此LinkedList可以通过持有的First和Last节点自前自后遍历整个链表。
4.E get(int):方法分析
由于LinkedList是一个双向链表,因此在索引时,其速度较于ArrayList和Vector(ArrayList和Vector基于数组实现)要慢很多,因此在get(int)方法调用时,LinkedList内部实现
并非按照单一的方式进行索引的。
源码如下:
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;
}
}
当查找位置index < (size >> 1)时(注:size >> 1是移位操作),从链表头部开始查找,当index >= (size >> 1)时,从链表尾部开始查找。LinkedList这样的操作是在避免链表
本身造成的索引低下的问题。
5.遍历:
ArryList<E>和Vector<E>实现了RandomAccess接口,RandomAccess接口是一个标记接口,实现此接口表明该类支持快速索引,即for遍历,且for遍历的速度优于Iterator
迭代的速度。
LinkedList<E>没有实现RandomAccess,因此LinkedList<E>的Iterator迭代速度要优于for遍历的速度。
示例代码如下:
List<String> list = new LinkedList<String>();
list.add("123");
list.add("456");
list.add("789");
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
当然也可以使用for遍历:
for(int i = 0;i<list.size(); i++){
System.out.println(list.get(i));
}
6.应用场景
由于LinkedList具有链表的特性,而链表结构的优点是便于向集合中插入和删除对象。那么经常向集合中插入或删除对象,应用LinkedList效率较高。但是LinkedList的随
机访问效率比较低下。这种分析都是在一定的数据量基础之上的,比如,虽然LinkedList拥有列表的高速插入和删除特性,但是数据达到一定数量级,依然没有基于数组的
ArrayList效率高。所以因时制宜,根据不同的应用场景选用不同的数据结构,才是正解。
LinkedList所有public方法:由于LinkedList在JDK版本更迭过程中的多次修改,其公开方法来自于三个接口的实现,尽管实现的接口不同,但是对外开放的功能是相同
的,在JDK1.5和JDK1.6的实现过程中,很多方法都是直接基于JDK1.2版本已有的功能实现的。
JDK1.2:AbstractSequentialList<E>和List<E>的方法实现
E getFirst(): 获取链表头元素
E getLast(): 获取链表尾元素
E removeFirst(): 移除链表头元素
E removeLast(): 移除链表尾元素
void addFirst(E e):在链表头部加入元素
void addLast(E e): 在链表尾部加入元素
boolean contains(Object o):是否包含指定元素
int size():获取链表内元素数
boolean add(E e): 在链表尾部加入元素
boolean remove(Object o): 在链表中移除指定元素
boolean addAll(Collection<? extends E> c): 将集合c中的元素加入到链表尾部
boolean addAll(int index, Collection<? extends E> c): 在指定index位置加入集合c的元素
void clear(): 清空链表
E get(int index): 获取index位置处的元素
E set(int index, E element): 替换index位置处的元素element
void add(int index, E element): 在index位置处加入元素element
E remove(int index):移除index位置处的元素
int indexOf(Object o): 获取元素o的位置index
int lastIndexOf(Object o): 获取元素o的位置index,从链尾向前
ListIterator<E> listIterator(int index):从指定位置获取迭代器
public Object clone(): 拷贝,浅拷贝
public Object[] toArray(): 链表转换为数组
JDK1.5:Queue<E>的方法实现
E peek(): 遍历检索,获取链表头元素,但不移除头元素,若链表为空,返回null
E element(): 遍历索引,获取链表头元素,若链表为空,抛出异常
E poll():遍历索引,获取链表头元素,并移除头元素,若链表为空,返回null
E remove():移除链表头元素,若链表为空,返回null
boolean offer(E e):在链表尾部加入元素e
JDK1.6:Deque<E>的方法实现
boolean offerFirst(E e):在链表头部加入元素e
public boolean offerLast(E e):在链表尾部加入元素e
E peekFirst(): 遍历索引,获取链表头元素,但不移除头元素,若链表为空,返回nulll
E peekLast(): 遍历索引,获取链表尾元素,但不移除尾元素,若链表为空,返回nulll
E pollFirst(): 获取链表头元素,并移除头元素,若链表为空,返回null
E pollLast(): 获取链表尾元素,并移除尾元素,若链表为空,返回null
void push(E e): 插入链表头元素
E pop():获取链表头元素,并移除
boolean removeFirstOccurrence(Object o):移除元素o,自前向后索引
boolean removeLastOccurrence(Object o):移除元素o,自后向前索引
public Iterator<E> descendingIterator(): 获取典型迭代器