想看我更多文章:【张旭童的博客】http://blog.csdn.net/zxt0601
想来gayhub和我gaygayup:【mcxtzhang的Github主页】https://github.com/mcxtzhang
概述
本篇是Java集合类解析的第二篇,上一篇[面试必备:ArrayList源码解析(JDK8)]里,我们唠了ArrayList
,今儿来继续说LinkedList
.面试中,这兄弟俩也经常会拿来比较。
它们两可以说是List
接口的两种不同的实现,ArrayList
的增删效率低,但是改查效率高。
而LinkedList
正好相反,增删由于不需要移动底层数组数据,其底层是链表实现的,只需要修改链表节点指针,所以效率较高。
而改和查,都需要先定位到目标节点,所以效率较低。
开篇前,再说一遍Collection.toArray();
。
这个方法很重要,不管是ArrayList
、LinkedList
在批量add的时候,都会先转化成数组去做。 因为数组可以用for循环直接花式遍历。比较方便 高效
套路依旧,
本文将从几个常用方法下手,来阅读LinkedList
的源码。
按照从构造方法->常用API(增、删、改、查)的顺序来阅读源码,并会讲解阅读方法中涉及的一些变量的意义。了解LinkedList
的特点、适用场景。
如果本文中有不正确的结论、说法,请大家提出和我讨论,共同进步,谢谢。
概述
概括的说,LinkedList
是线程不安全的,允许元素为null的双向链表。
其底层数据结构是链表,它实现List<E>, Deque<E>, Cloneable, java.io.Serializable
接口,它实现了Deque<E>
,所以它也可以作为一个双端队列。和ArrayList
比,没有实现RandomAccess
所以其以下标,随机访问元素速度较慢。
因其底层数据结构是链表,所以可想而知,它的增删只需要移动指针即可,故时间效率较高。不需要批量扩容,也不需要预留空间,所以空间效率比ArrayList
高。
缺点就是需要随机访问元素时,时间效率很低,虽然底层在根据下标查询Node的时候,会根据index判断目标Node在前半段还是后半段,然后决定是顺序还是逆序查询,以提升时间效率。不过随着n的增大,总体时间效率依然很低。
当每次增、删时,都会修改modCount。
构造方法
//集合元素数量
transient int size = 0;
//链表头节点
transient Node<E> first;
//链表尾节点
transient Node<E> last;
//啥都不干
public LinkedList() {
}
//调用 public boolean addAll(Collection<? extends E> c) 将集合c所有元素插入链表中
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
构造方法基本没干啥。
节点Node结构:
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;
}
}
可以看出,这是一个双向链表。
增
1 addAll
接上文,先说addAll
//addAll ,在尾部批量增加
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);//以size为插入下标,插入集合c中所有元素
}
//以index为插入下标,插入集合c中所有元素
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//检查越界 [0,size] 闭区间
Object[] a = c.toArray();