LinkedList
LinkedList 是一个双向链表. 因此他的增删都很方便, 查询操作就比较麻烦.
主要用途 : 队列, 堆栈.
1. LinkedList使用链表结构
在LinkedList内部维护这头部和尾部节点的引用
// 开始节点引用
transient Node<E> first;
// 尾部节点引用
transient Node<E> first;
元素节点的包装类 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;
}
}
通过上面的源码可以看出, Node内部维护这上一个元素的引用和下一个元素的引用, 因此实现了双向链表的结构.
2. 构造方法
// 无参
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList构造方法也很简单, 我们主要看下 addAll()方法
, 源码如下
public boolean addAll(Collection<? extends E> c) {
// 调用双参方法, 第一参数是添加的起始位置.
return addAll(size, c);
}
再看 addAll(startIndex, collection)
fangfa
public boolean addAll(int index, Collection<? extends E> c) {
// 检查index 是否合法
checkPositionIndex(index);
// 将集合转换成数组.
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
// pred 上一个元素,新元素添加到这个元素后面
// succ 如果index 位置上的元素.
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;
}
// 如果succ 是一个元素则将他添加到新元素的后面.形成一个整链表.
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
上面的代码也十分好理解. 首先上来就是检查了index
的合法性(index > 0 && index <= size
)
然后将集合转换成了数组这样便于遍历. 接着定义了两个引用, 便于进行插入操作.最后将index之后的元素添加到链表中.
3. add
添加新元素
默认添加到最后
// 不指定插入位置.则默认添加到最后.
public boolean add(E e) {
linkLast(e);
return 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++;
}
上面的代码也非常清晰, 就是在链表最后添加新元素.
在指定位置插入元素
// 在指定位置添加元素
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
上面的代码首先判断了是否是最后位置, 如果是则直接走linkLast
流程, 否则走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++;
}
代码很清晰, 就是将新元素的prev
指向上一个元素, 将next
执行index位置的元素.其实就是简单的链表操作.
4. remove
删除操作
无参删除则直接从头删除
public E remove() {
return removeFirst();
}
来看下 removeFirst
方法
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
接着是核心方法 unlinkFirst
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;
}
删除头部的方法也非常清晰, 首先是将First节点的元素内存释放掉, 然后将第二个元素设置为first元素.
删除指定元素index
来看下方法吧.
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
代码中直接调用了 unlink
核心方法
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;
// 设置first,
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
// 设置 last
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
注意 : first 的prev = null; last 的next = null;
5. 队列功能
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列是一种先进先出的结构, LinkedList的add和remove也实现了这种效果
/**
* 测试队列功能
*/
private static void testQueue() {
// 队列 先进先出
// 入队 offer
// 出队 poll
LinkedList<String> linkedList = new LinkedList<>();
// 入队操作
linkedList.offer("111");
linkedList.offer("222");
linkedList.offer("333");
linkedList.offer("444");
// 出队操作
String headerEle = linkedList.poll();
headerEle = linkedList.poll();
}
6. 栈功能
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
/**
* 测试栈功能
*/
private static void testStack() {
// 栈先进后出
LinkedList<String> linkedList = new LinkedList<>();
// 入栈
linkedList.push("111");
linkedList.push("222");
linkedList.push("333");
// 出栈
String ele = linkedList.poll();
System.out.println(linkedList);
}
关于LinkedList就写这么多吧, 如果理解链表结构, 理解代码就很轻松了.