LinkedList
继承AbstractSequentialList
实现List,Dueue,Cloneable,Serializable
由链表实现
三个变量
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
size:节点个数
first:头节点
last:尾节点
两个构造函数
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
1.创建一个空的list
2.创建一个包含集合c所有元素的list集合
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;
}
}
可以看出这LinkedList构建的链表是一个双向链表
方法
getFirst
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
获取第一个节点的元素值,如果为空,则抛出异常,元素依旧存在于集合中
getLast
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
获取最后一个节点的元素值,如果为空抛出异常,元素依旧存在于集合中
removeFrist
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
移除第一个节点且返回,通过unlikeFirst方法,为空抛出异常
private E unlinkFirst(Node<E> f) {
final E element = f.item;
// 获取下一个节点的node对象
final Node<E> next = f.next;
// 将当前头节点存储的元素值和指向下一个节点的地址置为null
f.item = null;
f.next = null; // GC回收
// 头节点变为了原头结点的下一个节点
first = next;
// 如果下一个节点为空,表示原先的头结点也是尾节点,last置为null
if (next == null)
last = null;
else
// 下一个节点不为空,那么指向头一个节点的指针置为null
next.prev = null;
// 长度减1
size--;
modCount++;
return element;
}
removeLast
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
移除最后一个节点并且返回节点元素,为空则抛出异常
移除调用了unlinkLast方法
private E unlinkLast(Node<E> l) {
// 首先获取到最后一个节点的元素值
final E element = l.item;
// 获取尾节点的前一个node节点
final Node<E> prev = l.prev;
// 置为null。方便GC回收
l.item = null;
l.prev = null;
// 设置原尾节点的前一个节点为尾节点
last = prev;
// 如果为空,那么就是说尾节点是链表唯一的元素也是头结点,所以头结点也要置为null
if (prev == null)
first = null;
else
// 否则现尾节点的下一个指针的指向置为null
prev.next = null;
size--;
modCount++;
return element;
}
addFirst
public void addFirst(E e) {
linkFirst(e);
}
头插法,将元素e构建的node节点作为头节点
调用了linkFirst方法实现
private void linkFirst(E e) {
// 首先获取当前的头结点
final Node<E> f = first;
// 构建node节点
final Node<E> newNode = new Node<>(null, e, f);
// 指定newNode为头节点
first = newNode;
// 如果f == null在没有添加之前,当前链表是空的,这个新添加的节点即是头节点也是尾节点
if (f == null)
last = newNode;
else
// 否则原先的头节点作为第二个节点
f.prev = newNode;
size++;
modCount++;
}
addLast
public void addLast(E e) {
linkLast(e);
}
将元素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++;
}
indexOf
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;
}
获取元素o在链表的位置
通过遍历实现,和ArrayList基本一致
contains
public boolean contains(Object o) {
return indexOf(o) != -1;
}
判断元素o是否存在于集合中,调用了indexOf方法来进行判断实现
add
public boolean add(E e) {
linkLast(e);
return true;
}
将元素e添加到链表中,从这里我们可以发现,他是使用了尾插法实现的,也就是说add方法添加元素是添加到链表末尾
remove
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;
}
remove 移除链表的指定元素,首先我们根据o是否为null来查找元素,找到了移除,没有找到就返回false。
移除使用了unlink方法
E unlink(Node<E> x) {
// 首先x节点的元素
final E element = x.item;
// 获取x节点的前一个元素和后一个元素
final Node<E> next = x.next;
final Node<E> prev = x.prev;
// 当前一个节点为null时,表示移除的节点为头节点,将frist往后移一个节点
if (prev == null) {
first = next;
} else {
// 否则将前一个节点的下一个节点指针指针后一个节点,将x指向前一个节点的指针指向后一个节点
prev.next = next;
x.prev = null;
}
// 当next为空,表示移除的节点为尾节点
if (next == null) {
// 将尾节点的指针指向头一个节点
last = prev;
} else {
// 否则他就是个中间节点,将next指向前一个节点的指针指向x前一个节点
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
addAll
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;
}
在不指定index情况下,默认将c集合的所有元素添加到末尾,所以主要看的是第二个addAll方法,
首先他会判断这个index是否合理
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
然后获取集合c的数组对象,和数组长度,判断这个集合有没有元素,
然后根据index判断从哪个地方插入,如果index== size表示插入到末尾,负责则获取index位置的node节点succ,和获取他的前一个node节点pred
接下来for循环就是插入操作,就是链表的插入操作,
最后的if判断 succ是否为null,为null的话,这种情况就是从尾节点进行插入的,这个时候我们要指定last,否则就是经典的从中间插入,就不多复述了,就是数据结构链表的操作,
clear
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
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++;
}
就是链表的清空操作
get
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
获取指定index位置的元素,首先判断index是否合理,然后通过node变量获取index位置的节点然后获取它的item元素。
set
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
set操作也是需要变量获取指定index位置的节点,然后重新指定item元素,然后原先的元素值。
add
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
在指定位置index插入元素element,两种情况一种是尾节点插入,一种是在某一个节点后插入
linkLast之前出现过就不说了
linkBefore看看,通过node方法,我们可以获取index位置的succ节点
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节点前。
remove
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
public E remove() {
return removeFirst();
}
第一个remove 操作移除指定index位置的元素,使用了unlink方法,
第二个套了个壳子,实则调用了removeFirst方法
lastIndexOf
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;
}
与indexOf不同的是,前者是正序,这个是逆序查找
peek
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
获取头节点的元素,不移除链表
element
public E element() {
return getFirst();
}
获取头节点元素,不移除链表
poll
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
获取头节点元素,且从链表中移除
总结
还有很多方法没有列出来说,但是剩下的很多方法都是套个壳子,都是已经实现了,因为这个LinkedList他不仅仅是一个链表操作,主要是他实现了Dueue,还是一个队列的实现,所以很多方法都重复调用了