LinkedList简介
- LinkedList类是继承于AbstractSequentialList的双向链表。
- LinkedList类实现了List接口,定义对集合的基本(包括与位置有关的操作)。
- LinkedList类实现了Deque(Double End Queue)接口,可将LinkedList用作双端队列使用。
- LinkedList实现了Cloneable接口,即覆盖了函数clone(),可进行克隆操作。
- LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
- LinkedList是非同步的。若在多线程的环境下使用,需要在外部进行同步操作。
- LinkedList类定义如下,
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList内部Node类
Node类定义如下,
private static class Node<E> {
E item; //存储值
Node<E> next;//指向下一个结点
Node<E> prev;//指向上一个结点
//构造函数,链接该结点的next和prev指针
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
LinkedList类的域
域 | 说明 |
---|---|
int size | size表示链表的长度 |
Node first | first指向链表中的第一个结点 |
Node last | last指向链表中的最后一个结点 |
LinkedList的构造函数
//构造一个空链表
public LinkedList() {
}
//将集合c中的元素都添加到新建的链表中
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList中的方法
该部分主要介绍LinkedList中新添加的方法、实现的List方法和实现的Deque中的方法三部分。
LinkedList类中新添加的方法
//把值e链接到头部结点
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);//第一个参数是新建结点的前驱结点,第三个参数是新建结点的后继结点
first = newNode;//第一个结点已经变为newNode,需重新设置first的值
if (f == null)//如果先前的f结点为空,则证明该链表中没有任何结点,则只需设置last
last = newNode;
else//f!=null,则需要设置f的prev指正指向新的first
f.prev = newNode;
size++;//node结点个数加1
modCount++;//链表的结构改变,即多了一个结点,modCount加1
}
//把值为e的结点链接到链表的最后
void linkLast(E e) {
final Node<E> l = last;//记录未改变之前的最后一个结点
final Node<E> newNode = new Node<>(l, e, null);//新建值为e的结点,并设置前驱结点和后继结点
last = newNode;//最后一个结点已经改变,需设置新值
if (l == null)//l的值为null,则证明先前的链表为空,则只需设置first指针
first = newNode;
else //l!=null,链表不为空,则需要设置l的next指针
l.next = newNode;
size++;
modCount++;
}
//在结点succ(succ!=null)之前插入值为e的结点
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;//记录succ的前驱结点
final Node<E> newNode = new Node<>(pred, e, succ);//建立值为e的结点并设置新结点的前驱结点和后继结点
succ.prev = newNode;//设置succ(succ!=null)的pre的值
if (pred == null)//若pred为空,则证明succ为first,则设置first指向newNode
first = newNode;
else//若pred!=null,则需要设置pred的next指正
pred.next = newNode;
size++;
modCount++;
}
//删除第一个结点,
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,让GC回收内存
first = next;//设置新的first
if (next == null)//若链表只有一个结点
last = null;
else//新first结点的prev置为null
next.prev = null;
size--;
modCount++;
return element;
}
//删除最后一个结点(和删除第一个结点类似)
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;//记录l的前驱结点
l.item = null;
l.prev = null; // help GC,让GC回收l和l.item所指向的对象所占的内存
last = prev;//设置新的last
if (prev == null)//若链表中只有一个结点
first = null;
else//prev!=null,则将prev.next置空,使最后一个结点变为非活动态。
prev.next = null;
size--;
modCount++;
return element;
}
//删除任意一个结点x
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;//记录x的后继结点
final Node<E> prev = x.prev;//记录x的前驱结点
if (prev == null) {//前驱结点为空,则证明x为first
first = next;//则设置fisrt为next(x结点需要删除)
} else {//prev!=null
prev.next = next;//设置prev.next指向next
x.prev = null;//x的prev指向null
}
if (next == null) {//若x的后继结点为空,则证明x为最后一个结点
last = prev;//则设置last为prev
} else {//next!=null
next.prev = prev;//设置next.prev指向prev
x.next = null;//x的next指向null
}
x.item = null;
size--;
modCount++;//链表结构改变,将modCount加1
return element;//返回删除元素
}
//返回链表中的第一个元素。若链表为空则抛出异常
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//返回链表中最后一个元素,为空则抛出异常
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
//删除并返回链表中的第一个元素
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//删除并返回链表中的最后一个元素
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//添加置为e的元素到链表首部
public void addFirst(E e) {
linkFirst(e);
}
//添加置为e的元素到链表尾部
public void addLast(E e) {
linkLast(e);
}
//返回链表中值为o的结点在链表中第一次出现时的下标。从前往后遍历
public int indexOf(Object o) {
int index = 0;
if (o == null) {//o为空
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {//o不为空
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;//如果不存在,则返回-1
}
//返回链表中值为o的结点在链表中最后一次出现时的下标。从后往前遍历
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;
}
//返回指定位置的结点。若指定的位置比(size/2)小,则从前往后遍历。否则,从后往前扫描。
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;
}
}
LinkedList类中实现List的方法
- List接口中继承了Collection接口中的方法,并新定义了一些有关位置的方法。
这些方法的实现绝大部分都依赖于 LinkedList类中新添加的方法 中介绍的方法,例如,
public void add(int index, E element) {//在指定位置添加元素 checkPositionIndex(index);//检查插入位置 if (index == size)//表示在链表的最后插入元素 linkLast(element); else//在node(index)之前插入元素 linkBefore(element, node(index)); }
- 该部分主要用到两个方法来检查下标,
//检查是否为链表中元素所在的下标,主要用于元素的存取操作
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
//检查是否为元素的插入位置,主要用于元素的添加操作
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
LinkedList类中实现的Deque中的方法
在这类方法主要设计栈和
方法 | 说明 |
---|---|
public E peek() | 返回链表中的第一个元素。链表为空,返回null |
public E peekFirst() | 返回链表中的第一个元素。链表为空,返回null |
public E peekLast() | 返回链表中的最后一个元素。链表为空,返回null |
public E element() | 返回链表中的第一个元素。链表为空,返回异常 |
public E poll() | 删除并返回链表中的第一个元素。为空返回null |
public E pollFirst() | 删除并返回链表中的第一个元素。为空返回null |
public E pollLast() | 删除并返回链表中的最后一个元素。为空返回null |
public E remove() | 删除并返回链表中的第一个元素。为空返回异常 |
public boolean offer(E e) | 在链表的最后添加值为e的结点 |
public boolean offerFirst(E e) | 在链表的首部添加值为e的结点 |
public boolean offerLast(E e) | 在链表的最后添加值为e的结点 |
public void push(E e) | 该方法主要用于模拟压栈的操作 |
public E pop() | 该方法主要用于模拟出栈的操作 |
LinkedList类中的Iterator
- ListIterator接口继承自Iterator接口。
- ListIterator可以向前、向后访问结点,而Iterator只能向后访问结点。
- ListIterator的域,
private Node<E> lastReturned;//记录上一次访问过的结点
private Node<E> next;//记录下一个将要访问的结点
private int nextIndex;//记录结点元素的下标
private int expectedModCount = modCount;//链表的修改次数
- ListIterator的构造函数,
//构造从指定位置开始的ListIterator。可以发现lastReturned变量并没有被设置值
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
- ListIterator中的方法如下,
public boolean hasNext() {//是否还有元素
return nextIndex < size;
}
//先将next结点保存在lastReturned中,然后next向后移动一个结点,并将lastReturned返回
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;//保存当前结点
next = next.next;//next往后移动一个结点
nextIndex++;//nextIndex加1
return lastReturned.item;//返回先前的next结点中的值
}
//前面是否还有结点
public boolean hasPrevious() {
return nextIndex > 0;
}
//先进行next的前向移动,后进行lastReturned赋值,再将lastReturned返回
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
//删除lastReturned结点
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)//当执行previous()函数时,会满足这一条件
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
//设置刚刚访问的lastReturned的值
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
//在next结点之前添加一个结点
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)//next为null,则在最后添加该结点
linkLast(e);
else//在next之前添加结点
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
疑问
void linkLast(E e)
方法为什么不是私有的?public E getFirst()
方法中为什么f变量要设为final。
本文作者能力,知识有限,若有错误,欢迎留言指正!