随笔都是学习笔记
随笔仅供参考,为避免笔记中可能出现的错误误导他人,请勿转载。
posts - 398,comments - 0,views - 13万

需求:

因为数组的各种局限性(长度,修改等),所以需要一种类似数组但是能够灵活操作的结构,也就是链表。

 

 

客户端(主类中)应该只是关心数据的存储、获取、修改等操作,而不是关心链表的操作,所以应该有一个专门的类来进行节点的配置。

 

例如:寄拿快递,本人只需要寄出或者拿取快递,关心的是寄出或者拿取多少快递,而不应该去关心快递的封装,运输等操作。

 

 

 

 简单的链表逻辑创建:

// 通过即可设置标准
public interface ILink <E> {    // 设置泛型避免安全隐患
    public void add(E e);

}

 

复制代码
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第一次调用:this = LinkImpl.root.next;
        // 第一次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }
    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }

    }
}
复制代码

 

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();
        all.add("Hello");
        all.add("World");
        all.add("!!!");
    }
}

 

获取集合个数、判断集合是否为空:

 

package Demo_1_28_链表;
// 通过即可设置标准
public interface ILink <E> {    // 设置泛型避免安全隐患
    public void add(E e);   // 增加数据
    public int size();  // 获取数据的个数
    public boolean isEmpty();   // 判断集合是否为空

}

 

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第一次调用:this = LinkImpl.root.next;
        // 第一次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }
    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }
}
复制代码

 

复制代码
package Demo_1_28_链表;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
    }
}
复制代码

 

 

返回集合的数据:

链表本身属于一个动态对象数组,所以可以把所有的数据以数组的形式返回,这时就可以定义一个toArray()方法,但是这个时候的方法只能返回Object类型的数组。

 

复制代码
package Demo_1_28_链表;
// 通过即可设置标准
public interface ILink <E> {    // 设置泛型避免安全隐患
    public void add(E e);   // 增加数据
    public int size();  // 获取数据的个数
    public boolean isEmpty();   // 判断集合是否为空
    public Object[] toArray();  // 将集合元素以数组的形式返回保存

}
复制代码

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第二次调用:this = LinkImpl.root.next;
        // 第三次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }

        // 第一次调用: this = LinkImpl.root
        // 第二次调用: this = LinkImpl.root.next
        // 第三次调用: this = LinkImpl.root.next.next
        public void toArrayNode(){  //  将数据添加到返回数组中
            LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data;  // 当前data添加到数组中
            if (this.next != null){ //还有下一个数据
                this.next.toArrayNode();    // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加
            }
        }

    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    private int foot = 0;   // 操作数组的脚标
    private Object[] returnData;    //返回的数据
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }

    @Override
    public Object[] toArray() {
        if (this.isEmpty()){ // 空集合
            return null;
        }
        this.foot = 0;  // 脚标起始为0
        this.returnData = new Object[this.count];   // 当前数据的个数就是返回数组的长度
        this.root.toArrayNode();    // 利用Node类进行递归获取数据
        return this.returnData;
    }
}
复制代码

 

复制代码
package Demo_1_28_链表;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        Object res [] = all.toArray();
        System.out.println(Arrays.toString(res));
        for (Object re : res) {
            System.out.println(re);
        }

    }
}
复制代码

 

 获取指定索引数据:

数组的获取时间复杂度为1,链表的获取时间复杂度为n,因为需要遍历n遍;

复制代码
package Demo_1_28_链表;
// 通过接口设置标准
public interface ILink <E> {    // 设置泛型避免安全隐患
    public void add(E e);   // 增加数据
    public int size();  // 获取数据的个数
    public boolean isEmpty();   // 判断集合是否为空
    public Object[] toArray();  // 将集合元素以数组的形式返回保存
    public E get(int index);    // 根据索引获取数据
}
复制代码

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第二次调用:this = LinkImpl.root.next;
        // 第三次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }

        // 第一次调用: this = LinkImpl.root
        // 第二次调用: this = LinkImpl.root.next
        // 第三次调用: this = LinkImpl.root.next.next
        public void toArrayNode(){  //  将数据添加到返回数组中
            LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data;  // 当前data添加到数组中
            if (this.next != null){ //还有下一个数据
                this.next.toArrayNode();    // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加
            }
        }
        public E getNode(int index){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                return this.data;   // 返回当前数据
            }else {
                return this.next.getNode(index);    // 递归调用继续向下匹配指定的索引
            }
        }

    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    private int foot = 0;   // 操作数组的脚标
    private Object[] returnData;    //返回的数据
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }

    @Override
    public Object[] toArray() {
        if (this.isEmpty()){ // 空集合
            return null;
        }
        this.foot = 0;  // 脚标起始为0
        this.returnData = new Object[this.count];   // 当前数据的个数就是返回数组的长度
        this.root.toArrayNode();    // 利用Node类进行递归获取数据
        return this.returnData;
    }

    @Override
    public E get(int index) {
        if (index >= this.count){   // 索引大于数据的个数
            return null;
        }   //索引数据的获取应该由Node类完成
        this.foot = 0;
        return  this.root.getNode(index);
    }
}
复制代码

 

复制代码
package Demo_1_28_链表;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        Object res [] = all.toArray();
        System.out.println(Arrays.toString(res));
        for (Object re : res) {
            System.out.println(re);
        }
        System.out.println("---------------数据获取------------------");
        System.out.println(all.get(0));
        System.out.println(all.get(1));
        System.out.println(all.get(2));
        System.out.println(all.get(3));
    }
}
复制代码

 

 指定索引的数据修改:

此操作的时间复杂度也是为n。

复制代码
package Demo_1_28_链表;
// 通过接口设置标准
public 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); // 修改指定索引数据
}
复制代码

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第二次调用:this = LinkImpl.root.next;
        // 第三次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }

        // 第一次调用: this = LinkImpl.root
        // 第二次调用: this = LinkImpl.root.next
        // 第三次调用: this = LinkImpl.root.next.next
        public void toArrayNode(){  //  将数据添加到返回数组中
            LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data;  // 当前data添加到数组中
            if (this.next != null){ //还有下一个数据
                this.next.toArrayNode();    // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加
            }
        }
        public E getNode(int index){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                return this.data;   // 返回当前数据
            }else {
                return this.next.getNode(index);    // 递归调用继续向下匹配指定的索引
            }
        }
        public void setNode(int index, E data){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                this.data = data;   // 返回当前数据
            }else {
                this.next.setNode(index, data);// 递归调用继续向下匹配指定的索引
            }
        }

    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    private int foot = 0;   // 操作数组的脚标
    private Object[] returnData;    //返回的数据
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }

    @Override
    public Object[] toArray() {
        if (this.isEmpty()){ // 空集合
            return null;
        }
        this.foot = 0;  // 脚标起始为0
        this.returnData = new Object[this.count];   // 当前数据的个数就是返回数组的长度
        this.root.toArrayNode();    // 利用Node类进行递归获取数据
        return this.returnData;
    }

    @Override
    public E get(int index) {
        if (index >= this.count){   // 索引大于数据的个数
            return null;
        }   //索引数据的获取应该由Node类完成
        this.foot = 0;
        return  this.root.getNode(index);
    }

    @Override
    public void set(int index, E data) {
        if (index >= this.count) {   // 索引大于数据的个数
            return; //结束
        }   //索引数据的修改应该由Node类完成
        this.foot = 0;  // 重置索引下标
        this.root.setNode(index,data);   // 修改数据
        }
    }
复制代码

 

复制代码
package Demo_1_28_链表;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        Object res [] = all.toArray();
        System.out.println("修改前的数据:" + Arrays.toString(res));
        System.out.println("---------------数据修改------------------");
        all.set(1,"世界");
        all.set(2,"@!@!@");
        Object res1 [] = all.toArray();
        System.out.println("修改后的数据:" + Arrays.toString(res1));
        System.out.println("---------------数据获取------------------");
        System.out.println(all.get(0));
        System.out.println(all.get(1));
        System.out.println(all.get(2));
        System.out.println(all.get(3));

    }
}
复制代码

 

 节点的删除:

复制代码
package Demo_1_28_链表;
// 通过接口设置标准
public 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 e);     // 删除节点
}
复制代码

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第二次调用:this = LinkImpl.root.next;
        // 第三次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }

        // 第一次调用: this = LinkImpl.root
        // 第二次调用: this = LinkImpl.root.next
        // 第三次调用: this = LinkImpl.root.next.next
        public void toArrayNode(){  //  将数据添加到返回数组中
            LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data;  // 当前data添加到数组中
            if (this.next != null){ //还有下一个数据
                this.next.toArrayNode();    // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加
            }
        }
        public E getNode(int index){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                return this.data;   // 返回当前数据
            }else {
                return this.next.getNode(index);    // 递归调用继续向下匹配指定的索引
            }
        }
        public void setNode(int index, E data){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                this.data = data;   // 返回当前数据
            }else {
                this.next.setNode(index, data);// 递归调用继续向下匹配指定的索引
            }
        }
        public boolean containsNode(E data){
            if (data.equals(this.data)){    // 想要查询的数据等于当前数据则表示存在
                return true;    // 返回真
            }else {     // 想要查询的数据不等当前数据则表示想要查询的数据不在当前位置
                if (this.next == null){
                    return false;   // 当前位置的下一个数据的地址为空返回false(后面没有数据了)
                }else {
                    return this.next.containsNode(data);    // 继续判断想要查询的数据是否在下一个位置
                }
            }
        }
        public void removeNode(Node previous,E data){
            if (this.data.equals(data)){    // 找到指定位置的节点
                System.out.println("删除了节点数据为:" + data + "、地址为:" + previous.next);
                previous.next = this.next;  // 上一节点与当前节点的下一节点连接,以空出当前节点
            }else {     // 当前节点不是指定节点
                if (this.next != null){     // 如果当前节点的下一个节点不为空
                    this.next.removeNode(this,data);    // 这里传入到下一个节点,this是当前节点(下一节点的上节点),data是指定的节点
                }
            }
        }

    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    private int foot = 0;   // 操作数组的脚标
    private Object[] returnData;    //返回的数据
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }

    @Override
    public Object[] toArray() {
        if (this.isEmpty()){ // 空集合
            return null;
        }
        this.foot = 0;  // 脚标起始为0
        this.returnData = new Object[this.count];   // 当前数据的个数就是返回数组的长度
        this.root.toArrayNode();    // 利用Node类进行递归获取数据
        return this.returnData;
    }

    @Override
    public E get(int index) {
        if (index >= this.count){   // 索引大于数据的个数
            return null;
        }   //索引数据的获取应该由Node类完成
        this.foot = 0;
        return  this.root.getNode(index);
    }

    @Override
    public void set(int index, E data) {
        if (index >= this.count) {   // 索引大于数据的个数
            return; //结束
        }   //索引数据的修改应该由Node类完成
        this.foot = 0;  // 重置索引下标
        this.root.setNode(index,data);   // 修改数据
    }

    @Override
    public boolean contains(E data) {
        if (data == null){
            return false;   // 想查看的数据为空返回false
        }
        return this.root.containsNode(data);
    }

    @Override
    public void remove(E data) {
        if (this.contains(data)){   // 判断想要删除的节点是否存在
            if (this.root.data.equals(data)){   // 如果想要删除的节点已存在,判断是否为根节点
                System.out.println(data + "-->数据为根节点已经删除!");
                this.root = this.root.next;     // 删除根节点
                // 根节点判断完成,以后应该从下一节点开始判断
            }else {
                this.root.next.removeNode(this.root,data);  // 不是根节点那么就让Node节点进行操作,传入根节点作为节点的上一节点,和指定节点data
            }
            this.count --;
        }
    }
}
复制代码

 

复制代码
package Demo_1_28_链表;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        Object res [] = all.toArray();
        System.out.println("修改前的数据:" + Arrays.toString(res));
        System.out.println("---------------数据修改------------------");
        all.set(1,"世界");
        all.set(2,"@!@!@");
        Object res1 [] = all.toArray();
        System.out.println("修改后的数据:" + Arrays.toString(res1));
        System.out.println("---------------数据获取------------------");
        System.out.println(all.get(0));
        System.out.println(all.get(1));
        System.out.println(all.get(2));
        System.out.println(all.get(3));
        System.out.println("---------------查看数据是否存在------------------");
        if (all.contains("世界")){    // 存在则为真
            System.out.println("存在!");
        }else {     // 数据不存在
            System.out.println("Not Found!");
        }
        System.out.println("---------------数据删除------------------");
        all.remove("Hello");
        all.remove("@!@!@");
        Object res2 [] = all.toArray();
        System.out.println("删除后的数据:" + Arrays.toString(res2));
    }
}
复制代码

 

链表清空:

复制代码
package Demo_1_28_链表;
// 通过接口设置标准
public 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 e);     // 删除指定节点
    public void clean();    // 清空链表
}
复制代码

 

复制代码
package Demo_1_28_链表;
/*
* 整个过程中LinkImpl类只是进行root节点的创建和add方法参数的接收并向Node节点发出指令addNode(),真正的节点操作类是LinkImpl类中的Node类
* 而节点的连接其实就是引用的传递,Node类只管进行将后节点的引用传给给前节点的next来记录
* data的值是通过客户端传入到LinkImpl的add()方法,所以Node节点并不关心data的值
* */
public class LinkImpl<E> implements ILink<E> {
    // 内部类的属性是提供给外部类使用,那么就没有必要再加上setter和getter方法了
    private class Node{ //保存节点的数据关系
        private E data; //保存的数据
        private Node next;  // 下一个节点的引用
        public Node(E data){    // 有数据才有意义
            this.data = data;
        }
        // 第一次调用:this = LinkImpl.root;
        // 第二次调用:this = LinkImpl.root.next;
        // 第三次调用:this = LinkImpl.root.next.next;
        public void addNode(Node newNode){  // 保存新的Node数据
            if (this.next == null){ //当前节点(根节点)的下一个节点为空
                // 下面将新节点(下一节点)的引用传给当前节点的next以记录地址
                this.next = newNode;    // 根节点的下一节点为newNode(将新节点与前一节点成功连接)
            }else {
                this.next.addNode(newNode); // 递归调用方法知道找出下一节点为空的节点并保存新节点
            }
        }

        // 第一次调用: this = LinkImpl.root
        // 第二次调用: this = LinkImpl.root.next
        // 第三次调用: this = LinkImpl.root.next.next
        public void toArrayNode(){  //  将数据添加到返回数组中
            LinkImpl.this.returnData [LinkImpl.this.foot ++ ] = this.data;  // 当前data添加到数组中
            if (this.next != null){ //还有下一个数据
                this.next.toArrayNode();    // 这一步是this.next调用的toArrayNode方法,递归进行数据的添加
            }
        }
        public E getNode(int index){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                return this.data;   // 返回当前数据
            }else {
                return this.next.getNode(index);    // 递归调用继续向下匹配指定的索引
            }
        }
        public void setNode(int index, E data){
            if (LinkImpl.this.foot ++ == index){    // 定位至指定的索引
                this.data = data;   // 返回当前数据
            }else {
                this.next.setNode(index, data);// 递归调用继续向下匹配指定的索引
            }
        }
        public boolean containsNode(E data){
            if (data.equals(this.data)){    // 想要查询的数据等于当前数据则表示存在
                return true;    // 返回真
            }else {     // 想要查询的数据不等当前数据则表示想要查询的数据不在当前位置
                if (this.next == null){
                    return false;   // 当前位置的下一个数据的地址为空返回false(后面没有数据了)
                }else {
                    return this.next.containsNode(data);    // 继续判断想要查询的数据是否在下一个位置
                }
            }
        }
        public void removeNode(Node previous,E data){
            if (this.data.equals(data)){    // 找到指定位置的节点
                System.out.println("删除了节点数据为:" + data + "、地址为:" + previous.next);
                previous.next = this.next;  // 上一节点与当前节点的下一节点连接,以空出当前节点
            }else {     // 当前节点不是指定节点
                if (this.next != null){     // 如果当前节点的下一个节点不为空
                    this.next.removeNode(this,data);    // 这里传入到下一个节点,this是当前节点(下一节点的上节点),data是指定的节点
                }
            }
        }

    }
    /*--------------- 以下为LinkImpl类中定义的成员 ------------------*/
    private Node root;  // 保存根元素
    private int count;  // 保存数据的个数
    private int foot = 0;   // 操作数组的脚标
    private Object[] returnData;    //返回的数据
    /*--------------- 以下为LinkImpl类中定义的方法 ------------------*/
    @Override
    public void add(E e) {
        if (e == null) {//保存的数据为空
            return;
        }
        // 数据本身不具备关联特性,只有Node类有,那么想实现关联处理,就必须将数据包装在Node类中
        Node newNode = new Node(e); // 创建一个新的节点,传入的数据e通过Node类的构造方法赋值给data
        if (this.root == null){
            this.root = newNode;    // 第一个节点为根节点
        }else{  // 根节点存在
            this.root.addNode(newNode);
        }
        this.count ++;
    }

    @Override
    public int size() {
        return this.count;
    }

    @Override
    public boolean isEmpty() {
//        return this.root == null;
        return this.count == 0; // 两个return效果都是一样的,为空则返回true,否则返回false
    }

    @Override
    public Object[] toArray() {
        if (this.isEmpty()){ // 空集合
            return null;
        }
        this.foot = 0;  // 脚标起始为0
        this.returnData = new Object[this.count];   // 当前数据的个数就是返回数组的长度
        this.root.toArrayNode();    // 利用Node类进行递归获取数据
        return this.returnData;
    }

    @Override
    public E get(int index) {
        if (index >= this.count){   // 索引大于数据的个数
            return null;
        }   //索引数据的获取应该由Node类完成
        this.foot = 0;
        return  this.root.getNode(index);
    }

    @Override
    public void set(int index, E data) {
        if (index >= this.count) {   // 索引大于数据的个数
            return; //结束
        }   //索引数据的修改应该由Node类完成
        this.foot = 0;  // 重置索引下标
        this.root.setNode(index,data);   // 修改数据
    }

    @Override
    public boolean contains(E data) {
        if (data == null){
            return false;   // 想查看的数据为空返回false
        }
        return this.root.containsNode(data);
    }

    @Override
    public void remove(E data) {
        if (this.contains(data)){   // 判断想要删除的节点是否存在
            if (this.root.data.equals(data)){   // 如果想要删除的节点已存在,判断是否为根节点
                System.out.println(data + "-->数据为根节点已经删除!");
                this.root = this.root.next;     // 删除根节点
                // 根节点判断完成,以后应该从下一节点开始判断
            }else {
                this.root.next.removeNode(this.root,data);  // 不是根节点那么就让Node节点进行操作,传入根节点作为节点的上一节点,和指定节点data
            }
            this.count --;
        }
    }

    @Override
    public void clean() {
        this.root = null;   // 根节点重置为空(根节点后指向的地址重置为空),后续所有节点为空
        this.count = 0;     // 个数清零
    }
}
复制代码

 

复制代码
package Demo_1_28_链表;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ILink<String> all = new LinkImpl<>();   // 设置接口引用(all),这个引用(all)只能使用接口存在的方法和属性
        System.out.println("add前数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        all.add("Hello");
        all.add("World");
        all.add("!!!");
        System.out.println("add后数据的个数:" + all.size());
        System.out.println("集合是否为空:" + all.isEmpty());
        Object res [] = all.toArray();
        System.out.println("修改前的数据:" + Arrays.toString(res));
        System.out.println("---------------数据修改------------------");
        all.set(1,"世界");
        all.set(2,"@!@!@");
        Object res1 [] = all.toArray();
        System.out.println("修改后的数据:" + Arrays.toString(res1));
        System.out.println("---------------数据获取------------------");
        System.out.println(all.get(0));
        System.out.println(all.get(1));
        System.out.println(all.get(2));
        System.out.println(all.get(3));
        System.out.println("---------------查看数据是否存在------------------");
        if (all.contains("世界")){    // 存在则为真
            System.out.println("存在!");
        }else {     // 数据不存在
            System.out.println("Not Found!");
        }
        System.out.println("---------------数据删除------------------");
        all.remove("Hello");
        all.remove("@!@!@");
        Object res2 [] = all.toArray();
        System.out.println("删除后的数据:" + Arrays.toString(res2));
        System.out.println("---------------数据清零------------------");
        all.clean();
        Object res3 [] = all.toArray();
        if (res3 != null) {
            System.out.println("清空后的数据:" + Arrays.toString(res3));
        }else{
            System.out.println("数据清空后的链表数据:" + res3);
        }
    }
}
复制代码

 

 

posted on   时间完全不够用啊  阅读(88)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示