(注意:本文源码基于JDK1.8)
Deque接口扩展了Queue接口,LinkedList实现了Deque接口,Deque接口表示双端队列的能力,双端队列在线性表的两端都可以插入元素与删除元素。本文分析与Deque接口相关的添加元素的方法:addFirst、addLast、offerFirst、offerLast、add、offer,共计6个方法
(图示为持有三个元素的LinkedList对象)
addFirst()方法分析
public void addFirst(E e) {
linkFirst(e);
}
用于向双向链表的头部添加一个元素的方法,传入的参数表示一个参数类型为E的元素对象
在方法体中,直接将传入的元素对象e传入到linkFirst()方法中进行插入元素的工作(在第三篇文章中,曾分析过此方法)
linkFirst()方法分析
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
向双向链表头部添加元素的方法,传入的参数为一个参数类型E的元素对象
1、获取当前LinkedList对象持有的第一个元素
首先取出LinkedList对象持有的指向第一个结点对象的实例变量first,并由局部变量f保存,局部变量f为final修饰,不允许再次修改
2、创建的新的Node对象,用于持有元素对象
创建一个新的Node对象,Node对象需持有添加的元素对象e、以及旧的头结点对象f,新的Node对象由局部变量newNode负责保存(提示:Node对象持有的next会连接(指向)到原来的第一个结点对象f)
3、变更LinkedList对象持有的first指向新添加的元素
将LinkedList对象持有的first指向新创建的结点对象newNode,此时first指向了新插入的第一个结点对象
4、对首次添加元素作处理
如果LinkedList是第一次添加元素,则LinkedList对象持有的last,也会指向新添加的结点对象newNode
5、针对LinekdList非首次添加元素作处理
如果不是第一次添加元素,则旧的头结点对象持有的prev需要指向新创建的结点对象newNode(因为旧的头结点成为了第二个结点对象)
6、更新元素总数
LinkedList对象持有的元素总数size需要加1(size++)
7、fail-fast机制更新
代表LinkedList的元素发生改变的modCount也加1(modCount++)
此时的方法已经添加元素成功,引用是这么重要呀,哈哈!
offerFirst()方法分析
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
用于向双向链表的头部添加一个元素,返回值表示添加元素成功,传入参数类型E的元素对象
1、调用addFirst()方法添加元素
offerFirst()方法中,没有再去实现添加元素的方法,而是直接将传入的元素对象e再传入到addFirst()方法中完成向双向链表头部添加元素
2、向调用者返回添加元素的结果
返回值为true,代表成功添加一个元素
add()方法分析
public boolean add(E e) {
linkLast(e);
return true;
}
看起来面熟吧?之前我也分析了一个add()方法,没错,它们是同一个方法,List接口与Deque接口都规范了一个称作add的方法,此处LinkedList在实现add()方法时,只要实现一个具体的方法即可,具体源码分析请看【第三篇:添加元素-List接口】
addLast方法()分析
public void addLast(E e) {
linkLast(e);
}
用于将元素添加到双向链表尾部的方法,传入的参数为类型参数E的对象
与addFirst()方法不同,addLast()方法添加的元素是在线性表的尾部,内部调用linkLast()方法完成元素的添加
linkLast()方法分析
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
用于向双向链表的尾部添加一个元素,在第三篇添加元素的文章中已经做了分析,这里作为复习!
1、本地获取最后一个节点对象
首先将LinkedList对象持有的最后一个结点对象的实例变量last,先保存到局部变量l中
2、创建新的Node对象
新创建一个Node对象,Node对象持有了局部变量l、元素对象e、以及一个null值,然后把该Node对象先由局部变量newNode负责保管,局部变量l与newNode均为final修饰,这样可以保证他们不能再被赋值。
3、第一次添加元素、非第一次添加元素的分开处理
新插入的元素对象在双向链表的尾部,所以LinedList对象持有的实例变量last则指向最新创建的Node对象newNode。根据当前LinkedList的情况,为持有的结点对象赋值,如果last为null,说明当前LinkedList还没有持有元素,此时新创建的newNode会赋值给LinkedList对象持有的first,即新创建的结点对象也是第一个结点,另外一种情况则是last不为null,说明当前LinkedList已经持有元素,此时会将旧的尾结点对象l持有的实例变量next指向新创建的结点对象newNode
4、最后则是将LinkedList的实际持有元素数量size加上1(size++),还有modCount结果也加1(modCount++),此时元素的添加过程已经完成
offerLast()方法分析
public boolean offerLast(E e) {
addLast(e);
return true;
}
内部实际调用addLast()方法完成插入元素的操作,比addLast()方法只多了一条语句,返回值true表示插入元素成功
offer()方法分析
public boolean offer(E e) {
return add(e);
}
用于向双向链表的尾部添加元素,内部实际调用的是add()方法具体源码分析请看【第三篇:添加元素-List接口】,add()方法的返回值将作为offer()方法的返回值
总结
1、offerFirst()方法内部调用的是addFirst()方法,offerFirst()方法只比addFirst()方法多了一个boolean值,其他都一样
2、offerLast()方法内部调用的是addLast()方法,自己没有做添加元素的具体实现
3、offer()方法内部调用的是add()方法完成实际添加元素的工作
4、添加元素的方法实际调用的是linkFirst()方法或者linkLast()方法完成添加元素工作
addFirst()与offerFirst()方法,元素添加到双向链表的头部
add()方法与offer()方法,元素添加到双向链表的尾部
addLast()与offerLast()方法,元素添加到双向链表的尾部