1. 關於Java實現雙向鏈表的整體思路構架:
(1)定義接口ILink,在實現雙向鏈表時的接口主要用於定義行爲,即定義關於雙向鏈表操作的抽象方法。
(2)定義外部類LinkImpl實現接口ILink的所有抽象方法。
(3)定義一個LinkImpl類的內部類Node類,用於節點的表示。爲什麼要爲LinkImpl類的內部類?一是因爲定義Node類只是爲LinkImpl類服務,即爲了節點的表示及其創建;二是因爲Node類若作爲外部類的話,由於類中的屬性需要私有化,那麼Node類就需要定義共有的setter和getter方法供LinkImpl類使用,造成了唯一使用它的LinkImpl類的不方便,故將Node類作爲LinkImpl類的內部類。
(4)定義一個工廠類Factory類,用於LinkImpl類對象的實例化。Java實現雙向鏈表採用的是工廠設計模式,爲了能夠實現解耦操作。如若不用第三方類,那麼在客戶端new操作進行對象的實例化時,類發生改變後就會改變客戶端中的代碼,這就是最大的耦合問題,而工廠設計模式正好解決了此類問題。
2. 代碼的實現
2.1 ILink接口的定義
interface ILink
{
void add(Object obj);
boolean remove(int index);
boolean contains(Object obj);
int indexOf(Object obj);
boolean set(int index,Object obj);
Object get(int index);
int length();
void clear();
Object[] toArray();
void printLink();
}
2.2 Factory工廠類的定義
class Factory
{
private Factory() {}
public static ILink getLinkInstance()
{
return new LinkImpl();
}
}
2.3 LinkImpl類實現Link接口
class LinkImpl implements ILink
{
//雙向鏈表的頭節點
private Node first;
//雙向鏈表的尾節點
private Node last;
//雙向鏈表中的有效元素
private int size;
//LinkImpl類的內部類Node類
private class Node
{
//data保存節點的數據
private Object data;
//next保存下一個節點
private Node next;
//prev保存上一個節點
private Node prev;
//Node類的構造方法,用於創建節點
private Node(Node prev,Object obj,Node next)
{
this.data=obj;
this.prev=prev;
this.next=next;
}
}
@Override
//尾插法實現鏈表的添加
public void add(Object obj) {
//1.創建新節點
Node newNode=new Node(null,obj,null);
//2.當鏈表爲空時
if(this.first==null)
{
//3.鏈表的頭節點和尾節點就是新創建的節點newNode
this.first=this.last=newNode;
}
//4.當鏈表非空時
else
{
//先保存之前鏈表的尾節點
Node temp=this.last;
//修改之前鏈表尾節點的next值
temp.next=newNode;
//修改新節點的prev值
newNode.prev=temp;
//最後將新節點作爲尾節點
this.last=newNode;
}
//鏈表的有效元素個數++,即this.size++
this.size++;
}
@Override
//根據索引值進行鏈表節點的刪除
public boolean remove(int index) {
//1.判斷index是否合法,index的合法範圍是[0,size)
if(index<0||index>=this.size)
return false;
//刪除的節點爲頭節點時
if(index==0)
{
//並且刪除的是尾節點
if(index==this.size-1)
{
this.first=null;
this.last=null;
}
//刪除的不是最後一個節點
else
{
//先保存要刪除的節點,用於之後節點的維護
Node delete=this.first;
//維護新的頭節點
this.first=delete.next;
this.first.prev=null;
//維護要刪除的節點delete的prev和next值
delete.next=null;
delete.data=null;
}
}
//刪除的只是尾節點時
else if(index==this.size-1)
{
//先保存要刪除的節點
Node delete=this.last;
//維護新的尾節點
this.last=delete.prev;
this.last.next=null;
//維護要刪除的節點delete的prev和next
delete.prev=null;
delete.data=null;
}
//刪除的是中間的任一節點時
else
{
//先找到要刪除的節點
Node delete=this.first;
while(index!=0)
{
delete=delete.next;
index--;
}
//保存要刪除的節點的前一節點
Node cur=delete.prev;
//保存要刪除的節點的後一節點
Node next=delete.next;
//維護cur和next
cur.next=next;
next.prev=cur;
}
//刪除一個節點後,將鏈表的有效元素個數size--
this.size--;
return true;
}
@Override
//根據節點數據判斷該節點是否存在
public boolean contains(Object obj) {
Node cur=this.first;
//循環遍歷鏈表
for(;cur!=null;cur=cur.next)
{
if(cur.data==obj)
return true;
}
return false;
}
@Override
//根據節點數據找到該節點的索引值
public int indexOf(Object obj) {
//空鏈表時,直接返回
if(this.first==null)
return -1;
//非空鏈表時
Node cur=this.first;
int index=0;
while(cur!=null)
{
if(cur.data==obj)
return index;
index++;
cur=cur.next;
}
return -1;
}
@Override
//根據鏈表的索引值,修改其對應的節點數據
public boolean set(int index, Object obj) {
//index非法時
if(index<0||index>=this.size)
return false;
//index合法時
Node cur=this.first;
while(index!=0)
{
cur=cur.next;
index--;
}
//找到該節點後,修改該節點數據
cur.data=obj;
return true;
}
@Override
//根據鏈表的索引值返回該索引值對應節點數據
public Object get(int index) {
//index非法時
if(index<0||index>=this.size)
return null;
Node cur=null;
//當index在鏈表的前半部分時,從前向後查找
if(index<this.size/2)
{
cur=this.first;
while(index!=0)
{
cur=cur.next;
index--;
}
}
//當index在鏈表的後半部分時,從後向前查找
else
{
cur=this.last;
int loop=this.size-index-1;
while(loop!=0)
{
cur=cur.prev;
loop--;
}
}
return cur.data;
}
@Override
//計算鏈表的長度,即爲鏈表中元素的有效個數size
public int length() {
if(this.first==null)
return 0;
return this.size;
}
@Override
//銷燬鏈表
public void clear() {
Node cur=this.first;
for(;cur!=null;)
{
Node next=cur.next;
cur.prev=null;
cur.next=null;
cur.data=null;
cur=next;
}
//刪除完後,設置first、last的值置爲null,size置爲0
this.first=null;
this.last=null;
this.size=0;
}
@Override
//將鏈表中的節點數據保存在數組中
public Object[] toArray() {
Node cur=this.first;
//空鏈表
if(cur==null)
return null;
//非空鏈表
int i=0;
Object[] obj=new Object[this.size];
for(;cur!=null;cur=cur.next)
{
obj[i]=cur.data;
i++;
}
return obj;
}
@Override
//打印鏈表的節點數據
public void printLink() {
Node cur=this.first;
for(;cur!=null;cur=cur.next)
{
System.out.println(cur.data);
}
}
}
2.4 主類Test及其主方法,測試以上方法正確與否
public class Test {
public static void main(String[] args) {
//一個簡單測試
ILink link=Factory.getLinkInstance();
link.add("火車頭");
link.add("車廂1");
link.add("車廂2");
link.add("車廂3");
link.add("車廂4");
link.add("車廂5");
link.set(5,"火車尾");
link.remove(3);
System.out.println(link.contains("火車頭"));
Object[] obj=link.toArray();
for(int i=0;i<link.length();i++)
{
System.out.println(obj[i]);
}
System.out.println(link.length());
}
}