(注意:本文源码基于JDK1.8)
LinkedList实现了List接口,截图为List接口中定义的添加元素的方法,实现List接口表示具备线性表的能力【此文未提及迭代器中的add()方法】
add(E)方法分析
public boolean add(E e) {
linkLast(e);
return true;
}
用于添加1个元素的方法,传入参数为添加的元素对象
1、调用linkLast()方法
首先调用linkLast()方法,传入参数为要添加的元素对象
2、向调用者返回添加结果
默认返回值为true,表示添加元素成功
add(int,E)方法分析
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
用于在指定下标处添加元素的方法,传入的第一个参数index表示添加元素的位置,第二个参数表示添加的元素对象
1、检查下标值是否符合要求
调用checkPositionIndex()方法用于检查添加元素的位置,只有符合范围的下标才能正常添加,正常范围是指在现有范围内才能添加元素,比如现在LinkedList对象持有3个元素,
那么index的合法下标范围是0-2,不符合范围的index值,作者会抛出异常告诉调用者
2、检查元素位置,不同的情况采用不同的方法
当index值与size值相同,说明指定的下标正好需要添加到LinkedList的尾部,那么此时调用的是linkLast()方法
当在中间处添加元素时,则调用linkBefore()方法添加元素
addAll(Collection)方法分析
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
用于添加多个元素的方法,传入参数为持有多个元素的Collection对象
1、调用addAll()方法添加元素
2、向调用者返回添加元素的结果,其实是addAll()方法的结果
addAll(int,Collection)方法分析
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
用于向指定位置处添加多个元素,传入的第一个参数表示添加元素的起始位置处,传入的第二个参数表示持有多个元素的Collection对象
1、检查下标值是否符合要求
通过调用checkPostionIndex()方法检查下标是否符合要求,不符合要求作者会抛出异常
2、将传入的多个元素转化为数组对象
传入的Collection持有多个元素,先将这些元素转换为Object数组对象,并由局部变量a负责保存……
3、提取传入的元素数量
通过数组对象的属性length提取出元素总数,并由局部变量numNew负责保存
4、检查添加的元素总数
当添加的元素总数numNew为0时,整个方法返回false,表示添加元素失败
5、先创建两个Node类型的局部变量
6、检查即将添加元素的位置index,根据不同的位置采用不同的添加方法
在尾部添加元素:当index与size值相同,此时刚好在尾部添加元素,这时局部变量succ赋值为null,局部变量pred则负责保存即将添加元素的上一个元素
在中间添加元素:在需要在中间位置添加元素时,局部变量succ用于保存即将添加元素的下一个元素,而局部变量pred则仍然负责保存即将添加元素的上一个元素
7、遍历传入的所有元素
每次会传入的元素对象,强行转换为类型参数对应的对象
针对添加的元素为第一个时,为LinkedList对象持有的first指向第一个Node对象
添加的元素不是第一个时,则使用节点对象的next一个一个的将元素链接起来……
更新当前节点对象……链表的元素添加,真是好玩
8、更新即将插入的元素的下一个元素的情况,两种情况
在尾部添加元素:即将添加的元素本身是从最后一个元素开始添加的,此时succ为null,所以需要更新last指向最后一个节点对象
在中间添加元素:更新添加的最后一个元素指向最后一个节点,更新最后一个元素指向已添加元素的最后一个
9、更新元素数量
size + numNew,然后赋值给size
10、更新modCount值,+1,表示LinekdList中已发生改变
11、向调用者返回true,表示添加元素成功
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++;
}
向LinkedList的尾部添加元素的方法,LinkedList中向尾部添加元素,主要使用的是该方法,传入的参数为需要添加的元素对象,它的类型是类型参数E,由我们指定元素的类型参数
1、本地保存last指向的Node对象
LinkedList对象持有的实例变量last,指向的是双向链表中的最后一个节点对象(Node对象),此处将其引用保存到局部变量l中
2、创建Node对象,用于持有元素对象
通过new一个Node对象,Node对象需要传入3个参数,第一个参数为当前LinkedList中的最后一个Node对象,第二个参数表示即将要添加的元素对象,第三个参数表示当前Node对象指向的下一个元素,此处传入的是null,因为添加的Node对象都是在尾部的……
3、更新LinekdList持有的最后一个Node对象
为LinkedList对象持有的last,指向为新创建的Node对象
4、更新原尾部节点对象指向新的Node对象,对两种情况处理
首个元素:若添加的元素为第一个元素,此时局部变量l一定为null,因为LinkedList并没有持有任何元素,此时更新LinkedList对象持有的first指向新添加的Node对象
非首个元素:将原尾部的最后一个Node对象持有的下一个元素引用next指向新添加的元素对象
5、更新元素总数
size增加1
6、更新modCount值,表示LinkedList的元素发生改变,防止多线程下使用LinkedList,这是fail-fast机制的要求
linkBefore()方法分析
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
用于向某个元素的前面插入一个元素的方法,传入的参数e表示即将要插入的元素,传入的参数succ表示,即将插入的元素的下一个元素……,即某个元素……
1、先提取某个元素的上一个元素,保存在局部变量pred中
2、创建Node对象,用于持有即将添加的元素对象
创建Node对象,需传入3个参数,第一个参数表示当前Node对象指向的上一个Node对象,第二个参数表示元素对象,第三个参数表示当前Node对象指向的下一个Node对象,新创建的Node保存在局部变量newNode中
3、更新succ的指向新插入的Node对象
4、更新新插入Node对象的上一个Node对象的指向,这里是两种情况
新添加的是第一个Node对象:此时更新first指向Node对象
新添加的为中间的Node对象:此时更新上一个Node对象指向新创建的Node对象
5、更新元素总数
size+1
6、modCount增加1,表示LinkedList的元素发生改变
checkPositionIndex()方法分析
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
用于检查插入元素的位置是否合理的方法,传入的参数index表示需要插入的位置,比如第一个元素的位置是0
1、调用isPositionIndex()方法判断index是否合理
2、当判断不合理时,先生成异常文本信息
由outOfBoundsMsg()方法生成文本字符串
3、创建IndexOutOfBoundsException对象,并向用户抛出……
isPositionIndex()方法分析
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
用于判断位置是否合理的方法,传入的参数index表示添加元素的位置
当传入的位置index大于等于0并且小于等于size值时,才是符合要求的位置
总结
1、LinkedList作为双向链表,使用Node对象持有每个元素对象,本片未详细提及Node对象,后面的文章会分析
2、向尾部添加元素、向中间添加元素,注意引用的变化