Java入门系列之集合LinkedList入门(八)

前言

前面两节内容我们详细介绍了ArrayList,一是手写实现ArrayList数据结构,而是通过分析ArrayList源码看看内置实现,关于集合内容一如既往,本节课我们继续学习集合LinkedList,我们首先入门LinkedList数据结构,然后再去看看LinkedList源码是如何实现的,我们开始吧。

LinkedList入门

LinkedList内置是通过双链表数据结构来存储数据,和ArrayList不同的是,ArrayList属于真正意义物理意义上的线性结构,而LinkedList也属于线性链表,只不过需要通过我们手动来关联前后节点数据,同时呢,双链表和单链表只是在结构上有所不同而已,只是双链表多了一个前驱节点,其他无差异,那么到底何为双链表呢?在我们日常生活中到处都是这样的例子,比如我们音乐播放器应该算比较形象了,如下:

单链表自定义实现

接下来我们来实现单链表,然后对单链表进行改造成双链表,我们看到如上播放器,单链表只是少了前驱节点,但是有后继节点(如上写错了),所以我们需要定义一个节点,然后在此节点上有连接下一节点的引用(在C或C++中为指针),和当前节点所存储的数据,所以我们定义如下泛型节点类:

public class Node<T> {

    //当前节点值
    public T data;

    //后继节点
    public Node next;

    public Node(T data) {
        this.data = data;
    }
}

接下来则是定义链表来操作上述节点类并存储数据了, 这里我们稍微做的简单点,在链表中会存在头节点和尾节点,这里呢我们通过来头节点来操作,等我们升级到双链表时再来定义尾节点,所以在单链表中有头节点和链表长度两个变量,如下:

public class MyLinkedList<T> {

    //头节点
    private Node head;

    //链表元素长度
    private int length;

}

温馨提示:这里我就不给大家画图演示了,自行脑补,实在感觉绕的话自己在画板或纸上画一下就明白了,我也是在纸上画了一番才动手写代码的。首先我们需要考虑头节点和尾节点即播放器中第一首歌和最后一首歌,然后针对指定位置添加歌曲通过next串联就形成了歌曲列表,更为形象的例子当属我们吃过的串串了。那么接下来我们完成往播放器列表中添加第一个首歌,此时我们应该想,头节点是否添加了第一首歌,若不存在则直接实例化头节点即可,若已存在第一首歌,我们则将重新实例化一首歌,然后将其已添加的第一首歌的引用赋值给新添加的歌曲的next,所以就有了如下方法:

//添加至头结点
public void addToHead(T data) {
        if (head == null) {
            head = new Node(data);
        } else {
            Node temp = head;
            head = new Node(data);
            head.next = temp;
        }
        length++;
}

好了,将新添加的歌曲放在第一首我们已经完全搞定了,然后我们再来往歌曲列表中最后添加一首歌曲,这个时候我们肿么知道是最后一首呢,只要next为空,说明就是最后一首歌曲,这就是判断依据,这点就不用我再过多解释了,那么就有了如下方法:

//添加至尾节点
public void addToTail(T data) {
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = new Node(data);
        length++;
}

单链表的确定就在这里,我们只能循环遍历才能找到最后一首,然后添加对应歌曲,所以当数据量足够大时,可想其性能。接下来则是最重要的一块了,我们想要在指定歌曲下添加歌曲,这个时候就涉及到找到对应歌曲索引然后添加数据,

    //添加到指定索引元素
    public void add(int index, T data) {
        if (index < 0) {
            throw new RuntimeException("非法索引");
        }
        if (index > length) {
            throw new RuntimeException("超出索引边界");
        }
        if (head == null || index == 0) {
            addToHead(data);
            return;
        }
        //头节点
        Node temp = head;
        //指定索引下一节点
        Node holder;
        for (int i = 0; i < index - 1 && temp.next != null; i++) {
            temp = temp.next;
        }
        //未插入节点时指定索引下一节点
        holder = temp.next;
        //指定索引节点下一节点即待插入的节点
        temp.next = new Node(data);
        //将列表中指定索引节点下一节点引用指向指定待插入节点(此时指定索引下节点即为待插入节点,然后再下一节点即为待插入节点)
        temp.next.next = holder;
        length++;
    }

接下来则是根据指定索引查找元素,我就不解释了,直接上代码,如下

    //根据索引查找元素
    public T find(int index) {
        if (index < 0) {
            throw new RuntimeException("非法索引");
        }
        if (length == 0 || index > length) {
            throw new RuntimeException("超出索引边界");
        }
        Node temp = head;
        for (int i = 0; i < index; i++) {
            temp = temp.next;
        }
        return (T) temp.data;
    }

最后老规矩重写toString方法,打印链表数据,如下:

    //链表元素大小
    public int size() {
        return length;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node temp = head;
        while (temp != null) {
            sb.append(temp.data);
            sb.append(",");
            temp = temp.next;
        }
        if (sb.charAt(sb.length() - 1) == ‘,‘) {
            sb.delete(sb.length() - 1, sb.length());
        }
        return sb.toString();
    }

最后我们来往播放器列表中添加歌曲做个测试吧,走你,如下:

public class Main {

    public static void main(String[] args) {
        MyLinkedList<Integer> list = new MyLinkedList<>();
        //添加元素11到头节点
        list.addToHead(11);
        System.out.println(list);

        //添加元素15到尾节点
        list.addToTail(15);
        System.out.println(list);

        //添加元素12到头节点
        list.addToHead(12);
        System.out.println(list);

        //添加元素13到头节点
        list.addToHead(13);
        System.out.println(list);

        //添加元素8到尾节点
        list.addToTail(8);
        //添加元素7到尾节点
        list.addToTail(7);
        list.add(2, 9);
        System.out.println(list);

        //在索引2位置添加元素9
        list.add(2, 9);
        System.out.println(list);

        //删除索引为4的元素
        list.delete(4);
        System.out.println(list);
    }
}

双链表自定义实现

有了如上单链表的铺垫,接下来我们再来实现双链表则是轻而易举了,只不过添加了前驱节点和链表中的尾结点而已,走你,我们往节点类中添加前驱节点,如下:

public class Node<T> {

    //当前节点值
    public T data;

    //前驱节点
    public Node previous;

    //后继节点
    public Node next;

    public Node(T data) {
        this.data = data;
    }
}

同理,我们在链表类中添加尾节点字段,如下:

public class MyLinkedList<T> {
    //头节点
    private Node head;

    //尾节点
    private Node tail;

    //链表元素长度
    private int length;
}

同样,当添加歌曲至首位时,此时我们也需初始化头节点,只不过这时多了个尾节点,没关系,这个时候头节点就是尾节点,我们封装一个初始化头节点和尾节点的方法,如下:

 //初始化头接点和尾节点
    void initHead(T data) {
        //初始化头节点
        head = new Node(data);
        //此时尾节点即头节点
        tail = head;
}

然后添加歌曲至头节点时,只不过多了个前驱节点,也就相应多了一行代码而已,就是将已添加首位歌曲的前驱节点赋给待添加的首位歌曲,如下:

    //添加元素至头结点
    public void addToHead(T data) {
        if (head == null) {
            initHead(data);
        } else {
            Node temp = head;
            head = new Node(data);
            head.next = temp;
            temp.previous = head;
        }
        length++;
    }

而添加歌曲至末位时就和上述单链表就有些不同了,单链表中是直接循环遍历,这里我们定义了尾节点,所以直接操作尾节点即可,如下:

    //添加至尾节点
    public void addToTail(T data) {
        if (size() == 0) {
            initHead(data);
        } else {
            Node temp = tail;
            tail = new Node(data);
            temp.next = tail;
            tail.previous = temp;
        }
        length++;
    }

接下来又是添加指定索引元素的核心方法了,其实也非常简单,我都将注释给你写好了,还是看不懂,建议到纸上画画哈。

    //添加指定索引元素
    public void add(int index, T data) {
        if (index < 0) {
            throw new RuntimeException("非法索引");
        }
        if (index > length) {
            throw new RuntimeException("超出索引边界");
        }
        if (head == null || index == 0) {
            initHead(data);
            return;
        }
        //头节点
        Node temp = head;
        //定义获取指定索引节点下一节点
        Node holder;
        for (int i = 0; i < index - 1 && temp.next != null; i++) {
            temp = temp.next;
        }
        //当前节点的下一节点
        holder = temp.next;
        //要添加的下一节点
        temp.next = new Node(data);
        //插入节点的后继节点为当前节点下一节点
        temp.next.next = holder;
        //当前节点下一前驱节点为插入节点
        temp.next.next.previous = temp.next;
        length++;
    }

无论是添加还是删除最重要的是我们需要想清楚,添加时和删除后前驱节点和后继节点分别指向谁,把这个问题想明白了,那也就没什么了,走你,删除方法:

    //删除指定索引元素
    public void delete(int index) {
        if (index < 0) {
            throw new RuntimeException("非法索引");
        }
        if (length == 0 || index > length) {
            throw new RuntimeException("超出索引边界");
        }
        Node temp = head;
        for (int i = 0; i < index - 1 && temp.next != null; i++) {
            temp = temp.next;
        }
        temp.next.next.previous = temp;
        temp.next = temp.next.next;
        length--;
    }

为了验证我们所写代码,我们打印出对应节点的前驱和后继节点,如下:

    public int size() {
        return length;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node temp = head;
        while (temp != null) {
            sb.append(temp.data);
            sb.append(",");
            if (temp.previous != null && temp.next != null) {
                System.out.println(temp.previous.data + "<-(" + temp.data + ")->" + temp.next.data);
            }
            temp = temp.next;
        }
        if (sb.charAt(sb.length() - 1) == ‘,‘) {
            sb.delete(sb.length() - 1, sb.length());
        }
        return sb.toString();
    }

控制台测试数据和单链表中一样,结果数据如下(当然我们可以分开打印对应节点前驱和后继节点去验证也是阔以的,这里我也验证过来,么有任何问题):

总结

本节我们通过手写代码实现了单链表和双链表,还是非常简单,下一节我们详细分析LinkedList源码,感谢您的阅读,我们下节见

原文地址:https://www.cnblogs.com/CreateMyself/p/11455832.html

时间: 2024-11-04 11:49:33

Java入门系列之集合LinkedList入门(八)的相关文章

Java多线程系列--“JUC集合”03之 CopyOnWriteArraySet

概要 本章是JUC系列中的CopyOnWriteArraySet篇.接下来,会先对CopyOnWriteArraySet进行基本介绍,然后再说明它的原理,接着通过代码去分析,最后通过示例更进一步的了解CopyOnWriteArraySet.内容包括:CopyOnWriteArraySet介绍CopyOnWriteArraySet原理和数据结构CopyOnWriteArraySet函数列表CopyOnWriteArraySet源码(JDK1.7.0_40版本)CopyOnWriteArraySet

「数据挖掘入门系列」Python快速入门

Python环境搭建 本次入门系列将使用Python作为开发语言.要使用Python语言,我们先来搭建Python开发平台.我们将基于Python 2.7版本.以及Python的开发发行版本Anaconda版本来开发. Anaconda指的是一个开源的Python发行版本,其包含了conda.Python等180多个科学包及其依赖项. 下载地址: https://www.anaconda.com/distribution/,注意要下载2.7版本 下载好Anaconda安装包后,即可安装,安装好后

WPF快速入门系列(8)——MVVM快速入门

一.引言 在前面介绍了WPF一些核心的内容,其中包括WPF布局.依赖属性.路由事件.绑定.命令.资源样式和模板.然而,在WPF还衍生出了一种很好的编程框架,即WVVM,在Web端开发有MVC,在WPF客户端开发中有MVVM,其中VM就相当于MVC中C(Control).在Web端,微软开发了Asp.net MVC这样的MVC框架,同样在WPF领域,微软也开发了Prism这样的MVVM框架.Prism项目地址是:http://compositewpf.codeplex.com/SourceCont

Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList

概要 本章是"JUC系列"的CopyOnWriteArrayList篇.接下来,会先对CopyOnWriteArrayList进行基本介绍,然后再说明它的原理,接着通过代码去分析,最后通过示例更进一步的了解CopyOnWriteArrayList.内容包括:CopyOnWriteArrayList介绍CopyOnWriteArrayList原理和数据结构CopyOnWriteArrayList函数列表CopyOnWriteArrayList源码分析(JDK1.7.0_40版本)Copy

Java入门系列之集合Hashtable源码分析(十一)

前言 上一节我们实现了散列算法并对冲突解决我们使用了开放地址法和链地址法两种方式,本节我们来详细分析源码,看看源码中对于冲突是使用的哪一种方式以及对比我们所实现的,有哪些可以进行改造的地方. Hashtable源码分析 我们通过在控制台中实例化Hashtable并添加键值对实例代码来分析背后究竟做了哪些操作,如下: public static void main(String[] args) { Hashtable hashtable = new Hashtable(); hashtable.p

Java入门系列之集合HashMap源码分析(十四)

前言 我们知道在Java 8中对于HashMap引入了红黑树从而提高操作性能,由于在上一节我们已经通过图解方式分析了红黑树原理,所以在接下来我们将更多精力投入到解析原理而不是算法本身,HashMap在Java中是使用比较频繁的键值对数据类型,所以我们非常有必要详细去分析背后的具体实现原理,无论是C#还是Java原理解析,从不打算一行行代码解释,我认为最重要的是设计思路,重要的地方可能会多啰嗦两句. HashMap原理分析 我们由浅入深,循序渐进,首先了解下在HashMap中定义的几个属性,稍后会

(Java多线程系列一)快速入门

Java多线程快速入门 1.线程和进程的区别 进程是所有线程的集合,每一个线程是进程的一条执行路径. 2.多线程的应用场景 多线程主要体现在提高程序的效率,比如迅雷多线程下载,多线程分批发送短信等. 3.多线程的创建方式 (1)继承Thread类,重写run()方法 class ThreadDemo extends Thread { @Override public void run() { System.out.println("这里是子线程"); System.out.printl

JAVA通信系列二:mina入门总结

一.学习资料 Mina入门实例(一) http://www.cnblogs.com/juepei/p/3939119.html Mina入门教程(二)----Spring4 集成Mina http://www.cnblogs.com/juepei/p/3940396.html Apache Mina 入门实例--创建一个MINA时间服务http://loftor.com/archives/apache-mina-quick-start-guide.html MINA2.0用户手册中文版--系列文

JAVA通信系列三:Netty入门总结

一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhttp://wenku.baidu.com/view/7765bc2db4daa58da0114a4c.html Netty了解与小试 http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561318.html Netty系列之Netty高性能之道[精彩]htt