LinkedList源码分析
1.总体概览
LinkedList的继承体系:
2.属性分析
LinkedList总共有三个属性:
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
- size:即是LinkedList的大小
- Node first:即是LinkedList的第一个节点
- Node last:即是LinkedList的最后一个节点
这里看一下Node这个类。熟悉C或者C++的人对Node这个名词一定不陌生,和C/C++语言中的Node一样,LinkedList中的Node中,也包括两个Node,一个指向前一个Node,一个指向下一个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;
}
}
3.方法分析
- 构造一个空的LinkedList
public LinkedList() {
}
- 构造一个包含指定集合的LinkedList,并且是按照集合的迭代器顺序的
主要调用public boolean addAll(int index, Collection<? extends E> c){}方法在指定的位置index开始添加元素
- 首先对下标index进行检查,如果指定的位置不正确,抛出异常
- 将指定集合转为数组,对数组的长队进行判断,如果为零则返回false,表示添加失败
- 定义一个前节点pred,一个后节点succ
- 如果下标index等于LinkedList的长度,代表是从链表末尾开始添加,后节点succ为空,前节点pred是链表最后一个节点;否则后节点succ是一个新的节点,前节点pred是后节点succ的前一个节点
- 遍历数组,将数组中的每个元素构造成一个新节点newNode,newNode的前一个节点是pred,后一个节点是null。如果pred是空,则新节点newNode是链表第一个节点first,否则将新节点newNode添加到pred后面。每次循环结束,新节点newNode称为前节点pred,进行下一个的节点添加
- 循环结束后,如果后节点succ为空,则前节点pred是尾节点last;否则将后节点succ添加到前节点pred末尾
- 将数组长度增加到链表长度
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[] 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;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
3.移除第一个节点
首先进行检查,如果第一个节点已经是空,则抛出异常。然后调用private E unlinkFirst(Node f){}方法移除第一个节点
- 获取第一个节点first的值item和下一个节点next的引用
- 将第一个节点first的值和下一个节点的应用置为空,进行垃圾回收
- 将下一个节点next设置为链表的第一个节点
- 如果节点next为空,代表这是链表最后一个节点,将链表尾节点last置为空;否则将新的首节点的前节点引用置为空,因为这是链表第一个节点,前面没有节点
- 将链表长度减一,返回删除节点的值item
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
- 移除最后一个节点
首先获取最后一个节点last进行判断,如果该节点已经为空则抛出异常,否则调用private E unlinkLast(Node l){}移除最后一个节点,思想和移除第一个节点大致相同
- 获取最后一个节点last的值item和前一个节点pred的引用
- 将最后一个节点last的值和前一个节点pred置为空,进行垃圾回收
- 将前一个节点prev设置为链表的最后一个节点
- 如果节点prev为空,代表这是链表最后一个节点,将链表首节点pred置为空;否则将新的尾节点的后节点引用置为空,因为这是链表最后一个节点,后面没有节点
- 将链表长度减一,返回删除节点的值item
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
- 在链表首增加一个节点
调用private void linkFirst(E e){}方法在链表首增加一个节点
- 取出链表本来的首节点first,赋值给f,构造一个包含指定元素的新节点newNode,新节点newNode的前节点引用为空,后节点的引用为f
- 将新节点newNode赋值给first
- 如果首节点f为空,代表原来链表为空,将新节点newNode同事赋值给尾节点last;否则将首节点f的前节点引用指向新节点newNode,将链表链在新节点newNode的后面,此时新节点newNode就成了首节点
- 将链表长度加一
public void addFirst(E e) {
linkFirst(e);
}
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++;
}
- 在链表尾增加一个节点
调用void linkLast(E e){}方法在链表尾增加一个节点,思路和在链表首增加一个节点大致相同
- 取出链表本来的尾节点last,赋值给l,构造一个包含指定元素的新节点newNode,新节点newNode的前节点引用为l,后节点的引用为空
- 将新节点newNode赋值给last
- 如果尾节点l为空,代表原来链表为空,将新节点newNode同事赋值给首节点first;否则将尾节点l的后节点引用指向新节点newNode,将链表链在新节点newNode的前面,此时新节点newNode就成了尾节点
- 将链表长度减一
public void addLast(E e) {
linkLast(e);
}
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++;
}
- 清空链表
遍历整个链表,去除此次遍历的节点x,将x的下一个节点复制给next,将x的前节点引用,值,后节点引用都置为null,进行垃圾回收,然后再讲next赋值给x,进行下一次的循环。最后将首尾节点都置为空,将链表长度置为0。
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
- 判断链表是否包含某个元素
判断链表是否包含某个元素,也就是计算这个元素在链表中的下标,如果返回为-1,则代表链表中不含这个元素
- 如果元素为空,遍历链表中的空值
- 如果元素不为空,遍历链表中符合条件的值,找不到返回-1
- 如果返回-1,代表链表中不包含这个元素,返回false
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
- 返回链表长度
返回链表长度,即size的值
public int size() {
return size;
}
- 增加一个元素
在链表尾用void linkLast(E e){}方法增加一个节点
public boolean add(E e) {
linkLast(e);
return true;
}
- 移除一个包含指定元素的节点
首先对改元素进行空值判断,遍历到改节点的位置,调用E unlink(Node x){}方法从链表中剔除该节点
- 依次取出改节点的值item、前节点的引用prev、后节点的引用next
- 如果前节点prev引用为空,那这就是首节点first,将后节点next设置为新的首节点first;否则将后节点next的引用赋值给前节点prev的后节点引用,即将后面的节点直接链接在前面节点后面,忽略这个节点,将prev置为空
- 如果后节点next引用为空,那这就是尾节点last,将前节点的引用赋值给尾节点last;否则将前节点prev的引用赋值给后面一个节点next的前节点引用,将next置为空,忽略这个节点
- 最后将值item设置为空,将链表长度size减一
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
- 返回指定下标index的节点值
先进行下标检查,然后调用Node node(int index){}获取节点的值
- 这里看出取值时不是从头开始遍历的,而是对链表长度一分为二,分两段遍历。前半段顺序遍历,后半段逆序遍历。因为每个节点都有前节点prev的引用,所以可以逆序遍历
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
- 替换指定下标index的值
首先对下标index进行检查,然后调用Node node(int index){}取到下标index对应的节点,将节点的值item替换为element,返回节点之前保存的值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
- 在指定下标index处增加一个值之元素element的节点
如果指定下标刚好等于链表的长度,则调用void linkLast(E e){}方法在链表尾增加一个节点;否则调用void linkBefore(E e, Node succ){}方法在指定下标的节点前增加一个节点
- 先取出指定下标的节点succ的前节点pred,构造一个前节点为pred,值为element,后节点为succ的新节点newNode
- 将指定下标的节点succ的前节点引用设置为newNode,代表将后面所有的节点都链接在新的节点newNode的后面
- 如果前节点pred为空,代表增加的位置在链表首,将新节点newNode设置为首节点first;否则将新节点newNode赋值给指定下标的节点succ的前节点pred的后节点引用,将以新节点为首的链表链接在后面
- 将链表长度size加一
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
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 unlink(Node x){}方法从链表中剔除该节点(此方法已经前面描述过)
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
- 找出链表中最后一个值为Object的下标
逆序遍历链表,找出符合条件的下标,同样分空值和非空值进行判断
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
- 取出链表首节点的值,但是并不删除
取出第一个节点的值,如果是空则返回空,否则返回节点的值
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
- 取出链表第一个元素的值,但是并不删除
public E element() {
return getFirst();
}
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
- 弹出首节点
这个方法使得链表像队列一样,弹出并删除第一个节点的值
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
- 删除链表的一个节点
调用public E removeFirst(){}方法,实际从链表首开始删除节点
public E remove() {
return removeFirst();
}
- 增加一个节点
实际调用的public boolean add(E e){}方法在链表末尾增加一个节点
public boolean offer(E e) {
return add(e);
}
- 在链表首增加一个节点
实际调用的是public void addFirst(E e){}方法
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
- 在链表尾增加一个节点
实际调用的是public void addLast(E e){}方法
public boolean offerLast(E e) {
addLast(e);
return true;
}
- 取出首节点的值
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
- 取出尾节点的值
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
- 弹出首节点的值
取出首节点的值并删除
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
- 弹出尾节点的值
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
- 在链表首压入一个节点
实际调用的是public void addFirst(E e){}方法
public void push(E e) {
addFirst(e);
}
- 弹出链表首的节点
实际调用的是public E removeFirst(){}方法
public E pop() {
return removeFirst();
}
- 删除第一个出现的包含某个值的节点
实际调用的是public boolean remove(Object o){}方法
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
- 删除最后一次出现包含某个值的节点
逆序遍历链表,找出符合条件的下标,同样分空值和非空值进行判断,找到则调用E unlink(Node x){}方法删除
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
- 链表转数组
这是一个比较简单的方法,构造以一个数组,一次将链表中节点的值加入数组
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}