LinkedList浅析

LinkedList浅析LinkedList简介LinkedList是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList实现List接口,能对它进行队列操作。LinkedList实现Deque接口,即能将LinkedList当作双端队列使用。LinkedList实现了Cloneable接口,即覆盖了函数clon…

大家好,又见面了,我是你们的朋友全栈君。


LinkedList是Collection下的一个list实现,就像ArrayList一样。

和ArrayList不同的是它是链表结构,而ArrayList是顺序结构。我们平常使用的list是一样的,理论上来说一种list就可以完成我们所有的需求。但是它们在运行过程中有区别的,完成需求所需要的资源也不相同,至于什么情况下使用哪种list就看需求和当前情况了。


视频:


LinkedList 是一个继承于AbstractSequentialList的双向链表
LinkedList 可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,所以能对它进行队列操作
LinkedList 实现 Deque 接口,能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,所以LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。

java集合

构造函数

// 默认构造函数
LinkedList()

// 创建一个LinkedList,保护Collection中的全部元素。
LinkedList(Collection<? extends E> collection)

LinkedList的本质是双向链表

  1. LinkedList继承于AbstractSequentialList,并且实现了Dequeue接口。
  2. LinkedList包含两个重要的成员:header 和 size。
      header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。
      size是双向链表中节点的个数。

##双向链表结构

  • 链表:链表是一种重要的数据结构,有单链表和双链表之分

单链表:

单链表(单向链表):由两部分组成 数据域(Data)和结点域(Node),单链表就像是一条打了很多结的绳子,每一个绳结相当于一个结点,每个节结点间都有绳子连接,这样原理的实现是通过Node结点区的头指针head实现的,每个结点都有一个指针,每个节点指针的指向都是指向自身结点的下一个结点,最后一个结点的head指向为null,这样一来就连成了上述所说绳子一样的链,对单链表的操作只能从一端开始,如果需要查找链表中的某一个结点,则需要从头开始进行遍历。

双向链表结构

双链表(双向链表):双链表和单链表相比,多了一个指向尾指针(tail),双链表的每个结点都有一个头指针head和尾指针tail,双链表相比单链表更容易操作,双链表结点的首结点的head指向为null,tail指向下一个节点的tail;尾结点的head指向前一个结点的head,tail 指向为null,是双向的关系;
在单链表中若需要查找某一个元素时,都必须从第一个元素开始进行查找,而双向链表除开头节点和最后一个节点外每个节点中储存有两个指针,这连个指针分别指向前一个节点的地址和后一个节点的地址,这样无论通过那个节点都能够寻找到其他的节点。

  • 插入删除不需要移动元素外,可以原地插入删除
  • 可以在结构的前后插入数据
  • 可以双向遍历

###链表


AbstractSequentialList

Linklist是AbstractSequentialList的子类

AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些函数。这些接口都是随机访问List的,LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”。
此外,我们若需要通过AbstractSequentialList自己实现一个列表,只需要扩展此类,并提供 listIterator() 和 size() 方法的实现即可。若要实现不可修改的列表,则需要实现列表迭代器的 hasNext、next、hasPrevious、previous 和 index 方法即可。

LinkedList的继承关系:

java.lang.Object
   ↳ java.util.AbstractCollection<E>
         ↳ java.util.AbstractList<E>
               ↳ java.util.AbstractSequentialList<E>
                     ↳  java.util.LinkedList<E>

    public class LinkedList<E>
         extends AbstractSequentialList<E>
            implements List<E>, Deque<E>, Cloneable, java.io.Serializable { …… }

LinkedList与Collection关系

Collection:

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。

LinkedList浅析


LinkedList的本质是双链表,实现 List 和 Deque接口:

LinkedList浅析

在LinkedList中,每个节点都用内部类Node表示:

LinkedList浅析

具体的过程可以看下面这张图:
点我

每个node都是节点,里面有三个属性,分别指向上一个节点、下一个节点、实际储存元素开辟的内存空间

而对linkedlist里元素的操作方法都是对这些节点进行操作:

add()操作:

 /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

  /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

remove()操作

    /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

	//查找对应索引
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * Unlinks non-null node x.
     */
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }


可以看到都是对node节点进行操作,真实的实例内存并没有任何改变,等着jvm回收。


List使用场景

ArrayList在随机访问方面比较擅长,LinkedList在随机增删方面比较擅长
对于需要快速插入,删除元素,使用LinkedList。因为ArrayList要想在数组中任意两个元素中间添加对象时,数组需要移动所有后面的对象。
对于需要快速随机访问元素(get()),应该使用ArrayList,因为LinkedList要移动指针、遍历节点来定位,所以速度慢。
对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。
对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。

对于插入数据使用时间的对比:

  public static void addTest(){

        //批量插入,每次都向首位插入数据;

        LinkedList linkedList = new LinkedList();

        long time1 = new Date().getTime();

        for(int m=0;m<300000;m++){
            linkedList.add(0,null);
        }

        long time2 = new Date().getTime();

        System.out.print("linkedList批量插入时间:"+(time2 - time1)+"ms\n");




        ArrayList arraylist = new ArrayList();

        long time3 = new Date().getTime();

        for(int n=0;n<300000;n++){
            arraylist.add(0, null);
        }

        long time4 = new Date().getTime();
        System.out.print("arrayList批量插入时间:"+(time4 - time3)+"ms\n");

    }

结果:

linkedList批量插入时间:9ms
arrayList批量插入时间:3696ms


那么问题就来了:

Q:什么时候使用Arraylist,什么时候使用LinkedList?
A:当你需要频繁查询数组的时候使用ArrayList比较快,当你需要频繁操作数组进行增删插入操作的时候使用LinkList比较合适。当然直接在末尾添加数据ArrayList用时也不是特别多,因为在末尾操作后面没有数据。

Q:那什么时候适合用list呢
A:涉及到“栈”、“队列”、“链表”等操作,应该考虑用List,具体的选择哪个List,根据下面的标准来取舍

  • 对于需要快速插入,删除元素,应该使用LinkedList。
  • 对于需要快速随机访问元素,应该使用ArrayList。
  • 对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。
  • 对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。

Q:线程安全的数组,什么是线程安全?
A:线程安全就是当前资源只能被单独一个线程访问,也就是加同步锁的线程。默认的线程安全的list是Vector;


以上,如果有哪里不对或者不清楚的地方欢迎勾搭,多谢指正

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148212.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月29日 下午12:16
下一篇 2022年6月29日 下午12:16


相关推荐

  • seaJs 使用

    seaJs 使用

    2021年9月16日
    57
  • 详解IPv4地址

    详解IPv4地址IPv4 地址范围 0 0 0 0 255 255 255 255 约有 43 亿 计算机的 IP 地址由两部分组成 一部分为网络位 一部分为主机位 同一网段内的计算机网络部分相同 主机部分不同同时重复出现 路由器连接不同网段 负责不同网段之间的数据转发 交换机连接的是同一网段的计算机 通过设置网络地址和主机地址 在互相连接的整个网络中保证每台主机的 IP 地址不会互相重叠 即 IP 地址具有了唯一性 网络掩码 用于区分一个 IP 地址的网络部分和主机部分 IP 地址根据网络 ID 的不同分为 A

    2026年3月19日
    2
  • ssm框架整合步骤_bootstrap框架搭建

    ssm框架整合步骤_bootstrap框架搭建描述普通情况下,控制台打印出的sql是带?的,开发人员在自己调试的过程中遇到bug是再常见不过了,如果想看一下程序打出来的sql,发现参数的位置都是?,是不是很不爽,若想在数据库中执行sql,还需要手动修改sql的参数,真是麻烦,使用p6spy可以将sql格式化然后打出,打出的sql不再是带有?的,而是可以直接拷贝到数据库执行的。整合步骤如下:添加jar包依赖在service层…

    2022年10月5日
    5
  • itext7学习笔记——第5章

    itext7学习笔记——第5章前言在之前的第 1 到第 3 章 我们总是从头开始用 iText 创建一个新的 PDF 文档 在第 4 章的最后几个例子中 我们使用了一个现有的 PDF 文档 利用现有的 PDF 来读取表单并填写了自己的表单或者预填充定义的表单 在本章 我们会使用 PdfReader 读取一个存在的 PDF 文件 或者使用 PdfWriter 对象来创建一个新的 PdfDocument 添加注释和内容在前面的章节中 我们读取了一个带有表单的

    2026年3月17日
    2
  • phpMyAdmin安装图解教程

    phpMyAdmin安装图解教程 先下载一份最新版的phpMyAdminMYSQL管理器,点击下载phpMyAdmin解压后得到一个phpMyAdmin的目录(你可以改名)找到/libraries/config.default.php文件(旧版本是根目录下的config.inc.php文件),用写字板(不要用记事本,这是UTF8编码)进行编辑。找到$cfg['PmaAbsoluteUri']

    2022年5月31日
    108
  • 测试平台开发(二) 高逼格登录页面

    测试平台开发(二) 高逼格登录页面

    2020年11月20日
    198

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号