LinkedList 源码阅读
指针
//链表指针对象
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;
}
}
成员边变量
LinkedList 的 长度. size(); 返回的就是这个数值.
transient int size = 0;
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
LinkedList 的头部指针 和 尾部指针.
/**
* Pointer to first node. Invariant: (first == null && last == null) || (first.prev == null && first.item != null)
*/
// 指向第一个节点的指针
// 链表 头部 指针
transient Node<E> first;
/**
* Pointer to last node. Invariant: (first == null && last == null) || (last.next == null && last.item != null)
*/
// 指向最后一个节点的指针
// 链表 尾部 指针
transient Node<E> last;
构造函数
默认无参构造函数
/**
* Constructs an empty list. 默认构造函数
*/
public LinkedList() {}
带初始化集合的构造函数
public LinkedList(Collection<? extends E> c) {
// 调用无参构造函数
this();
// 将初始化集合 添加到链表
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
// 校验 索引下标是否合法.
// 上一层函数传入的参数 为链表的长度,此处合法跳过.
checkPositionIndex(index);
// 将初始化集合转为 Object 数组
Object[] a = c.toArray();
// 获取数组的长度
int numNew = a.length;
// 如果
if (numNew == 0)
// 结束函数.
return false;
// pred (上一个指针)
// succ 索引位置的指针
Node<E> pred, succ;
// 如果 索引位置 等于 集合长度
if (index == size) {
// 索引位置的指针 为 null
succ = null;
// 上一个指针便是 链表之前的 尾部指针.
pred = last;
} else {
// 获取 索引位置的指针. 下文有函数注释,
succ = node(index);
// 上一个指针 便是 索引位置指针的 上一个指针.
pred = succ.prev;
}
// 遍历 Object 数组
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 创建新的指针,
// 新指针的上一个指针 未 pred.
Node<E> newNode = new Node<>(pred, e, null);
// 如果新指针的 上一个指针 为 null
if (pred == null)
// 将新指针设置为 头部指针
first = newNode;
else
// 否则 将新指针设置为 pred 的 下一个指针.
pred.next = newNode;
// 将下一个指针变成 新创建的指针, 再次进行循环,
pred = newNode;
}
// 此时 pred 已经是 Object 的最后一个元素生成的指针了.
// 如果 目标索引位置的 指针 为 空,(从链表尾部开始追加)
if (succ == null) {
// 将 pred 设置 为上一个 指针.
last = pred;
} else {
//从某个索引位置开始追加.
//将 Object 最后一个元素生成的指针的 下一个指针设置未 索引位置的指针.
pred.next = succ;
// 将索引位置指针的上一个指针 设置为 Object 最后一个元素生成的指针
succ.prev = pred;
}
// 链表的长度 增加 Object数组的长度.
size += numNew;
// 链操作数 +
modCount++;
// 函数返回结束
return true;
}
// 返回 index 位置的链表指针.
Node<E> node(int index) {
// assert isElementIndex(index);
// size >> 1 等同于0.5(size)
// 由于链表和数组的结构不同,无法直接通过索引获取值.
// 链表只能通过遍历,依次获取值.
// 这里 size 取半是为了 避免全链表遍历.
// 根据 size 取半的值可以判断出 index 距离 头部近还是 尾部近.
if (index < (size >> 1)) {
// 距离头部近,从头部开始遍历,到index的所在位置结束,然后 x位置在的值.
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 距离尾部近,从头部开始遍历,到index的所在位置结束,然后 x位置在的值.
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
// 校验函数
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
// 不合法 抛出异常.
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 索引值必须大于 等于 0 且 索引值小于等于 链表长度.
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
新增元素
add (E e) 新增元素
public boolean add(E e) {
// 默认add 函数会往链表末端追加元素.
linkLast(e);
return true;
}
linkLast(E e) 追加到链表尾部
void linkLast(E e) {
// 将当前链表末端指针复制给变量 l
final Node<E> l = last;
// 创建新的指针, 指针的存储 e ,指针的上一个指针设置为 l
final Node<E> newNode = new Node<>(l, e, null);
// 新的指针设置为 末端指针.
last = newNode;
// 原先的末端指针未空,
if (l == null)
// 将新的指针设置为链表头部指针.
first = newNode;
else
// 将末端指针(旧)的next节点引用设置未 新节点.
l.next = newNode;
// 集合的长度 + 1
size++;
// 链操作数 + 1
modCount++;
}
add (int index, E element) 给指定索引添加元素
public void add(int index, E element) {
// 校验 索引合法性.
// 索引值必须大于 等于 0 且 索引值小于等于 链表长度.
checkPositionIndex(index);
// 如果 索引值等于 链表的长度.
if (index == size)
// 追加到链表尾部(见上文)
linkLast(element);
else
// 追加到指定索引位置.
// node(index) ,返回index位置的链表指针.
linkBefore(element, node(index));
}
// 返回 index 位置的链表指针.
Node<E> node(int index) {
// assert isElementIndex(index);
// size >> 1 等同于0.5(size)
// 由于链表和数组的结构不同,无法直接通过索引获取值.
// 链表只能通过遍历,依次获取值.
// 这里 size 取半是为了 避免全链表遍历.
// 根据 size 取半的值可以判断出 index 距离 头部近还是 尾部近.
if (index < (size >> 1)) {
// 距离头部近,从头部开始遍历,到index的所在位置结束,然后 x位置在的值.
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 距离尾部近,从头部开始遍历,到index的所在位置结束,然后 x位置在的值.
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
// 校验函数
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
// 不合法 抛出异常.
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 索引值必须大于 等于 0 且 索引值小于等于 链表长度.
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
linkBefore(E e, Node succ)
E e 添加的元素.
Node<E> succ 索引位置的 指针,
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 获取索引位置指针的 上一个指针.
final Node<E> pred = succ.prev;
// 创建新指针, 新指针将 pred 设置为他的 上一个指针,将 succ 设置为他的下一个指针,
final Node<E> newNode = new Node<>(pred, e, succ);
// 将原先索引位置指针的 上一个指针设置为 新创建的指针.
succ.prev = newNode;
// 如果新节点的上一个指针是空的.
if (pred == null)
// 将新节点设置未 头部指针
first = newNode;
else
// 否则 将 pred 的 下一个指针设置为 新创建的指针
pred.next = newNode;
// 集合长度 + 1
size++;
// 链操作数 + 1
modCount++;
}
删除元素
E remove(int index) 异常指定索引的元素.
public E remove(int index) {
// 校验索引是否越界
checkElementIndex(index);
// node(index) 上文已经看过了. 返回索引位置的指针
return unlink(node(index));
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 这里的索引范围 : 从 0 开始到集合总长度, 但不包括size
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
E unlink(Node x) 将某个元素从链上剔除
E unlink(Node<E> x) {
// assert x != null;
// 取出 指针中的 元素
final E element = x.item;
// 获取 x 的下一个指针.
final Node<E> next = x.next;
// 获取 x 的上一个指针
final Node<E> prev = x.prev;
// 如果上一个指针 为 null
if (prev == null) {
// next 指针就是头部指针了
first = next;
} else {
// 否则 prev 的 next 指针 就由 x -> next
prev.next = next;
// x 指针的头部引用 断开.
x.prev = null;
}
// 如何 x 的next 节点是 null.
if (next == null) {
// 那么 x 的 prev 指针 就是 新的 尾部指针了.
last = prev;
} else {
// 否则 x的 next 指针的 prev 指针 就是 prev 指针
next.prev = prev;
// 将x 指针的尾部引用 断开
x.next = null;
}
// 将 x 指针位置的 元素设置为 null
x.item = null;
// 链表的长度 -1
size--;
// 链操作数 + 1
modCount++;
return element;
}
boolean remove(Object o) 从链表上移除某个元素.
public boolean remove(Object o) {
// 如果 移除的 元素 是 null.
if (o == null) {
// 遍历全链表 找到 null 然后移除.
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
// unlink 见上文
unlink(x);
return true;
}
}
} else {
// 移除元素不是null/
// 遍历全链表 找到 o 然后移除.
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
// unlink 见上文
unlink(x);
return true;
}
}
}
return false;
}
总结
1.LinkedList 底层是 使用 链表结构存储.
2.新增元素是,只需改变 指针 头尾部 的 指针引用.然后插入元素后,再重新更改指针引用即可.
2.1 :不同于ArrayList 由于链表和数组 结构的不同,不需要进行扩容校验,不需要进行,预先分配内存,也不需要连续的,顺序的内存空间.
3.根据索引值 删除元素时, 可以依据索引值的大小, 选择性的从头部 或者尾部 开始遍历链接,极大程度上避免了全链遍历.
4.直接根据 元素 删除元素时, 则需要全链遍历.然后删除.