Java实现双向链表的相关操作

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


猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/79996901
今日推荐