System.out.println(str[x]);
}
}
}
通过以上的分析,可以发现,对象数组所有的对象元素被数组的索引控制,可以说是自动完成的控制,但是链表需要人为进行关系的设置,而且每个操作设置的时候,除了要保存“对象”之外,还要再多保留一个引用。
这个引用就和之前讲解的领导是一样的:
一个雇员的领导还是雇员,雇员领导的领导也是雇员。
范例:
先给出链表的基本操作结构
classNode{
privateStringdata; // 假设要保存的数据类型是字符串
privateNodenext;
publicNode(Stringdata){
this.data=data;
}
publicStringgetData(){
returnthis.data;
}
publicvoidsetNext(Nodenext){
this.next=next;
}
publicNodegetNext(){
returnthis.next;
}
}
范例:
挂节点
publicclassTestDemo{
publicstaticvoidmain(Stringargs[]){
//1、设置数据
Noden1=newNode("火车头");
Noden2=newNode("车厢A");
Noden3=newNode("车厢B");
n1.setNext(n2);
n2.setNext(n3);
//2、取出数据
NodecurrentNode=n1; // 设置每一个当前节点
while(currentNode!
=null){ // 有节点存在
System.out.println(currentNode.getData()); // 当前节点内容
currentNode=currentNode.getNext();
}
}
}
但是这样的方式来输出所有的节点的配置过程,发现并不是特别好,这种输出的操作,应该采用递归合适。
publicclassTestDemo{
publicstaticvoidmain(Stringargs[]){
//1、设置数据
Noden1=newNode("火车头");
Noden2=newNode("车厢A");
Noden3=newNode("车厢B");
n1.setNext(n2);
n2.setNext(n3);
//2、取出数据
print(n1);
}
publicstaticvoidprint(Nodenode){ // 取出节点内容
System.out.println(node.getData());
if(node.getNext()!
=null){ // 后面还有货
print(node.getNext()); // 向下
}
}
}
可以发现,整个一链表的关键是在于Node节点的关系匹配上。
3.1.2 、链表的基本雏形
通过之前的分析,可以发现链表的最大作用的类就是Node,但是以上程序都是由用户自己去匹配节点关系的,但是这些节点的匹配工作不应该由用户完成,应该由一个程序专门负责。
那么专门负责这个节点操作的类,就称为链表类 —— Link,负责处理节点关系,而用户不需要关心节点问题,只需要关心Link的处理操作即可。
范例:
开发Link类
classNode{
privateStringdata; // 假设要保存的数据类型是字符串
privateNodenext;
publicNode(Stringdata){
this.data=data;
}
publicStringgetData(){
returnthis.data;
}
publicvoidsetNext(Nodenext){
this.next=next;
}
publicNodegetNext(){
returnthis.next;
}
publicvoidaddNode(NodenewNode){ // 操作的是节点关系
if(this.next==null){ // 当前节点的next为null
this.next=newNode; // 保存新节点
}else{
this.next.addNode(newNode);
}
}
publicvoidprintNode(){
System.out.println(this.data);
if(this.next!
=null){ // 还有下一个节点
this.next.printNode();
}
}
}
classLink{ // 处理节点关系
privateNoderoot; // 根节点
publicvoidadd(Stringdata){ // 处理数据保存
if(data==null){ // 没有数据
return; // 直接返回
}
// 每一个数据如果要想保存在链表之中,必须将其封装为节点
// 这一操作的过程外部(用户)不需要知道
NodenewNode=newNode(data);
if(this.root==null){ // 现在没有根节点
this.root=newNode; // 第一个作为根节点
}else{
this.root.addNode(newNode);
}
}
publicvoidprint(){
if(this.root!
=null){ // 现在有根节点
this.root.printNode(); //Node类处理
}
}
}
publicclassTestDemo{
publicstaticvoidmain(Stringargs[]){
Linkall=newLink();
all.add("Hello");
all.add("World");
all.add("MLDN");
all.print();
}
}
通过以上的代码可以发现,Link类处理节点的操作,而Node类负责节点的具体顺序的操作,但是客户端,不关心节点,只关心Link类即可。
3.1.3 、开发可用链表
3.1.3 .1、增加数据:
publicStringadd(数据 对象)
通过上面的程序分析,可以发现,对于链表实现,Node类是整个操作的关键,但是首先来研究一下之前程序的问题:
Node是一个单独的类,那么这样的类是可以被用户直接使用的,但是这个类由用户直接去使用,没有任何的意义,即:
这个类有用,但是不能让用户去用,只能让Link类去使用,内部类完成。
classLink{ // 用户唯一关注的是此类
// 使用内部类的最大好处是可以和外部类进行私有操作的互相访问
privateclassNode{ // 处理节点关系
privateStringdata; // 要保存的数据
privateNodenext; // 下一个节点
publicNode(Stringdata){
this.data=data;
}
publicvoidaddNode(NodenewNode){ // 增加节点
if(this.next==null){ // 当前节点之后没有节点
this.next=newNode; // 保存新节点
}else{ // 当前节点之后有节点了
this.next.addNode(newNode); // 向下继续判断
}
}
}
privateNoderoot; // 根节点,第一个保存元素
publicbooleanadd(Stringdata){ // 增加数据
if(data==null){ // 如果保存的是一个空数据
returnfalse; // 增加失败
}
// 将数据封装为节点,目的:
节点有next可以处理关系
NodenewNode=newNode(data);
// 链表的关键就在于根节点
if(this.root==null){ // 现在没有根节点
this.root=newNode; // 第一个作为根节点
}else{ // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode); //Node类负责处理
}
returntrue; // 增加成功
}
}
使用内部类可以发现比之前的代码要节省一些,而且访问也方便了。
3.1.3 .2、增加多个数据:
publicbooleanaddAll(数据 对象 [])
以上的操作是每次增加了一个对象,那么如果现在要求增加多个对象呢,例如:
增加对象数组。
可以采用循环数组的方式,每次调用add()方法。
publicbooleanaddAll(Stringdata[]){ // 一组数据
for(intx=0;x if(!
this.add(data[x])){ // 保存不成功
returnfalse;
}
}
returntrue;
}
3.1.3 .3、统计数据个数:
public intsize()
在一个链表之中,会保存多个数据(每一个数据都被封装为Node类对象),那么要想取得这些保存元素的个数,可以增加一个size()方法完成。
应该在Link类之中增加一个统计的属性:
count:
privateintcount;// 统计个数
当用户每一次调用add()方法增加新数据的时候应该做出统计:
publicbooleanadd(Stringdata){ // 增加数据
if(data==null){ // 如果保存的是一个空数据
returnfalse; // 增加失败
}
// 将数据封装为节点,目的:
节点有next可以处理关系
NodenewNode=newNode(data);
// 链表的关键就在于根节点
if(this.root==null){ // 现在没有根节点
this.root=newNode; // 第一个作为根节点
}else{ // 根节点有了,新的节点要保留在合适的位置
this.root.addNode(newNode); //Node类负责处理
}
this.count++; // 保存数据量增加
returntrue; // 增加成功
}
而在size()方法就是简单的将count这个变量的内容返回:
publicintsize(){
returnthis.count;
}
3.1.3 .4、判断是否是空链表:
public booleanisEmpty()
所谓的空链表指的是链表之中不保存任何的数据,实际上这个null可以通过两种方式判断:
一种判断链表的根节点是否为null,另外一个是判断保存元素的个数是否为0。
publicbooleanisEmpty(){
returnthis.count==0;
}
3.1.3 .5、查找数据是否存在:
publicbooleancontains(数据 对象)
现在如果要想查询某个数据是否存在,那么基本的操作原理:
逐个盘查,盘查的具体实现还是应该交给Node类去处理,但是在盘查之前必须有一个前提:
有数据存在。
在Link类之中,增加查询的操作:
publicbooleancontains(Stringdata){ // 查找数据
// 根节点没有数据,查找的也没有数据
if(this.root==null||data==null){
returnfalse; // 不需要进行查找了
}
return this.root.containsNode(data) ; // 交给Node类处理
}
在Node类之中,完成具体的查询,查询的流程:
· 判断当前节点的内容是否满足于查询内容,如果满足返回true;
· 如果当前节点的内容不满足,则向后继续查,如果已经没有后续节点了,则返回false。
publicbooleancontainsNode(Stringdata){ // 查找数据
if(data.equals(this.data)){ // 与当前节点数据吻合
returntrue;
}else{ // 与当前节点数据不吻合