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;
}