LinkedList就是双链表的数据结构,所以主要复习下双链表的插入、删除和查找
1、结点类
结点类是LinkedList类的静态内部类,包括指向上一个结点的prev引用、指向下一个结点的next引用和数据域item。构造函数也是初始化这三个参数。
private static class Node<E>{
E item;
Node<E> prev;
Node<E> next;
Node(Node<E> prev,E element,Node<E> next){
this.item = element;
this.prev = prev;
this.next = next;
}
}
2、LinkedList中的某些域
这里只写了表示LinkedList大小的size、指向最后一个结点的last引用和指向第一个结点的first引用
private int size = 0; //链表元素个数
private Node last; //指向最后一个结点
private Node first; //指向首元结点
3、LinkedList的插入结点操作
(1)在链表头插入一个元素,在链表尾插入元素与之相似
注意考虑链表本来就没有元素的情况
/**
* 在链表头插入一个元素e
* @param e
*/
private void linkFirst(E e) {
Node<E> f = first; //初始化f,使其指向首元结点,如果链表为空,则f==first==null
Node<E> newNode = new Node<>(null,e,f);
//新建一个newNode结点,newNode结点的next引用指向原来的首元结点,此时该结点成为新的首元结点
first = newNode; //first引用指向新的首元结点
if(f == null)
last = newNode;//如果f==null则表示newNode是第一个创建的结点,此时最后一个元素也就是
//第一个元素,last指向最后一个元素
else
f.prev = newNode; //如果f!=null则表示链表之前就有元素,并且f指向的是旧的首元结点,
//使旧首元结点的prev引用指向新首元结点
size++; //插入了元素,链表长度变化
// modCount++;
}
(2)在链表尾插入元素
/**
* 在链表尾插入一个元素,操作与linkFirst类似
* @param 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++;
}
(3)在某个非空结点前插入一个元素
/**
* 在结点succ(确保非空)前插入元素e
* @param e
* @param succ
*/
void linkBefore(E e,Node<E> succ) {
//succ为空会抛出空指针异常,使用succ结点的prev引用初始化pred,此时pred指向succ的前一个结点
final Node<E> pred = succ.prev;
//创建一个新节点,并且使其prev指向succ的前一个结点,next指向succ
final Node<E> newNode = new Node<>(pred,e,succ);
succ.prev = newNode; //将succ的prev指向新的结点,此时新结点成为succ的前一个结点
if(pred == null) //pred为空即succ为首元结点,则first指向新结点
first = newNode;
else
pred.next = newNode;
size++;
// modCount++;
}
4、LinkedList结点删除操作
(1)删除第一个结点(确保非空),并返回它的值
/**
* 删除第一个结点(非空),并返回其数据值,传给该函数的参数一般为first
* 当链表只有一个结点时,first和last指向的是同一个结点
* @param f
* @return
*/
private E unlinkFirst(Node<E> f) {
final E element= f.item; //element为返回值
final Node<E> next = f.next; //使next指向f的下一个结点
first = next; //第一个元素删除后要使first指向之前的第二个元素
f.item = null; //这里相当于将item和next引用设为空,有助垃圾回收机制回收内存
f.next = null;
if(next == null) //如果next为空则表示元链表只有一个元素
last = null; //last原本指向的是要删除的那一个元素,所删除后要将last指向空
else
next.prev = null;
size--;
// modCount++;
return element;
}
(2)删除最后一个结点(确保非空)并返回其值
/**
* 删除最后一个结点l(确保非空),并返回结点的数据值
* @param l
* @return
*/
private E unlinkLast(Node<E> l) {
final E element = l.item; //获取结点l的数据值
final Node<E> prev = l.prev;//l的前一个结点引用
l.item = null;
l.prev = null; //帮助垃圾回收
last = prev; //将旧尾结点的引用指向前一个结点
if(prev == null) //如果最后一个结点 l 也是首元结点,即只有一个结点
first = null; //则将首元结点引用指向空(此时链表中已经没有结点了)
else
prev.next = null; //如果l不是唯一的结点,则将新尾节的next设为空
size--;
return element;
}
(3)删除指定的结点并返回其值
/**
* 删除某一个结点x(确保非空),并返回结点数据值
* @param x
* @return
*/
E unlink(Node<E> x) {
final E element = x.item; //取得结点x的数据值
final Node<E> prev = x.prev;
final Node<E> next = x.next;
//如果x为首元结点,则将first引用指向下一个结点(x删除后x的下一个结点为首元结点)
if(x.prev == null) {
first = next;
}else { //否则将x前一个结点的next指向x的下一个结点
prev.next = next;
x.prev = null; //帮助垃圾回收
}
if(next == null) { //如果x为尾结点,则将last指向x的前一个结点
last = prev;
}else { //否则将x的下一个结点的prev引用指向x的前一个结点
next.prev = prev;
x.next = null; //设为空,帮助垃圾回收
}
x.item = null; //设为空,帮助垃圾回收
size--;
// modCount++;
return element;
}
上面的这几个方法并不是完全对外暴露的接口,但是常用的那些方法实际上都是调用的这些方法,如下
/**
* 返回链表第一个元素值,链表中无元素则抛出异常
* @return
*/
public E getFirst() {
final Node<E> f = first;
if(f == null){
throw new NoSuchElementException();
}
return f.item;
}
/**
* 返回链表最后一个元素值,链表中无元素则抛出异常
* @return
*/
public E getLast() {
final Node<E> l =last;
if(l == null) {
throw new NoSuchElementException();
}
return l.item;
}
/**
* 删除第一元素并返回数据值
* @return
*/
public E removeFirst() {
final Node<E> f = first;
if(f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* 删除最后一个元素并返回值
* @return
*/
public E removeLast() {
final Node<E> l = last;
if(l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 删除指定元素,顺在链表查找,找到了就删除
*/
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;
}