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