java动态链表功能程序组_菜鸡的Java笔记 第二十七 - java 链表基本概念

链表基本概念

1.链表的基本形式

2.单向链表的完整实现

认识链表

链表= 可变长的对象数组,属于动态对象数组的范畴

链表是一种最简单的线性数据结构,之所以会存在有数据结构问题主要是解决亮点:存储的数据不受限制,查找速度快

对象数组有那些问题呢?

对象数组可以保存一组对象方便开发

对象数组的长度固定,而且数据的删除,修改,增加处理麻烦

所有的开发之中都100%不可能避免掉对象数组的使用

正因为如此现在如果要想让其可以编写出便于维护的代码,那么就需要实现一个动态对象数组,那么就可以使用链表完成

但是现在如果要想实现动态的对象数组,要考虑两个问题:

为了适应于所有的开发要求,此对象数组要求可以保存所有的数据类型,那么一首选Object类型

为了可以保存多个数据,需要采用引用的方式来进行保存,但是数据本身是不可能保存顺序的

所以需要有一个可以负责保存顺序的类来包装这个数据

分析 结论:

保存数据为了方便使用 Object

数据本身不包含有先后的逻辑关系,所以将数据封装在一个 Node 类,负责关系的维护

范例:定义出如下的一个类

class Node{//b表示定义的节点

private Object data;//要保存的数据

private Node next; //保存下一个节点

public Node(Object data){ //有数据才可以保存节点

this.data =data;

}public void setNext(Node next){//设置节点

this.next =next;

}public Node getNext(){ //取得节点

return this.next;

}

}public classlinkedList{public static voidmain(String args[]){

}

}

完成节点之后,下面就可以进行节点的基本使用了

范例:采用循环的方式操作节点

class Node{//b表示定义的节点

private Object data;//要保存的数据

private Node next; //保存下一个节点

public Node(Object data){ //有数据才可以保存节点

this.data =data;

}public void setNext(Node next){//设置节点

this.next =next;

}public Node getNext(){ //取得节点

return this.next;

}publicObject getData(){return this.data;

}

}public classlinkedList{public static voidmain(String args[]){//1.定义各自独立的操作节点

Node root = new Node ("火车头");

Node n1= new Node("车厢1");

Node n1= new Node("车厢2");//2.设置彼此间的关系

root.setNext(n1);

n1.setNext(n2);//3.输出

Node currentNode = root;//从根节点开始取出数据

while(currentNode != null){

System.out.println(currentNode.getData());//取出数据

currentNode = currentNode.getNext();//下一个节点

}

}

}

以上的操作如果使用循环并不方便。最好的做法是递归调用

范例:利用递归的方式实现内容的取得

class Node{//b表示定义的节点

private Object data;//要保存的数据

private Node next; //保存下一个节点

public Node(Object data){ //有数据才可以保存节点

this.data =data;

}public void setNext(Node next){//设置节点

this.next =next;

}public Node getNext(){ //取得节点

return this.next;

}publicObject getData(){return this.data;

}

}public classlinkedList{public static voidmain(String args[]){//1.定义各自独立的操作节点

Node root = new Node ("火车头");

Node n1= new Node("车厢1");

Node n1= new Node("车厢2");//2.设置彼此间的关系

root.setNext(n1);

n1.setNext(n2);//3.输出

}public static voidprint(Node node){if(node == null){return;//结束方法调用

}

System.out.println(node.getData());

print(node,getNext());

}

}

}

通过以上的结构讲解,应该已经清楚了链表在整个实现的关键就是 Node 类,Node 类要保存数据与下一个节点

链表开发入门

虽然以上的代码已经实现了链的形式,但是以上的代码之中存在有两个问题:

用户需要自己手工定义Node 类,但是实际上Node 类对用户没用

Node 类的先后关系如果交由用户处理,那么整个代码就乱了

所以现在发现,需要有一个类,这个类可以负责所有的Node 的关系匹配,而用户只需要通过这个类保存数据或取得数据即可

那么就可以编写一个Link类完成

范例:基本结构

class Node{//b表示定义的节点

private Object data;//要保存的数据

private Node next; //保存下一个节点

public Node(Object data){ //有数据才可以保存节点

this.data =data;

}public void setNext(Node next){//设置节点

this.next =next;

}public Node getNext(){ //取得节点

return this.naxt;

}publicObject getData(){return this.data;

}

}class link{ //表示一个链表操作类,利用此类来隐藏Node的节点匹配

public void add(Object obj){//向链表里面追加数据

}public void print(){//输出链表中的全部数据

}

}public classlinkedList{public static voidmain(String args[]){

Link all= newLink();

all.add("商品1");

all.add("商品2");

all.add("商品3");

all.print();

}

}

用户不关心Node,用户只关心通过Link操作完成后可以取得数据

范例:完善程序

class Node{//b表示定义的节点

private Object data;//要保存的数据

private Node next; //保存下一个节点

public Node(Object data){ //有数据才可以保存节点

this.data =data;

}public void setNext(Node next){//设置节点

this.next =next;

}public Node getNext(){ //取得节点

return this.next;

}publicObject getData(){return this.data;

}//第一次调用:Link.root//第一次调用:Link.root.next//第一次调用:Link.root.next.next

public voidaddNode(Node newNode){if(this.next == null){ //当前节点之后没有节点

this.next =newNode;

}else{//如果现在当前节点后有节点

this.next.addNode(newNode);

}

}//第一次调用:this = Link.root//第一次调用:this = Link.root.next

public voidprintNode(){

System.out.println(this.data);//当前节点数据

if(this.next != null){ //还有后续的节点

this.next.printNode();

}

}

}class Link{ //表示一个链表操作类,利用此类来隐藏Node的节点匹配

private Node root;//需要有一根元素

public void add(Object obj){//向链表里面追加数据//将操作的数据包装为Node类对象,这样才可以进行先后关系的排列

Node newNode = newNode(obj);//x现在没有根节点

if(this.root == null){//this出现在Link类,表示Link类的当前对象

this.root = newNode;//将第一个节点作为根节点

}else{//根节点存在了//this.root.setNext(newNode);

this.root.addNode(newNode); //由根节点负责调用

}//(Node 负责排序Link 负责根)

}public void print(){//输出链表中的全部数据

if(this.root != null){ //现在有数据

this.root.printNode(); //输出节点数据

}

}

}public classlinkedList{public static voidmain(String args[]){

Link all= newLink();

all.add("商品1");

all.add("商品2");

all.add("商品3");

all.print();

}

}

此时的代码就实现了链表的基本操作,整个过程之中,用户不关心Node的处理,只关心数据的保存和输出

开发可用链表

以上的代码只能够说是基本的链表结构形式,但是从另外一个方面,以上的代码给我们提供了链表的实现思路

可是如何才能设计一个好的链表呢?

链表的实现必须依靠于节点类Node类来实现,但是整个的过程之中一定要清楚,用户不需要操作Node

而且通过现在的代码可以发现Node类里的操作有特定的需要

但是这个时候Node类写在了外面,那么就表示用户可以直接操作Node类对象

所以程序现在的问题在于:如何可以让Node类只为Link类服务,但是有不让其他类所访问

那么自然就要想到使用内部类完成,而且内部类的好处在于:可以与外部类直接进行私有属性的访问

范例:合理的结构规划

class Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}

}//************************************

private Node root; //根元素

}public classlinkedList{public static voidmain(String args[]){

}

}

如果要开发程序,那么一定要创建自己的操作标准,那么一旦说到标准就应该想到使用接口来完成

interfaceLink{

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}

}//************************************

private Node root; //根元素

}public classlinkedList{public static voidmain(String args[]){

}

}

在随后完善代码的过程之中,除了功能的实现之外,实际上也属于接口功能的完善

实现数据增加操作  public void add(Object data)

1.需要在接口里面定义好数据增加的操作方法

interfaceLink{public void add(Object data);//数据增加

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}

}//************************************

private Node root; //根元素

}public classlinkedList{public static voidmain(String args[]){

}

}

2.进行代码的实现,同样实现的过程之中 LinkImpl 类只关心根节点,而具体的子节点的排序都交由 Node 类负责处理

在Link类中实现add()方法:

interfaceLink{public void add(Object data);//数据增加

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}

}//************************************

private Node root; //根元素

public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}

}

}public classlinkedList{public static voidmain(String args[]){

}

}

在Node类中进行数据的追加操作:

interfaceLink{public void add(Object data);//数据增加

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}

}//************************************

private Node root; //根元素

public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}

}

}public classlinkedList{public static voidmain(String args[]){

}

}

此时的代码实现过程与基本的实现是完全一样的

取得保存元素个数: public int size()

每个Link接口的对象都要保存各自的内容,所以为了方便控制保存个数,可以增加一个Link类中的属性,并且用此属性在数据成功追加之后实现自增操作

在Link类中定义一个 count 属性,默认值为:0

interfaceLink{public void add(Object data);//数据增加

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}

}//************************************

private Node root; //根元素

private int count = 0;//当数据已经成功添加完毕之后实现计数的统计

public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}this.count ++; //当节点保存完毕之后就可以进行数据增加了

}

}public classlinkedList{public static voidmain(String args[]){

}

}

而在Link接口里面追加 size() 的方法,同时在LinkImpl子类里面进行方法的覆写

interfaceLink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}

}//************************************

private Node root; //根元素

private int count = 0;//当数据已经成功添加完毕之后实现计数的统计

public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}this.count ++;//当节点保存完毕之后就可以进行数据增加了

}public intsize(){return this.count;

}

}public classlinkedList{public static voidmain(String args[]){

Link all= newLinkImpl();

System.out.println(all.size());

all.add("商品1");

all.add("商品2");

all.add("商品3");

System.out.println(all.size());

}

}

此操作直接与最后的输出有关

判断是否为空集合: public boolean isEmpty()

所谓的空链表指的是链表之中没有任何的数据存在

如果要想判断集合是否为空,有两种方式:长度为 0 ,另外一个就是判断根元素是否为 null

范例:在Link 接口中追加一个新的方法: isEmpty

interfaceLink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

public boolean isEmpty();//判断是否为空集合

}

范例:在LinkImpl类中实现此方法

interfaceLink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

public boolean isEmpty();//判断是否为空集合

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}

}//************************************

private Node root; //根元素

private int count = 0;//当数据已经成功添加完毕之后实现计数的统计

public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}this.count ++;

}public intsize(){return this.count;

}public booleanisEmpty()(return this.count == 0;//或者 return this.root == null;

)

}public classlinkedList{public static voidmain(String args[]){

Link all= newLinkImpl();

System.out.println(all.isEmpty());

all.add("商品1");

all.add("商品2");

all.add("商品3");

System.out.println(all.isEmpty());

}

}

实际上此操作与 size() 几乎一脉相承

数据查询: public boolean contains(Object data)

任何情况下 Link 类只负责与根元素操作有关的内容,而所有额其他元素的数据的变更,查找,关系的匹配都应该交由 Node 类来负责处理

1.在Link接口里面创建一个新的方法

interfaceLink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

public boolean isEmpty();//判断是否为空集合

public boolean contains(Object data);//判断是否有指定的元素

}

2.在LinkImpl 子类里面要通过根元素开始调用查询,所有的查询交由 Node 类负责

在LinkImpl 发出具体的查询要求之前,必须要保证有集合数据

public booleancontains(Object data){if(this.root == null){//没有集合数据

return false;

}return this.root.containsNode(data);

//根元素交给 Node 类完成

}

在Node 类中实现数据的查询

//第一次:this.LinkImpl.root//第二次:this.LinkImpl.root.next

public booleancurrentNode(Object data){if(this.data.equals(data)){ //该节点数据符合于查找数据

return true;

}else{//继续向下查找

if(this.next != null){//当前节点之后还有下一个节点

return this.next.containsNode(data);

}else{return false;

}

}

}

这样查询的模式实质上也属于逐行的判断扫描

根据索引取得数据: public Object get(int index)

既然链表属于动态的对象数组,所以数组本身一定会提供有根据索引取得数据的操作支持,那么在链表中也可以定义与之类似的方法

但是在进行数据保存的时候并没有设置索引,那么现在有两个方案:

修改 Node 类的结构,为每一个节点自动匹配一个索引,数据的删除不方便

在操作索引时动态生存索引,适合集合的修改

1.修改Lnik 类为其增加一个 foot 的属性,之所以将foor属性定义在 LinkImpl 类之中,主要目的是方便多个 Node 共同进行属性的操作使用的,,同时内部类可以方便的访问外部类中的私有成员

private int foot = 0;//操作索引的脚标

2.在Link 接口里面首先定义出新的操作方法

public Object get(int index);//根据索引取得内容,索引从0开始

3.在LinkImpl 类里面定义功能实现:

在Node 类中应该提供有一个 getNode() 的方法,那么这个方法的功能是依靠判断每一个索引值的操作形式

public Object getNode(int index){//传递索引的序号

if(LinkImpl.this.foot++ == index){

//当前的索引为要查找的索引

return this.data;//返回当前节点对象

}else{return this.next.getNode(index);

}

}

在Link 类中实现 get() 方法

public Object get(intindex){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

return this.root.getNode(index);

}

这种查询的模式与 contains() 最大的不同一个是数字索引,一个是内容

修改数据: public void set(int index,Object obj)

与get() 相比 set() 方法依然需要进行循环的判断,只不过 get() 索引判断成功之后会返回数据,而 set() 只需要用新的数据更新已有节点数据即可

1.在Link接口里面创建一个新的方法

public void set(int index,Object obj)

2.修改LnikImopl 子类,流程与 get() 差别不大:

在Node 类里面追加一个新的 setNode() 方法;

public void setNode(intindex,Object obj){if(LinkImpl.this.foot ++ ==index){this.obj = obj;//重新保存数据

}else{this.next.setNode(index,obj);

}

}

在LinkImpl 子类里面覆写 set() 方法,在 set() 方法编写的时候也需要针对于给定的索引进行验证

public void set(intindex,Object obj){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

this.root.setNode(index,obj);

}

set() 与 get() 方法实际上在使用时都有一个固定的条件:集合中的保存数据顺序应该为添加顺序

数据删除: public void remove(Object obj)

如果要进行数据的删除,那么对于整个链表而言就是节点的删除操作

而节点的删除操作过程之中需要考虑的问题是什么?

要删除的是根节点还是子节点问题

1.要删除的是根节点:Link 类处理,因为根节点有关的所有节点都应该交由 Link 类管理

Link.root = Link.root.next

2.要删除的是子节点:交由 Node 类负责处理

删除节点的上一个节点.next = 删除节点.next

1.在Link接口里面创建一个新的方法

public void remove(Object data);// 删除数据

2.修改LnikImopl 类的操作:

在Node 类中增加一个 removeNode() 的方法

//第一次:this.LinkImpl.root.next,previous = LinkImpl.root;//第二次:this.LinkImpl.root.next.next,previous = LinkImpl.root.next

public voidremove(Node previous,Object data){if(this.data.equals(data)){

previous.next= this.next;//空出当前节点

}else{this.next.removeNode(this,data);

}

}

在Link类中增加新的操作:

public voidremove(Object data){if(this.contains(data)){ //数据如果存在则删除

if(this.root.equals(data)){//根元素为要删除的元素

this.root = this.root.next; //第二个元素作为根元素

}else{ //不是根元素,根元素一斤判断完了

this.root.next.removeNode(this.root,data);

}this.count --;

}

}

整个删除操作很好的体现了 this 的特性

contains() 和 remove() 方法必须有对象比较的支持,对象比较使用的就是 Object 类中的 equals() 方法

清空链表:public void clear()

当链表中的数据不需要在使用的时候,那么可以进行清空,而清空最简单的做法就是将 root设置为 null

1.在Link接口里面创建一个新的方法

public void clear();//清空链表

2.直接在 LinkImpl 类中修改清空操作

public voidclear(){this.foot = null;this.count = 0; //元素的保存个数清0

System.gc();//回收内存空间

}

实际上这种代码还欠缺一个很好的内存释放问题

返回数据: public Object[] toArray()

恒定的概念:链表就是动态对象数组,但是要想操作链表中的数据,那么最好的做法是将其转换为对象数组返回

所以这个时候就需要针对数据做递归处理

1.在Link 接口里面定义返回对象数组的方法

public Object[] toArray()

2.修改LnikImopl 子类

由于Node 类需要操作链表数据读取,所以应该在LinkImpl 子类里面应该提供有一个对象数组的属性

public Object retData[] = null;

在LinkImpl 子类里面覆写 toArray() 方法,并且要根据长度开辟数组空间

publicObject[] toArray(){if(this.root == null){return null;

}this.retData = new Object[this.count];this.foot = 0;this.root.toArrayNode();return this.retData;

}

在Node类里面实现数据的保存操作

public voidtoArrayNode(){

LinkImpl.this.retData[LinkImpl.this.foot ++] = this.data;if(this.next != null){this.next.toArrayNode();

}

}

不过以上的设计都没有考虑过性能问题

interfaceLink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

public boolean isEmpty();//判断是否为空集合

public boolean contains(Object data);//判断是否有指定的元素

public Object get(int index);//根据索引取得内容,索引从0开始

public void set(intindex,Object obj);public void remove(Object data);//删除数据

public void clear();//清空链表

publicObject[] toArray();

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}public Object getNode(int index){//传递索引的序号

if(LinkImpl.this.foot++ == index){ //当前的索引为要查找的索引

return this.data;//返回当前节点对象

}else{return this.next.getNode(index);

}

}public void setNode(intindex,Object data){if(LinkImpl.this.foot ++ ==index){this.data = data;//重新保存数据

}else{this.next.setNode(index,data);

}

}//第一次:this.LinkImpl.root.next,previous = LinkImpl.root;//第二次:this.LinkImpl.root.next.next,previous = LinkImpl.root.next

public voidremove(Node previous,Object data){if(this.data.equals(data)){

previous.next= this.next;//空出当前节点

}else{this.next.removeNode(this,data);

}

}//第一次:this.LinkImpl.root//第二次:this.LinkImpl.root.next

public booleancurrentNode(Object data){if(this.data.equals(data)){ //该节点数据符合于查找数据

return true;

}else{//继续向下查找

if(this.next != null){//当前节点之后还有下一个节点

return this.next.containsNode(data);

}else{return false;

}

}

}public voidtoArrayNode(){

LinkImpl.this.retData[LinkImpl.this.foot ++] = this.data;if(this.next != null){this.next.toArrayNode();

}

}

}//************************************

private Node root; //根元素

private int count = 0;//当数据已经成功添加完毕之后实现计数的统计

private int foot = 0;//操作索引的脚标

public Object retData[] = null;public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}this.count ++;

}public voidremove(Object data){if(this.contains(data)){ //数据如果存在则删除

if(this.root.data.equals(data)){//根元素为要删除的元素

this.root = this.root.next; //第二个元素作为根元素

}else{ //不是根元素,根元素一斤判断完了

this.root.next.removeNode(this.root,data);

}this.count --;

}

}public voidclear(){this.foot = null;this.count = 0;

System.gc();//回收内存空间

}public intsize(){return this.count;

}public booleanisEmpty(){return this.count == 0;//或者 return this.root == null;

}public booleancontains(Object data){if(this.root == null){//没有集合数据

return false;

}return this.root.containsNode(data);//根元素交给 Node 类完成

}publicObject[] toArray(){if(this.root == null){return null;

}this.retData = new Object[this.count];this.foot = 0;this.root.toArrayNode();return this.retData;

}public Object get(intindex){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

return this.root.getNode(index);

}public void set(intindex,Object data){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

this.root.setNode(index,data);

}

}public classlinkedList{public static voidmain(String args[]){

Link all= newLinkImpl();

System.out.println(all.isEmpty());

all.add("A");

all.add("B");

all.add("C");

Object obj[]=all.toArray();for(int x = 0;x < obj.length; x++){

System.out.println(obj[x]);

}

}

}

数组形式返回

1.在Link接口里面追加有返回数据的方法:

public Object [] toArray(); // 以对象数组的形式返回链表数据

2.修改LnikImopl 子类:

需要追加一个进行返回数据数组下标控制,并且这一操作属性需要被Node内部类使用,那么必须将其定义为外部类属性

private int foot = 0;//操作索引的脚标

对于返回数据保存由于需要在内部类中处理,所以在外部类中定义属性

private Object retData[]; // 定义一个返回的数组

在进行 toArray() 方法覆写的时候由于该方法可能调用很多次,并且有可能调用过程之中链表中的数据个数发生了改变,以每一次都需要重新根据数组大小开辟空间

publicObject [] toArray(){if(this.root == null){ //现在没有数据

return new Object[0]; //没有数据返回

}this.retData = new Object[this.count];//根据已有的数据个数开辟数组个数

this.foot = 0;//脚标重置

this.root.toArrayNode();//交给Node类负责

return this.retData;

}

3.真正获得数据的过程(节点迭代过程)应该有Node类负责

//第1次调用: this = LinkImpl.root9//第2次调用: this = LinkImpl.root.naxt

public void toArrayNode(){ //递归调用

LinkImpl.this.retData[LinkImpl.this.foot ++] = this.data;if(this.naxt != null){ //还有下一个节点

this.naxt.toArrayNode();

}

}

综合实战:宠物商店

接口实际上是属于某几类事物的抽象,也就是说在整个的定义结构上,接口标准应该优先于类定义出来,而后类按照指定的接口标准进行实现

如果说现在有这样的一个案例要求:定义一个宠物商店,在这个宠物商店里面可以实现宠物的上架,下架,关键字查询

现在假设宠物里面只包含两个信息(名字,年龄),而后要求通过类的结构关系描述出本程序

宠物商店应该是一个程序类,因为宠物商店可能有很多种,但是其具备的特征肯定只有一个,而后宠物商店只与宠物标准有关(符合此标准的可能有多种宠物类型)

而宠物商店里面需要保存有多中宠物信息(不知道个数的对象数组,应该采用链表实现)

1.应该建立宠物的标准服务接口

interfacePet{publicString getName();public intgetAge();

}

2.定义宠物商店,宠物尚待年不关心具体的宠物类型,只关心宠物标准

classPetShop{private Ilink allPets = new LinkImpl();//动态对象数组

public void add(Pet pet){ //追加的是宠物

this.allPets.add(pet); //宠物信息追加

}public void delete(Pet pet){ //删除宠物信息

this.allPets.remove(pet); //equals() 支持

}public ILink search(String keyWord){ //返回查询结果

ILink result = new LinkImpl(); //查询结果

Object data[] = this.allPets.toArray(); //变为对象数组返回

for(int x = 0; x < data.length; x++){

Pet tempPet=(Pet)data[x];if(tempPet.getName().contains(keyWord)){//有此关键字

result.add(tempPet); //保存返回结果

}returnresult;

}

}

}

3.定义宠物

class Dog{ //狗

privateString name;private intage;public Dog(String name,intage){this.name =name;this.age =age;

}public booleanequals(Object obj){if(this ==obj){return true;

}if(obj == null){return false;

}if(!(obj instanceofDog)){return false;

}

Dog pet=(Dog)obj;return this.name.equals(pet.name) && this.age ==pet.age;

}publicString getName(){return this.name;

}public intgetAge(){return this.age;

}publicString toString(){return "【宠物狗】 name = "+ this,name +",age = "+this.age;

}

}classCat{privateString name;private intage;public Cta(String name,intage){this.name =name;this.age =age;

}public booleanequals(Object obj){if(this ==obj){return true;

}if(obj == null){return false;

}if(!(obj instanceofCat)){return false;

}

Cat pet=(Cat)obj;return this.name.equals(pet.name) && this.age ==pet.age;

}publicString getName(){return this.name;

}public intgetAge(){return this.age;

}publicString toString(){return "【宠物猫】 name = "+ this,name +",age = "+this.age;

}

}

4.测试

public classlinkedList{public static voidmain(String args[]){

PetShop shop= new PetShop(); //宠物商店准备好了

shop.add(new Dog("狗子",1));

shop.add(new Dog("二狗子",1));

shop.add(new Cat("喵喵",1));

shop.add(new Cat("小瞄",1));

shop.delete(new Dog("二狗子",1)); //删除操作

ILink result = shop.search("瞄");

Object data[]=result.toArray();for(int x = 0; x< data.length; x++){

System.out.println(data[x]);

}

}

}

宠物商店和宠物之间没有任何的联系,依靠的是接口关键在一起,同时为了存储更多的数据使用了链表处理

利用此原则可以实现更毒的场景:

一个停车可以停放各种车辆,例如:小轿车,越野车,卡车

一个饭店不允许任何宠物进入 或 有多种菜品

interfaceILink{public void add(Object data);//数据增加

public int size();//取得保存元素的个数

public boolean isEmpty();//判断是否为空集合

public boolean contains(Object data);//判断是否有指定的元素

public Object get(int index);//根据索引取得内容,索引从0开始

public void set(intindex,Object obj);public void remove(Object data);//删除数据

public void clear();//清空链表

publicObject[] toArray();

}class LinkImpl implements Link{ //外部的程序只关心此类

private class Node{//使用私有内部类,防止外部使用此类

privateObject data;privateNode next;publicNode(Object data){this.data =data;

}public voidaddNode(Node newNode){if(this.next == null){this.next =newNode;

}else{this.next.addNode(newNode);

}

}public Object getNode(int index){//传递索引的序号

if(LinkImpl.this.foot++ == index){ //当前的索引为要查找的索引

return this.data;//返回当前节点对象

}else{return this.next.getNode(index);

}

}public void setNode(intindex,Object data){if(LinkImpl.this.foot ++ ==index){this.data = data;//重新保存数据

}else{this.next.setNode(index,data);

}

}//第一次:this.LinkImpl.root.next,previous = LinkImpl.root;//第二次:this.LinkImpl.root.next.next,previous = LinkImpl.root.next

public voidremove(Node previous,Object data){if(this.data.equals(data)){

previous.next= this.next;//空出当前节点

}else{this.next.removeNode(this,data);

}

}//第一次:this.LinkImpl.root//第二次:this.LinkImpl.root.next

public booleancurrentNode(Object data){if(this.data.equals(data)){ //该节点数据符合于查找数据

return true;

}else{//继续向下查找

if(this.next != null){//当前节点之后还有下一个节点

return this.next.containsNode(data);

}else{return false;

}

}

}public voidtoArrayNode(){

LinkImpl.this.retData[LinkImpl.this.foot ++] = this.data;if(this.next != null){this.next.toArrayNode();

}

}

}//************************************

private Node root; //根元素

private int count = 0;//当数据已经成功添加完毕之后实现计数的统计

private int foot = 0;//操作索引的脚标

public Object retData[] = null;public voidadd(Object data){if(data == null){//现在没有要增加的数据

return;//结束调用

}

Node newNode= new Node(data);//创建新的节点

if(this.root == null){//保留有根节点

this.root =root;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}this.count ++;

}public voidremove(Object data){if(this.contains(data)){ //数据如果存在则删除

if(this.root.data.equals(data)){//根元素为要删除的元素

this.root = this.root.next; //第二个元素作为根元素

}else{ //不是根元素,根元素一斤判断完了

this.root.next.removeNode(this.root,data);

}this.count --;

}

}public voidclear(){this.foot = null;this.count = 0;

System.gc();//回收内存空间

}public intsize(){return this.count;

}public booleanisEmpty(){return this.count == 0;//或者 return this.root == null;

}public booleancontains(Object data){if(this.root == null){//没有集合数据

return false;

}return this.root.containsNode(data);//根元素交给 Node 类完成

}publicObject[] toArray(){if(this.root == null){return null;

}this.retData = new Object[this.count];this.foot = 0;this.root.toArrayNode();return this.retData;

}public Object get(intindex){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

return this.root.getNode(index);

}public void set(intindex,Object data){if(index >= this.count){ //索引不存在

return null;

}this.foot = 0;//查询之前执行一次清零操作

this.root.setNode(index,data);

}

}interfacePet{publicString getName();public intgetAge();

}classPetShop{private Ilink allPets = new LinkImpl();//动态对象数组

public void add(Pet pet){ //追加的是宠物

this.allPets.add(pet); //宠物信息追加

}public void delete(Pet pet){ //删除宠物信息

this.allPets.remove(pet); //equals() 支持

}public ILink search(String keyWord){ //返回查询结果

ILink result = new LinkImpl(); //查询结果

Object data[] = this.allPets.toArray(); //变为对象数组返回

for(int x = 0; x < data.length; x++){

Pet tempPet=(Pet)data[x];if(tempPet.getName().contains(keyWord)){//有此关键字

result.add(tempPet); //保存返回结果

}returnresult;

}

}

}class Dog implementsPet{privateString name;private intage;public Dog(String name,intage){this.name =name;this.age =age;

}public booleanequals(Object obj){if(this ==obj){return true;

}if(obj == null){return false;

}if(!(obj instanceofDog)){return false;

}

Dog pet=(Dog)obj;return this.name.equals(pet.name) && this.age ==pet.age;

}publicString getName(){return this.name;

}public intgetAge(){return this.age;

}publicString toString(){return "【宠物狗】 name = "+ this,name +",age = "+this.age;

}

}class Cat implementsPet{privateString name;private intage;public Cta(String name,intage){this.name =name;this.age =age;

}public booleanequals(Object obj){if(this ==obj){return true;

}if(obj == null){return false;

}if(!(obj instanceofCat)){return false;

}

Cat pet=(Cat)obj;return this.name.equals(pet.name) && this.age ==pet.age;

}publicString getName(){return this.name;

}public intgetAge(){return this.age;

}publicString toString(){return "【宠物猫】 name = "+ this,name +",age = "+this.age;

}

}public classlinkedList{public static voidmain(String args[]){

PetShop shop= new PetShop(); //宠物商店准备好了

shop.add(new Dog("狗子",1));

shop.add(new Dog("二狗子",1));

shop.add(new Cat("喵喵",1));

shop.add(new Cat("小瞄",1));

shop.delete(new Dog("二狗子",1)); //删除操作

ILink result = shop.search("瞄");

Object data[]=result.toArray();for(int x = 0; x< data.length; x++){

System.out.println(data[x]);

}

}

}

总结

1.以上只是简单的单向链表,要求清楚大概的原理

2.对于以下给出的方法一定要掌握

No

方法名称

类型

描述

1

public void add(Object data)

普通

向集合追加数据

2

public int size()

普通

取得集合中保存的元素个数

3

public boolean isEmpty()

普通

判断是否为空集合

4

public boolean contains(Object data)

普通

判断是否存在有指定的元素,需要 equals() 支持

5

public Object get(int index)

普通

根据索引取得指定的数据

6

public void set(int index,Object obj)

普通

修改指定索引位置上的数据

7

public void remove(Object obj)

普通

数据删除操作,需要 equals() 支持

8

public void clear()

普通

清空链表

9

public Object[] toArray()

普通

链表转换为对象数组数据

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值