• LinkedList源码解读


    1.背景

    这一节来带着大家一起阅读LinkedList的源码

    2.先来认识一下什么是双向链表

    示例图:

    代码:

    package com.ldp.collection.demo01;
    
    import org.junit.Test;
    
    /**
     * @author 姿势帝-博客园
     * @address https://www.cnblogs.com/newAndHui/
     * @WeChat 851298348
     * @create 04/05 6:03
     * @description
     */
    public class Test04LinkedList {
        /**
         * 双向链表理解
         */
        @Test
        public void test01() {
            // 定义三个节点
            Node node1 = new Node("张无忌");
            Node node2 = new Node("赵敏");
            Node node3 = new Node("周芷若");
            // 将节点node1->node2->node3 依次使用双向链表链接
            // node1->node2
            node1.next = node2; // 节点1的下一个元素是节点2
            node2.prev = node1; // 节点2的上一个元素是节点1
            // node2->node3
            node2.next = node3; // 节点2的下一个元素是节点3
            node3.prev = node2; // 节点3的上一个元素是节点2
    
            Node first = node1;// 头节点为node1
            Node last = node3; // 尾节点为node3
    
            printNode(first, true); // 从头到尾
            printNode(last, false);    // 从尾到头遍历
    
            System.out.println("尾部添加一个元素:小昭");
            Node node4 = new Node("小昭");
            last.next = node4;
            node4.prev = last;
            last = node4;
            printNode(first, true);
    
            System.out.println("头部添加一个元素:张三丰");
            Node node5 = new Node("张三丰");
            node5.next = first;
            first.prev = node5;
            first = node5;
            printNode(first, true);
    
            // 在中间添加一个节点
            System.out.println("在node1[张无忌]与node2[赵敏]之前添加一个:node6[金毛狮王]");
            Node node6 = new Node("金毛狮王");
            node1.next = node6; // 张无忌的下一个是金毛狮王
            node6.prev = node1; // 金毛狮王的上一个是张无忌
    
            node6.next = node2; // 金毛狮王的下一个是赵敏
            node2.prev = node6; // 赵敏的上一个是金毛狮王
            printNode(first, true);
        }
    
        /**
         * 节点遍历
         *
         * @param node 遍历的节点
         * @param flag flag=true表示从头到尾遍历,flag=false从尾到头遍历
         */
        public void printNode(Node node, boolean flag) {
            System.out.println("节点遍历开始......");
            while (true) {
                if (node == null) break;
                System.out.println("当前节点数据为:" + node.item);
                if (flag) {
                    node = node.next; // 指向下一个节点,从头到尾遍历
                } else {
                    node = node.prev; // 指向上一个节点,从尾到头遍历
                }
            }
            System.out.println("   ");
        }
    }
    class Node {
        Node prev; // 上一个节点
        Object item; //  数据
        Node next;// 下一个节点
        public Node(Object item) {
            this.item = item;
        }
    }
    View Code

    3.源码解读

    代码中重点解读了add方法,与remove方法,get方法

    package com.ldp.collection.my;
    
    import java.util.*;
    
    /**
     * @author 姿势帝-博客园
     * @address https://www.cnblogs.com/newAndHui/
     * @WeChat 851298348
     * @create 04/05 6:51
     * @description
     */
    public class MyLinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
        transient int size = 0;
        transient Node<E> first;
        transient Node<E> last;
    
        public boolean add(E e) {
            linkLast(e);
            return true;
        }
    
        void linkLast(E e) {
            final Node<E> l = last;// 当前最后一个节点为 l
            final Node<E> newNode = new Node<>(l, e, null);// 新建立一个节点,上一为l,下一个为null
            last = newNode;// 设置最后一个节点为 新的节点
            if (l == null) // 最后一个节点为空(首次添加节点的时候),newNode是第一个节点也是最后一个节点
                first = newNode;
            else
                l.next = newNode;// 如果当前的最后一个节点不为空,那么它的下一个节点为newNode
            size++; // 节点数累加1
            modCount++; // 修改次数累加1
        }
    
        public E get(int index) {
            checkElementIndex(index); // 检查下标是否越界
            return node(index).item;// 根据下标取节点
        }
    
        /**
         * 检查下标是否越界
         */
        private void checkElementIndex(int index) {
            if (!isElementIndex(index))
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
        private String outOfBoundsMsg(int index) {
            return "Index: " + index + ", Size: " + size();
        }
    
        private boolean isElementIndex(int index) {
            return index >= 0 && index < size;
        }
    
        /**
         * 根据下标删除节点
         *
         * @param index
         * @return
         */
        public E remove(int index) {
            checkElementIndex(index);// 判断下标是否越界
            return unlink(node(index));
        }
    
        /**
         * 删除当前节点
         *
         * @param x
         * @return
         */
        E unlink(Node<E> x) {
            final E element = x.item;// x节点的元素值
            final Node<E> next = x.next; // x节点的下一个节点
            final Node<E> prev = x.prev; // x节点的上一个节点
            // 处理x节点的下一个节点的下一个节点
            if (prev == null) {// x节点的上一个节点为null,说明x节点就是第一个节点,删除x节点后,x的下一个节点为第一个节点
                first = next;
            } else {
                prev.next = next;
                x.prev = null;// 切断x的上一个节点
            }
            // 处理x节点的上一个节点的上一个节点
            if (next == null) {// x的下一个节点为null,说明x节点是最后一个节点,删除x节点后,x的上一个节点为最后一个节点
                last = prev;
            } else {
                next.prev = prev;
                x.next = null;// 切断x的下一个节点
            }
            x.item = null; // x节点的数据设置为空
            size--;// 长度减一
            modCount++; // 修改次数加一
            return element; // 返回删除的元素
        }
    
        /**
         * 根据下标取出节点
         *
         * @param index
         * @return
         */
        Node<E> node(int index) {
            if (index < (size >> 1)) { // size >> 1 表示长度除以2,如果下标小于长度的1/2则从头开始遍历
                Node<E> x = first;
                for (int i = 0; i < index; i++)// 循环遍历一直取下一个,直到指定的下标
                    x = x.next;// 下个表示从头遍历
                return x;
            } else { // 下标不小于长度的1/2则从尾开始遍历
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev; // 上一个表示从尾遍历
                return x;
            }
        }
    
        private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;
    
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
        }
    
        @Override
        public ListIterator<E> listIterator(int index) {
            return null;
        }
    
        @Override
        public void addFirst(E e) {
    
        }
    
        @Override
        public void addLast(E e) {
    
        }
    
        @Override
        public boolean offerFirst(E e) {
            return false;
        }
    
        @Override
        public boolean offerLast(E e) {
            return false;
        }
    
        @Override
        public E removeFirst() {
            return null;
        }
    
        @Override
        public E removeLast() {
            return null;
        }
    
        @Override
        public E pollFirst() {
            return null;
        }
    
        @Override
        public E pollLast() {
            return null;
        }
    
        @Override
        public E getFirst() {
            return null;
        }
    
        @Override
        public E getLast() {
            return null;
        }
    
        @Override
        public E peekFirst() {
            return null;
        }
    
        @Override
        public E peekLast() {
            return null;
        }
    
        @Override
        public boolean removeFirstOccurrence(Object o) {
            return false;
        }
    
        @Override
        public boolean removeLastOccurrence(Object o) {
            return false;
        }
    
        @Override
        public boolean offer(E e) {
            return false;
        }
    
        @Override
        public E remove() {
            return null;
        }
    
        @Override
        public E poll() {
            return null;
        }
    
        @Override
        public E element() {
            return null;
        }
    
        @Override
        public E peek() {
            return null;
        }
    
        @Override
        public void push(E e) {
    
        }
    
        @Override
        public E pop() {
            return null;
        }
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public Iterator<E> descendingIterator() {
            return null;
        }
    }
    View Code

    测试代码:

     @Test
        public void test02() {
            // LinkedList list = new LinkedList();
            MyLinkedList list = new MyLinkedList();
            list.add("张无忌");
            list.add("赵敏");
            list.add("张无忌");
            list.add("周芷若");
            System.out.println("size=" + list.size());
            System.out.println("获取index=3的元素"+list.get(3));//周芷若
            // System.out.println("list=" + list);// 因这个方法没有重写listIterator(int index),不能使用迭代器
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
            System.out.println("删除后index=3的元素");
            list.remove(3);
            System.out.println("删除后的list");
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
        } 

    完美!

  • 相关阅读:
    像画笔一样慢慢画出Path的三种方法(补充第四种)
    占位符行为 PlaceHolderBehavior 的实现以及使用
    WPF实现物理效果 拉一个小球
    WPF实现Twitter按钮效果
    WPF自适应可关闭的TabControl 类似浏览器的标签页
    WPF仿百度Echarts人口迁移图
    WPF绘制简单常用的Path
    51Nod 1534 棋子游戏
    数论基础
    Buy a Ticket
  • 原文地址:https://www.cnblogs.com/newAndHui/p/16103353.html
Copyright © 2020-2023  润新知