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