介绍
- 使用双向链表实现,由节点组成的数据结构
- LinkedList没有初始容量
- 允许存储null值
- 不是线程安全的
- 可以通过迭代器遍历,具有fail-fast机制
- 增删快
继承关系 继承类介绍
- AbstractSequentialList:继承自AbstractList,重写了增删改查的方法,通过listIterator实现,这里可以不关注,毕竟LinkedList重写了这些方法,看起来它好像并没有什么diao用,也许设计出来是为了让我们实现此类来自定义索引检索集合以减少部分工作量,毕竟它的父类AbstractList没有实现增删改查的方法,有人又要捣鼓了(还是我自己)为什么ArrayList没有继承AbstractSequentialList,别问我,问设计者去
- List:这个在ArrayList已经聊过了,没看过的倒回去看一下
- Deque:LinkedList实现了Queue接口,用于支持两端元素的插入,移除和检索
- Cloneable:可被克隆
- Serializable:可被序列化
源码解析 变量 常用方法解析 常用技术点(所有代码都是简化后的源码 减少篇幅 结合源码食用更佳)
Node,它是LinkedList中存储数据的节点,结构如下:
//存储数据域
E item;
//指向链表的下一个节点
Node<E> next;
//指向链表的上一个节点(prev = previous)
Node<E> prev;
图示:
变量
//总节点数
transient int size = 0;
//指向第一个节点的指针
transient Node<E> first;
//指向最后一个节点的指针
transient Node<E> last;
常用方法
node()
作用:根据索引获取列表中指定的元素返回!
这不是一个public方法,但是LinkedList依赖它来从列表中通过索引检索到索引对应位置的元素,增删改查等方法都会用到!提前学习是有必要的!
/**
* 我不想单独去解析每一行代码的意义,这个方法需要一个总结,最后再举个例子
* 首先将整个列表分为两个范围,分界线就是列表size>>1
* 再判断index属于哪一范围,在这一范围内遍历 而index就是遍历的条件,只要i达到index的条件 循环结束 返回获取的节点
* 很蛋疼 这个总结看起来不是很友好 所以需要举个例子
* 条件是: size = 100 index = 60
* 100 >> 1 = 50
* if (60 < 50))
* 走else
* else的判断很简单 它的遍历范围是99到60之间 当i递减到61时 此时x.prev获取到的就是它的上一个节点也就是index = 60
* 此时i = 60 也就不满足条件 循环结束
*
*
*/
Node<E> node ( int 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;
}
}
emmmmmm 本来很简单的方法被我解释的好像复杂了…
add(E e)
作用:将指定的元素e放到列表尾部,等价于addLast()
//获取last节点指针
final Node<E> l = last;
//根据旧的last指针和需要保存的元素e 生成新的last节点
//next传null是因为该节点已经是last节点了 是最后一个
final Node<E> newNode = new Node<>(l, e, null);
//将last节点指针重新赋值
last = newNode;
//如果第一次add 那么l==null 这时first和last指向同一个Node
if (l == null)
first = newNode;
else
//如果不是第一次添加 需要保证上一个节点的next指向当前的last节点
l.next = newNode;
size++;
modCount++;
都知道LinkedList的插入和删除比ArrayList效率高,为什么?
部分人可能会回答前者是链表实现的后者是数组实现的
这样回答也对,但是具体的细节呢?
1.LinkedList 不需要扩容,避免了扩容等判断和扩容量计算及数组复制等操作
2.LinkedList 删除或者添加任意位置后续元素不需要移位
这样回答更体现你对他们具体实现的了解,面试官可能更倾向于听这个,至于其他的比如删除或修改或查询其实就差不多了
remove()
作用:移除头部的第一个元素并返回,等价于removeFirst(),其实也就是在remove方法中直接调用了
removeFirst()
//获取第一个节点
final Node<E> f = first;
//获取第一个节点的数据 主要用于返回给调用者
final E element = f.item;
//获取第一个节点的下一个节点
final Node<E> next = f.next;
//将第一个节点数据置为null
f.item = null;
//将第一个节点的next指针置为null
f.next = null; // help GC
//将上面获取到的下一个节点置为第一个节点
first = next;
//如果列表中原本只有一个节点 那么next肯定会null,就不存在下一个节点 ,此时first和last都置为null
if (next == null)
last = null;
else
//如果列表中原本不止一个节点,那么此时first节点的prev指针就要指向null 因为此节点已经升级为first 它就是首节点
next.prev = null;
size--;
modCount++;
//返回已删除的数据
return element;
set(int index, E element)
作用:用指定的元素替换列表中指定位置的元素。
//根据index从列表中获取到对应位置的节点
Node<E> x = node(index);
//使用element对数据进行覆盖
x.item = element;
核心方法就是node,所以上面第一个node方法一定要搞明白
get(int index)
//根据索引从node方法中获取到对应位置的节点 再获取数据返回
return node(index).item;
扩展知识点
LinkedList实现栈功能
LinkedList实现队列功能
算了,我还是不写了,百度吧 有一堆.我想偷懒