java单链表
一、直接对Node进行操作
class Node<E>{
private E data;
private Node<E> next;
public Node (E data){
this.data = data;
}
public E getData(){
return this.data;
}
public void setNext(Node<E> next){
this.next = next;
}
public Node<E> getNext(){
return this.next;
}
}
public class javaDemo {
public static void main(String args[]) {
Node<String> n1 = new Node<String>("Node 1");
Node<String> n2 = new Node<String>("Node 2");
Node<String> n3 = new Node<String>("Node 3");
Node<String> n4 = new Node<String>("Node 4");
Node<String> n5 = new Node<String>("Node 5");
n1.setNext(n2);
n2.setNext(n3);
n3.setNext(n4);
n4.setNext(n5);
printNode(n1);
}
public static void printNode(Node<?> node){
if(node != null){
System.out.println(node.getData() + " ");
printNode(node.getNext());
}
}
}
可以看出让用户直接对Node类进行操作是十分复杂的,而且不利于链表内容的管理。我们想得到的只是“链表”这个类和对链表的操作方法,对于节点的增删查改都可以作为方法封装起来。下面尝试实现。
二、利用内部类实现
首先我们需要定义一个接口ILink,在这个接口中,我们定义有链表需要具备的一些操作方法,同时接口设置泛型,便于管理各种类型的数据。
interface ILink<E>{
public void add(E e);//向链表中追加数据
public int size();//获取链表数据个数
public boolean isEmpty();//判断链表是否为空
public Object[] toArray();//将元素以数组的形式返回
public E get(int index);//通过索引获得数据
public void set(int index,E data);//修改指定索引的数据
public boolean contains(E data);//查询指定元素是否存在
public void remove(E data);//删除数据 传递的是数据内容
public void clean();//清空链表
}
接下来我们实现这个接口。
class LinkImpl<E> implements ILink<E>{
private class Node<E>{
//定义一个Node内部类
private E data;//Node类中属性,用于存放该节点的数据
private Node<E> next;//Node类中属性,下一个节点
public Node(E data){
this.data = data;
}//Node类构造方法
public void addNode(Node<E> newNode){
//添加新的Node节点,注意这只是Node类中的一个方法,并不能把数据添加到链表中
if(this.next == null){
//如果this已经指向了链表尾部,那么直接将其添加到this.next;
this.next = newNode;
}else{//如果this还没有指向链表尾部,那么要向后寻找,递归调用该方法,直到其指向链表尾部
this.next.addNode(newNode);
}
}
public void toArrayNode(){
//递归调用该方法,并且将returnData数组逐一填充
LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data;
if(this.next != null){
this.next.toArrayNode();
}
}
public E getNode(int index){
//判断index是否与foot相等,如果不等,就判断下一个节点
if(LinkImpl.this.foot ++ == index){
return this.data;
}else{
return this.next.getNode(index);
}
}
public void setNode(int index,E data){
//核心方法同getNode(),核心是找到index对应的节点
if(LinkImpl.this.foot ++ == index){
this.data = data;
}else{
this.next.setNode(index,data);
}
}
public boolean containsNode(E data){
//遍历链表,从第一个开始,如果相同返回true,否则判断下一个节点,直到最后一个;
if(this.data.equals(data)){
return true;
}else{
if(this.next == null){
return false;
}else{
return this.next.containsNode(data);
}
}
}
public void removeNode(Node<E> previous,E data){
//
if(this.data.equals(data)){
previous.next = this.next;
}else{
if(this.next != null){
this.next.removeNode(this,data);
}
}
}
}
//------------以下为Link类中定义的成员------------//
private Node<E> root;//保存根元素
private int count;//保存链表元素个数
private int foot;//描述的是操作数组的角标,在涉及到index的方法中用到
private Object [] returnData;//返回的数据保存在这个数组中
//------------以下为Link类中定义的方法------------//
@Override
public void add(E e){
if(e == null){//如果保存的数据为null,则直接返回
return ;
}
//数据本身是不具有关联特性的,只有Node类有,那么要想实现数据的关联处理就必须将数据包装在Node中
Node<E> newNode = new Node<E>(e);//创建一个新的节点,并将数据包装在节点中
if(this.root == null){//如果这个链表是空链表,则新元素作根
this.root = newNode;
}else{//否则调用addNode()方法,将其添加到链表尾部
this.root.addNode(newNode);
}
this.count++;//
}
public int size(){//链表长度
return this.count;
}
public boolean isEmpty(){//判断链表长度是否为空
return this.count == 0;
//return this.root == null;
}
public Object[] toArray(){//数据返回
if(this.isEmpty()){
return null;
}
this.foot = 0;
this.returnData = new Object [this.count];
//利用Node类进行递归数据获取
this.root.toArrayNode();
return this.returnData;
}
public E get(int index){//和数组类似,但是数组时间复杂度为1,链表时间复杂度为n
if(index > this.count){
return null;
}//索引数据的获取应该由Node类完成
this.foot = 0;
return this.root.getNode(index);
}
public void set(int index, E data){
if(index > this.count){
return ;
}
this.foot = 0;
this.root.setNode(index,data);
}
public boolean contains(E data){
if(data == null){
return false;
}
return this.root.containsNode(data);
}
public void remove(E data){
if(this.contains(data)){//要删除的是根节点,可以直接让root指向原来root的next
if(this.root.data.equals(data)){
this.root = this.root.next;
}else{//要删除的元素不是根节点,要让这个目标节点前一个节点的next指向目标节点的next
this.root.next.removeNode(this.root,data);
}
this.count--;
}
}
public void clean(){
this.root = null;
this.count = 0;
}
}
下面是一个功能测试的Demo,并不是很完善。
public class javaDemo {
public static void main(String args[]) {
ILink<String> all = new LinkImpl<String>();
all.add("hello");
all.add("tomorrow");
all.clean();
System.out.println(all.isEmpty());
System.out.println(all.size());
}
}
总结
java中可以通过内部类可以访问外部类私有属性这一特点将节点Node类封装在LinkList类中,从而方便地实现单链表,用户只需要对链表进行操作,而对节点的操作完全封装在了类中。