• 模拟LinkedList


          Linkedlist是一个集合容器,具有增删改查基本功能,本例中模拟增删查,对于修改,只需要将原节点处的val更新为新值即可,增加和删除在链表中速度是比较快的,查找,修改慢,因为要从头或者从尾开始遍历。自定义的linkedList名称为yLinkedList。

          类似于ArrayList中需要有一个数组来存储元素,在Linkedlist中也需要一个容器来存储元素,这个容器叫什么名字自己随意取,这里使用Node。该容器理论上可以放在任意处,只要在Mylinkedlist中可以调用即可,但考虑到外部类不会直接使用Node,所以将Node作为Mylinkedlist的内部私有类比较合适。

           链表有长度,所以具有属性size,链表通过索引查找元素时,从头部或者尾部开始遍历,链表中需要存在头部(head)和尾部(tail)节点,LinkedList是双链表,在每个Node节点中都需要存在一个指向前一个节点指针和指向后一个节点指针,且Node节点也必须保存当前节点的值,这样MyLinkedList的大致结构就有了,由于是简单模拟增删查,List接口中的方法挺多的,故未实现List

    package com.demo.langdemo.jdk;
    
    public class MyLinkedList {
        int size;
        // 当前集合的首节点
        private Node head;
        // 当前集合的尾节点
        private Node tail;
    
        /*
         * 存储元素的容器
         */
        private class Node {
            // 当前节点的值
            Object val;
            // 当前节点的前一个节点
            Node pre;
            // 当前节点的后一个节点
            Node next;
    
            Node(Node pre, Object val, Node next) {
                this.pre = pre;
                this.val = val;
                this.next = next;
            }
        }
    }

    在首部添加元素

    当前添加的元素的前一个节点是null,后一个节点就是之前的head节点,若当前的首节点为null,则表明整个集合元素个数为0,,将当前元素添加后,首尾节点相同,都为当前节点。当首节点不为null,则将当前首节点的的pre指向新添加节点,并且将首节点指向新添加节点

    public void addFirst(Object val) {
            // 定位新添加的节点的位置,添加到首节点,则其前驱节点为null
            Node newNode = new Node(null, val, head);
            // 修改其他节点位置
            if (head == null) {
                // 首节点为空,则说明当前集合中还不存在数据,尾节点为空,也说明为空,但两个判断条件同样效果,使用一个判断即可
                tail = newNode;
            } else {
                head.pre = newNode;
            }
            // 更新head为当前节点
            head = newNode;
            size++;
        }

    在尾部添加元素

    在尾部添加元素时,前一个节点即为尾节点,后一个节点为null,当尾节点为null时,则表明整个集合元素个数为0,此时首尾节点相同,都为当前节点。当尾节点不为null时,将当前尾节点的的next指向新添加节点,并且将tail节点指向新添加元素

    public void addLast(Object val) {
            // 定位新添加的节点的位置,添加到尾节点,则其后继节点为null
            Node newNode = new Node(head, val, null);
            if (tail == null) {
                // 尾节点为空
                head = newNode;
            } else {
                tail.next = newNode;
            }
    
            tail = newNode;
            size++;
        }

    直接调用add方法时,从头插入或者从尾插入可根据具体情况选择

    在指定位置添加元素

    首先确定索引是否超出了当前集合的size,超出则抛出异常,如果index==size,则在尾部插入,如果index==0,则在头部插入,否则即是在中间部位插入。中间部位插入时,首先要获取到查入位置已存在的节点(查询方法在后面介绍),获取其pre节点,新添加节点的pre指向已存在节点的pre节点,新添加节点的next节点指向已经存在的已存在的节点。已存在的节点的pre节点的next节点指向新添加节点,已存在的节点的pre节点指向新添加节点

    public void add(int index, Object val) {
            // 需要找到当前索引的节点
            try {
                if (index < 0 || index > size) {
                    throw new Exception("error");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (index == size) {
                addLast(val);
            } else if (index == 0) {
                addFirst(val);
            } else {
                Node currNode = getNode(index);
                Node pre = currNode.pre;
                Node newNode = new Node(pre, val, currNode);
                pre.next = newNode;
                currNode.pre = newNode;
    size ++; } }

    查找指定位置的节点

    从中间开始查找无从下手,但每次添加元素时,size都会做对应的自增,所以每个索引也是唯一对应一个元素的,这里不考虑性能,直接就从head开始遍历查找,找index对应元素,那么index-1的next节点即为当前要找的index对应的元素

        public Object get(int index) {
            return getNode(index).val;
        }
    
        private Node getNode(int index) {
            Node node = head;
            for (int i = 0; i < index; i++) {
                // 将head的next节点重新赋值给node,继续查询下一个head的next节点,这样,index-1的next节点即为我们所需要的节点
                node = head.next;
            }
            return node;
        }

    根据指定索引删除

    获取索引对应的元素,找到其pre和next节点,并将pre和next节点之间建立联系,将当前被删除元素的pre和next节点置为null,解除与集合中元素的联系

    public Object remove(int index) {
            // 找到索引对应的节点
            Node node = getNode(index);
            // 获取该节点的pre和next节点
            Node pre = node.pre;
            Node next = node.next;
    
            // 设置该节点的pre和next对应关系
            pre.next = next;
            next.pre = pre;
            
            node.next=null;
            node.pre = null;
            
            size--;
            return node.val;
        }

    根据元素值删除

    从头或者从尾或者两头开始遍历等,扎到每个node节点的值与要删除的对比,若一致,则删除,将当前要删除的节点的pre和next节点删除,解除与集合中元素的关系,注意要删除的元素必须是重写了equals方法,否则删除的结果可能与预期不一致。

    public Object remove(Object val) {
            Node node = head;
            for (int i = 0; i < size; i++) {
                if (node.val.equals(val)) {
                    Node pre = node.pre;
                    Node next = node.next;
    
                    pre.next = next;
                    next.pre = pre;
                    break;
                }
                node = node.next;
            }
    
            node.pre = null;
            node.next = null;
            size--;
            return node.val;
        }

    完整实现类

    MyLinkedList.java

    package com.demo.langdemo.jdk;
    
    public class MyLinkedList {
        int size;
        // 当前集合的首节点
        private Node head;
        // 当前集合的尾节点
        private Node tail;
    
        /*
         * 存储元素的容器
         */
        private class Node {
            // 当前节点的值
            Object val;
            // 当前节点的前一个节点
            Node pre;
            // 当前节点的后一个节点
            Node next;
    
            Node(Node pre, Object val, Node next) {
                this.pre = pre;
                this.val = val;
                this.next = next;
            }
        }
    
        public void addLast(Object val) {
            // 定位新添加的节点的位置,添加到尾节点,则其后继节点为null
            Node newNode = new Node(head, val, null);
            if (tail == null) {
                // 尾节点为空
                head = newNode;
            } else {
                tail.next = newNode;
            }
    
            tail = newNode;
            size++;
        }
    
        public void addFirst(Object val) {
            // 定位新添加的节点的位置,添加到首节点,则其前驱节点为null
            Node newNode = new Node(null, val, head);
            // 修改其他节点位置
            if (head == null) {
                // 首节点为空,则说明当前集合中还不存在数据,尾节点为空,也说明为空,但两个判断条件同样效果,使用一个判断即可
                tail = newNode;
            } else {
                head.pre = newNode;
            }
            // 更新head为当前节点
            head = newNode;
            size++;
        }
    
        public void add(Object val) {
            // addFirst(val);
            addLast(val);
        }
    
        public Object get(int index) {
            return getNode(index).val;
        }
    
        private Node getNode(int index) {
            Node node = head;
            for (int i = 0; i < index; i++) {
                // 将head的next节点重新赋值给node,继续查询下一个head的next节点,这样,index-1的next节点即为我们所需要的节点
                node = head.next;
            }
            return node;
        }
    
        public String toString() {
            StringBuilder str = new StringBuilder();
    
            Node node = head;
            while (node != null) {
                str.append(node.val).append(",");
                node = node.next;
            }
    
            return str.deleteCharAt(str.length() - 1).toString();
        }
    
        public void add(int index, Object val) {
            // 需要找到当前索引的节点
            try {
                if (index < 0 || index > size) {
                    throw new Exception("error");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (index == size) {
                addLast(val);
            } else if (index == 0) {
                addFirst(val);
            } else {
                Node currNode = getNode(index);
                Node pre = currNode.pre;
                Node newNode = new Node(pre, val, currNode);
                pre.next = newNode;
                currNode.pre = newNode;
                size++;
            }
        }
    
        public Object remove(int index) {
            // 找到索引对应的节点
            Node node = getNode(index);
            // 获取该节点的pre和next节点
            Node pre = node.pre;
            Node next = node.next;
    
            // 设置该节点的pre和next对应关系
            pre.next = next;
            next.pre = pre;
    
            node.next = null;
            node.pre = null;
    
            size--;
            return node.val;
        }
    
        public Object remove(Object val) {
            Node node = head;
            for (int i = 0; i < size; i++) {
                if (node.val.equals(val)) {
                    Node pre = node.pre;
                    Node next = node.next;
    
                    pre.next = next;
                    next.pre = pre;
                    break;
                }
                node = node.next;
            }
    
            node.pre = null;
            node.next = null;
            size--;
            return node.val;
        }
    
        public int getSize() {
            return size;
        }
    
    }
    View Code

    在本次模拟中,未考虑性能等问题,只是实现了简单的功能,了解下链表的结构及实现思路。还需要查看大神写的LinkedList源码,了解其实现精髓。

    ArrayList和LinkedList,前者查询和修改快,添加和删除慢,后者是查询,修改慢,添加删除快。

  • 相关阅读:
    (14)python函数与变量
    ①③python中的字符串与字符编码
    ①②python文件操作及文件增删改查
    rsa公钥私钥
    MySQL创建数据库和表
    Rsync + Innotify 部署实例
    LNMPT部署示例
    Nginx 调优
    Nginx 二进制方式安装
    wget & curl 命令
  • 原文地址:https://www.cnblogs.com/qq931399960/p/10904344.html
Copyright © 2020-2023  润新知