Java之链表学习笔记

链表的使用与定义

链表的学习需要依赖以下两点:

  • 依赖于引用传递问题
  • this表示当前对象

1 链表的基本形式

链表是一种最为简单的数据结构,它的主要目的是依靠引用关系来实现多个数据的保存。

每个链表实际上就是由多个节点所组成的,每个节点:保存数据+下一节点引用。

  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;
    }
}
  1. 设置和获取节点元素(两种方式)
    递归操作的代码更为直观
  • 使用第一种方法实现
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. 总结

  1. 以上讲解的链表均为单链表。
  2. 需要熟记链表的常用方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

就是二二二二婷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值