Java之链表学习笔记
- 链表的使用与定义
- 1 链表的基本形式
- 2 链表基本实现
- 3. 开发可用链表
- 3.1 程序基本结构
- 3.2 常用方法
- 3.2.1 增加:public void add(数据类型 变量)
- 3.2.2 取得链表中保存的元素个数 :public int size()
- 3.2.3 判断是否为空链表: public boolean isEmpty()
- 3.2.4 数据查询:public boolean contains(数据类型 变量)
- 3.2.5 根据索引取得数据: public 数据类型 get(int index)
- 3.2.6 修改:public void set(int index, 数据类型 变量)
- 3.2.7 数据删除: public void remove(数据类型 变量)
- 3.2.8 将链表转为对象数组:public 数据类型 to Array[]
- 4. 使用链表
- 5. 在关系中使用链表
- 6. 总结
链表的使用与定义
链表的学习需要依赖以下两点:
- 依赖于引用传递问题
- this表示当前对象
1 链表的基本形式
链表是一种最为简单的数据结构,它的主要目的是依靠引用关系来实现多个数据的保存。
每个链表实际上就是由多个节点所组成的,每个节点:保存数据+下一节点引用。
- 保存节点关系的代码
以上是专门负责保存节点关系的类,至于说怎么保存的关系,现在并未考虑(没有考虑每个节点的位置)。
代码:
//每一个链表实际上由多个节点组成
class Node{//定义一个节点
private String data; //要保存的数据
private Node next; //要保存的下一节点
public Node(String data){
this.data=data;
}
public void setNext(Node next)
{
this.next=next;
}
public Node getNext(){
return this.next;
}
public String getData(){
return this.data;
}
}
- 设置和获取节点元素(两种方式)
递归操作的代码更为直观 。
- 使用第一种方法实现
public class LinjDemo {
public static void main(String[] args) {
//第一步:设置所有数据
Node root=new Node("火车头");
Node node1=new Node("车厢A");
Node node2=new Node("车厢B");
//设置节点位置关系
root.setNext(node1);
node1.setNext(node2);
//第二步:取出所有数据
Node currentNode=root; //从根节点开始读取
while (currentNode!=null){//当前节点存在数据
System.out.println(currentNode.getData());
//将下一节点设置为当前节点
currentNode=currentNode.getNext();
}
- 上述方式采用循环并不方便,最好的方式应该是采用递归实现。使用第二种(递归)实现
public class LinjDemo {
public static void main(String[] args) {
//第一步:设置所有数据
Node root = new Node("火车头");
Node node1 = new Node("车厢A");
Node node2 = new Node("车厢B");
//设置节点位置关系
root.setNext(node1);
node1.setNext(node2);
print(root);
}
//第二步:取出所有数据
public static void print(Node current){
if (current==null){ //递归结束条件
return; //结束方法
}
System.out.println(current.getData());
print(current.getNext()); //每次递归时改变节点
}
}
由于数据本身不具备先后的关系,因此使用Node类来封装数据,同时利用Node类指向下一个节点。
2 链表基本实现
实际上,用户在使用时完全没必要关系Node类是否存在。同时,所有的节点的引用关系也不应由用户处理,应该由一个专门的工具类进行处理。
下面需要定义一个类,来帮助客户端去隐藏所有的链表中给出的细节,用户只关心存取数据 即可。
- 创建链表时根节点root没有数据,为null。
- 为了设置数据的先后关系,所以将data封装成一个Node类对象
代码:
//每一个链表实际上由多个节点组成
class Node {//定义一个节点
private String data; //要保存的数据
private Node next; //要保存的下一节点
public Node(String data) {
this.data = data;
}
public void setNext(Node next) {
this.next = next;
}
public Node getNext() {
return this.next;
}
public String getData() {
return this.data;
}
//实现节点的添加
//第一次调用(Link):this=Link.root
//第二次调用(Node):this=Link.root.next
//第三次调用 (Node):this=Link.root.next.next
public void addNode(Node newNode) {
if (this.next == null) { //当前节点的下一个为空
this.next = newNode;
} else {
this.next.addNode(newNode);
}
}
//实现节点的输出
//第一次调用(Link):this=Link.root
//第二次调用(Node):this=Link.root.next
//第三次调用 (Node):this=Link.root.next.next
public void printNode(){
System.out.println(this.data); //输出当前数据
if(this.next!=null){ //判断后面是否还有数据
this.next.printNode(); //输出下一个数据
}
}
}
class Link { //负责数据设置和输出
private Node root; //根节点
public void add(String data) { //增加数据
Node newNode = new Node(data);
//判断有无根节点
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在
this.root.addNode(newNode);
}
}
public void print() { //数据输出
if(this.root!=null){
this.root.printNode();
}
}
}
public class LinjDemo {
public static void main(String[] args) {
Link link = new Link(); //由这个类负责存取数据
link.add("Hello"); //存放数据
link.add("WORLD"); //存放数据
link.add("MLDN"); //存放数据
link.add("YTT"); //存放数据
link.print(); //展示数据
}
}
- Link类的主要功能是控制Node类对象的产生和根节点;
- Node类主要负责数据的保存以及节点引用关系的分配。
3. 开发可用链表
指的是可以使用链表实现数据的增加、修改、删除、查询操作。
3.1 程序基本结构
在开发具体的可用链表之前,首先必须明确一个道理,Node类负责所有的节点数据的保存以及节点引用关系的分配,所以Node类不可能单独去使用,而以上的Node类可以单独使用,下面需要修改设计结构,让Node类只能被Link类使用。
这个时候使用内部类就是一个最好的选择。内部类可以使用private定义,这样一个内部类只能被一个外部类使用,另外内部类可以与外部类之间进行私有属性的直接访问。
3.2 常用方法
3.2.1 增加:public void add(数据类型 变量)
如果进行数据增加,Link类负责节点对象的产生,并由Link类维护根节点,所有的节点的关系匹配交给Node类负责处理。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
}
public void print() { //数据输出
if (this.root != null) {
this.root.printNode();
}
}
}
public class LinjDemo {
public static void main(String[] args) {
Link all = new Link();
all.add("Hello");
all.add("World");
all.add(null);
}
}
注意 :此时使用了一个不许为null的判断,但并不是所有的链表都不许为空。
3.2.2 取得链表中保存的元素个数 :public int size()
既然每一个链表对象都只有一个root根元素,那么每一个链表都有自己的长度,可直接在Link类里面设置一个count属性,随后每一次数据添加完成之后,可以进行个数的自增。
修改Link.java类:
- 增加一个count属性。
- 在add()方法里面增加数据统计操作。
- 随后在Link类中增加一个方法size()。
代码:
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count=0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size(){
return this.count;
}
}
public class LinjDemo {
public static void main(String[] args) {
Link all = new Link();
all.add("Hello");
all.add("World");
all.add(null);
System.out.println(all.size());
}
}
注意 :本程序中null不会被保存,因此运行结果为2。
3.2.3 判断是否为空链表: public boolean isEmpty()
- 判断root是否有对象,即root是否为null
- 判断保存的数据量 count
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count=0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size(){
return this.count;
}
public boolean isEmpty(){
return this.count==0;
}
}
public class LinjDemo {
public static void main(String[] args) {
Link all = new Link();
System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add(null);
System.out.println(all.size());
System.out.println(all.isEmpty());
}
}
3.2.4 数据查询:public boolean contains(数据类型 变量)
判断某一个数据是否存在。循环链表中的内容与要查询的数据进行匹配。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(String data){
if(data.equals(this.data)){ //当前节点数据与查询数据是否相等
return true;
}else{
if(this.next!=null){ //是否还有其他节点数据
return this.next.containsNode(data);
}else{ //后续无节点数据,结束查询
return false;
}
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count=0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size(){
return this.count;
}
public boolean isEmpty(){
return this.count==0;
}
public boolean contains(String data){
//现在没有查询的数据或者根节点也未保存数据
if(data==null||this.root==null){
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
}
public class LinjDemo {
public static void main(String[] args) {
Link all = new Link();
// System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add(null);
// System.out.println(all.size());
// System.out.println(all.isEmpty());
System.out.println(all.contains("hi"));
}
}
本次使用String型数据,采用equals()方法进行比较。但如果使用自定义对象,需要定义一个对象比较的方法(暂时将方法名称定义为compare())。
3.2.5 根据索引取得数据: public 数据类型 get(int index)
链表中保存了多个String对象,在程序里面只有数组可以保存多个对象。与数组相比,链表没有长度限制,严格来说,链表属于动态对象数组,那么也应该具备数组那样根据索引取得元素的功能。
数组的编号/索引是动态的。
- 在Link类中增加foot属性,表示每一个Node元素的编号。
- 一个链表可能查询多次,那么foot应该在每一次查询时都从头开始。
- 但是必须要记住,只有在链表不为空且查询索引小于链表数据个数时可以有效查询。
- 在Node类中实现getNode()方法,内部类和外部类之间可以方便的进行私有属性的互相访问。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(String data){
if(data.equals(this.data)){ //当前节点数据与查询数据是否相等
return true;
}else{
if(this.next!=null){ //是否还有其他节点数据
return this.next.containsNode(data);
}else{ //后续无节点数据,结束查询
return false;
}
}
}
//
public String getNode(int index){
//使用当前的foot内容与要查询的索引进行比较
//将foot的内容自增,目的是为了下一次比较
if(Link.this.foot++==index){
return this.data; //返回当前节点数据
}else{ //继续向后查询
return this.next.getNode(index);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count=0;
private int foot=0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size(){
return this.count;
}
public boolean isEmpty(){
return this.count==0;
}
public boolean contains(String data){
//现在没有查询的数据或者根节点也未保存数据
if(data==null||this.root==null){
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
public String get(int index){
if(index>this.count){
return null;
}
this.foot=0; //表示从前向后开始查找
return this.root.getNode(index); //将查询过程交给Node类处理
}
}
public class LinkDemo {
public static void main(String[] args) {
Link all = new Link();
// System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add(null);
// System.out.println(all.size());
// System.out.println(all.isEmpty());
//System.out.println(all.contains("hi"));
System.out.println(all.get(1));
}
}
3.2.6 修改:public void set(int index, 数据类型 变量)
使用新的数据内容替换原有数据,即修改指定索引内容。修改数据和查询的差别不大,查询的时候当满足索引时仅返回数据,此处将数据返回变为重新赋值即可。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(String data){
if(data.equals(this.data)){ //当前节点数据与查询数据是否相等
return true;
}else{
if(this.next!=null){ //是否还有其他节点数据
return this.next.containsNode(data);
}else{ //后续无节点数据,结束查询
return false;
}
}
}
//
public String getNode(int index){
//使用当前的foot内容与要查询的索引进行比较
//将foot的内容自增,目的是为了下一次比较
if(Link.this.foot++==index){
return this.data; //返回当前节点数据
}else{ //继续向后查询
return this.next.getNode(index);
}
}
//
public void setNode(int index,String data){
if(Link.this.foot++==index){
this.data=data; //进行内容修改
}else{
this.next.setNode(index,data);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count=0;
private int foot=0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size(){
return this.count;
}
public boolean isEmpty(){
return this.count==0;
}
public boolean contains(String data){
//现在没有查询的数据或者根节点也未保存数据
if(data==null||this.root==null){
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
public String get(int index){
if(index>this.count){
return null;
}
this.foot=0; //表示从前向后开始查找
return this.root.getNode(index); //将查询过程交给Node类处理
}
public void set(int index, String data){
if(index>this.count){
return; //结束方法调用
}
this.foot=0;
this.root.setNode(index,data);
}
}
public class LinkDemo {
public static void main(String[] args) {
Link all = new Link();
// System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add(null);
all.set(1,"已经修改");
// System.out.println(all.size());
// System.out.println(all.isEmpty());
//System.out.println(all.contains("hi"));
System.out.println(all.get(1));
//System.out.println(all.get(10));
}
}
3.2.7 数据删除: public void remove(数据类型 变量)
删除指定数据,如果是对象则需要进行对象比较。对于删除数据而言,实际上需要分为两种情况:
- 要删除的数据是根节点,此种情况Link类处理
- 要删除的不是根节点,删除的是node节点,此情况Node类处理,从第二节点开始判断。
删除数据的最终形式:当前节点上一节点.next=当前节点。next,即:空出了当前节点。
在Node类里面增加一个removeNode()方法,此方法专门负责处理非根节点。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(String data) {
if (data.equals(this.data)) { //当前节点数据与查询数据是否相等
return true;
} else {
if (this.next != null) { //是否还有其他节点数据
return this.next.containsNode(data);
} else { //后续无节点数据,结束查询
return false;
}
}
}
//
public String getNode(int index) {
//使用当前的foot内容与要查询的索引进行比较
//将foot的内容自增,目的是为了下一次比较
if (Link.this.foot++ == index) {
return this.data; //返回当前节点数据
} else { //继续向后查询
return this.next.getNode(index);
}
}
//
public void setNode(int index, String data) {
if (Link.this.foot++ == index) {
this.data = data; //进行内容修改
} else {
this.next.setNode(index, data);
}
}
//要传递上一个节点及要删除的数据
public void removeNode(Node previous, String data) {
if (data.equals(this.data)) { //当前节点为要删除节点
previous.next = this.next;
} else {
this.next.removeNode(this, data);
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count = 0;
private int foot = 0;
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size() {
return this.count;
}
public boolean isEmpty() {
return this.count == 0;
}
public boolean contains(String data) {
//现在没有查询的数据或者根节点也未保存数据
if (data == null || this.root == null) {
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
public String get(int index) {
if (index > this.count) {
return null;
}
this.foot = 0; //表示从前向后开始查找
return this.root.getNode(index); //将查询过程交给Node类处理
}
public void set(int index, String data) {
if (index > this.count) {
return; //结束方法调用
}
this.foot = 0;
this.root.setNode(index, data);
}
public void remove(String data) {
if (this.contains(data)) { //判断要删除的内容是否存在
if (data.equals(this.root.data)) { //判断要删除的是否是根节点
this.root=this.root.next; //空出当前根节点
}else{
this.root.next.removeNode(this.root,data);
}
}
this.count--;
}
}
public class LinkDemo {
public static void main(String[] args) {
Link all = new Link();
// System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add("YTT");
all.add("你好");
all.add(null);
//all.set(1, "已经修改");
all.remove("YTT");
// System.out.println(all.size());
// System.out.println(all.isEmpty());
//System.out.println(all.contains("hi"));
System.out.println(all.get(2));
//System.out.println(all.get(10));
}
}
删除是整个链表中最麻烦的功能,但是理解删除这个操作就可以更好地理解this对象和引用关系。
3.2.8 将链表转为对象数组:public 数据类型 to Array[]
任何情况下都不可能在类中使用输出语句,要是想输出数据,一定要将数据返回至调用处,而链表是动态对象数组,因此最好将链表以对象数组的形式返回。
链表数据变为对象数据取出是最为重要的功能。
class Link { //链表类
private class Node { //内部类,为Link服务
private String data; //保存数据
private Node next; //引用关系
public Node(String data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(String data) {
if (data.equals(this.data)) { //当前节点数据与查询数据是否相等
return true;
} else {
if (this.next != null) { //是否还有其他节点数据
return this.next.containsNode(data);
} else { //后续无节点数据,结束查询
return false;
}
}
}
//
public String getNode(int index) {
//使用当前的foot内容与要查询的索引进行比较
//将foot的内容自增,目的是为了下一次比较
if (Link.this.foot++ == index) {
return this.data; //返回当前节点数据
} else { //继续向后查询
return this.next.getNode(index);
}
}
//
public void setNode(int index, String data) {
if (Link.this.foot++ == index) {
this.data = data; //进行内容修改
} else {
this.next.setNode(index, data);
}
}
//要传递上一个节点及要删除的数据
public void removeNode(Node previous, String data) {
if (data.equals(this.data)) { //当前节点为要删除节点
previous.next = this.next;
} else {
this.next.removeNode(this, data);
}
}
public void toArrayNode(){
Link.this.retArray[Link.this.foot++]=this.data;
if(this.next==null){
return;
}else{
this.next.toArrayNode();
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count = 0;
private int foot = 0;
private String [] retArray; //返回的数组
public void add(String data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size() {
return this.count;
}
public boolean isEmpty() {
return this.count == 0;
}
public boolean contains(String data) {
//现在没有查询的数据或者根节点也未保存数据
if (data == null || this.root == null) {
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
public String get(int index) {
if (index > this.count) {
return null;
}
this.foot = 0; //表示从前向后开始查找
return this.root.getNode(index); //将查询过程交给Node类处理
}
public void set(int index, String data) {
if (index > this.count) {
return; //结束方法调用
}
this.foot = 0;
this.root.setNode(index, data);
}
public void remove(String data) {
if (this.contains(data)) { //判断要删除的内容是否存在
if (data.equals(this.root.data)) { //判断要删除的是否是根节点
this.root = this.root.next; //空出当前根节点
} else {
this.root.next.removeNode(this.root, data);
}
}
this.count--;
}
public String [] toArray(){
if(this.root==null){
return null;
}
this.foot=0; //需要控制脚标
this.retArray=new String[this.count];
this.root.toArrayNode(); //交给Node类处理
return this.retArray;
}
}
public class LinkDemo {
public static void main(String[] args) {
Link all = new Link();
// System.out.println(all.isEmpty());
all.add("Hello");
all.add("World");
all.add("YTT");
all.add("你好");
all.add(null);
//all.set(1, "已经修改");
//all.remove("YTT");
//System.out.println(all.size());
// System.out.println(all.isEmpty());
//System.out.println(all.contains("hi"));
//System.out.println(all.get(2));
//System.out.println(all.get(10));
String[] data=all.toArray();
for(String d:data){
System.out.println(d);
}
}
}
4. 使用链表
以上链表严格来说使用意义并不大,能够操作、保留的数据也比较少,所以可以采用自定义类来进行链表的操作。
由于链表中需要保存的对象要能够实现contains()、remove()等功能,所以在类中要提供有对象比较方法的支持。
新建一个Book对象,并且写出其比较方法。
class Book {
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
public String getInfo(){
return "图书名称:"+this.title+",价格"+this.price;
}
public boolean compare(Book book){
if(this==book){
return true;
}
if(book==null){
return false;
}
if(this.title.equals(book.title)&&this.price==book.price){
return true;
}
return false;
}
}
class Link { //链表类
private class Node { //内部类,为Link服务
private Book data; //保存数据
private Node next; //引用关系
public Node(Book data) {
this.data = data;
}
public void addNode(Node newNode) {
if (this.next == null) { //当前的下一节点为空
this.next = newNode;
} else { //向后继续保存
this.next.addNode(newNode);
}
}
//第一次调用Link进行
//第二次调用Node进行
public boolean containsNode(Book data) {
if (data.compare(this.data)) { //当前节点数据与查询数据是否相等
return true;
} else {
if (this.next != null) { //是否还有其他节点数据
return this.next.containsNode(data);
} else { //后续无节点数据,结束查询
return false;
}
}
}
//
public Book getNode(int index) {
//使用当前的foot内容与要查询的索引进行比较
//将foot的内容自增,目的是为了下一次比较
if (Link.this.foot++ == index) {
return this.data; //返回当前节点数据
} else { //继续向后查询
return this.next.getNode(index);
}
}
//
public void setNode(int index,Book data) {
if (Link.this.foot++ == index) {
this.data = data; //进行内容修改
} else {
this.next.setNode(index, data);
}
}
//要传递上一个节点及要删除的数据
public void removeNode(Node previous, Book data) {
if (data.compare(this.data)) { //当前节点为要删除节点
previous.next = this.next;
} else {
this.next.removeNode(this, data);
}
}
public void toArrayNode(){
Link.this.retArray[Link.this.foot++]=this.data;
if(this.next==null){
return;
}else{
this.next.toArrayNode();
}
}
}
//===============以上为内部类=======================================
private Node root; //需要根节点
private int count = 0;
private int foot = 0;
private Book [] retArray; //返回的数组
public void add(Book data) {
if (data == null) {
return;
}
Node newNode = new Node(data); //要保存的数据
if (this.root == null) {
this.root = newNode; //将这一节点设置为根节点
} else { //根节点已经存在,其他节点让Node类处理
this.root.addNode(newNode);
}
this.count++;
}
public int size() {
return this.count;
}
public boolean isEmpty() {
return this.count == 0;
}
public boolean contains(Book data) {
//现在没有查询的数据或者根节点也未保存数据
if (data == null || this.root == null) {
return false;
}
return this.root.containsNode(data); //从根元素开始查询数据是否存在
}
public Book get(int index) {
if (index > this.count) {
return null;
}
this.foot = 0; //表示从前向后开始查找
return this.root.getNode(index); //将查询过程交给Node类处理
}
public void set(int index, Book data) {
if (index > this.count) {
return; //结束方法调用
}
this.foot = 0;
this.root.setNode(index, data);
}
public void remove(Book data) {
if (this.contains(data)) { //判断要删除的内容是否存在
if (data.compare(this.root.data)) { //判断要删除的是否是根节点
this.root = this.root.next; //空出当前根节点
} else {
this.root.next.removeNode(this.root, data);
}
}
this.count--;
}
public Book[] toArray(){
if(this.root==null){
return null;
}
this.foot=0; //需要控制脚标
this.retArray=new Book[this.count];
this.root.toArrayNode(); //交给Node类处理
return this.retArray;
}
}
public class BookDemo {
public static void main(String[] args) {
Link all = new Link();
all.add(new Book("数据结构与算法", 23.0));
all.add(new Book("计算机网络", 26.0));
all.add(new Book("通信原理", 26.0));
all.add(new Book("信号与系统", 26.0));
System.out.println(all.size());
Book[] books=all.toArray();
for(int i=0;i<all.size();i++){
System.out.println(books[i].getInfo());
}
}
}
5. 在关系中使用链表
链表就是动态对象数组,那么在之前进行数据表映射的时候(本次只以一对多为例进行讲解),都会出现对象数组的概念,所以现在可以利用链表实现对象数组的保存。
链表就是横向替代数组。
对于任何一个要使用链表的类而言,一定要提供对象比较方法。
6. 总结
- 以上讲解的链表均为单链表。
- 需要熟记链表的常用方法。