【震精】LinkedList源码竟然可以这样玩!!

640?wx_fmt=jpeg


原文:转载自公众号【乱敲代码】

1.介绍

LinkedList 是线程不安全的,允许元素为null的双向链表。就这么多。

640?wx_fmt=png

2.继承结构

我们来看一下LinkedList的继承结构图:640?wx_fmt=png代码实现:

public class LinkedList<E>	
    extends AbstractSequentialList<E>	
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • Cloneable实现克隆

  • Serializable序列化

  • List 定义了一些集合类的方法

  • Deque双向队列接口(就是两端都可以进行增加删除操作)

注意一点LinkedList并没有实现RandomAccess所以随机访问是非常慢的。

3.属性

元素个数

transient int size = 0;

指向第一个节点的指针(注释直接就写着)

  /**	
     * Pointer to first node.	
     * Invariant: (first == null && last == null) ||	
     *            (first.prev == null && first.item != null)	
     */	
    transient Node<E> first;

指向最后一个节点的指针

    /**	
     * Pointer to last node.	
     * Invariant: (first == null && last == null) ||	
     *            (last.next == null && last.item != null)	
     */	
    transient Node<E> last;

transient关键字的作用是保持变量不被序列化具体的百度。

640?wx_fmt=png不过我们注意到LinkedList是有Node类,这里的Node是一个内部类:

640?wx_fmt=png

  • item存储的元素

  • next指向后置节点

  • prev指向前置节点

  • 内部类同时包含了一个构造函数

其实LinkedList 集合就是由许多个 Node 对象一个连着一个组成手构成,可以看下方的图640?wx_fmt=png可以看上面的四个元素,分别就是四个Node对象,可以看到node1所指向的prev上一个节点是空也就是没有上节点,node4所指向的next节点为空也就是没有下一个节点。

4.构造方法

640?wx_fmt=pngLinkedList共有两个构造方法,一个是创建一个空的构造函数,一个是将已有的Collection添加到LinkedList中。
因为LinkedList不同于ArrayList所以初始化不需要指定大小取分配内存空间。

5.添加元素

LinkedList有几种添加方法我们先从add()开始。

5.1 add方法

640?wx_fmt=png

checkPositionIndex(index);

可以看到在调用Add方法首先校验一下索引是否合法,如果不合法就会抛出异常。

if (index == size)	
            linkLast(element);	
        else	
            linkBefore(element, node(index));

然后如果索引值等于size的值则直接调用linkLast插入到尾部节点。
否则就linkBefore方法。linkLast方法:

void linkLast(E e) {	
        //将l设为尾节点	
        final Node<E> l = last;	
        //构造一个新节点,节点上一个节点引用指向尾节点l	
        final Node<E> newNode = new Node<>(l, e, null);	
        //将尾节点设为创建的新节点	
        last = newNode;	
        //如果尾节点为空,表示原先链表为空	
        if (l == null)	
        //将头节点设为新创建的节点(尾节点也是新创建的节点)	
            first = newNode;	
        else	
        //将原来尾节点下一个节点的引用指向新节点	
            l.next = newNode;	
        size++;//节点数加1	
        modCount++;	
    }

注意一下这里出现了modCount方法,和ArrayList中一样,iterator和listIterator方法返回的迭代器和列表迭代器实现使用。linkBefore:

void linkBefore(E e, Node<E> succ) {	
        //将pred设为插入节点的上一个节点	
        final Node<E> pred = succ.prev;	
        //将新节点的上引用设为pred,下引用设为succ	
        final Node<E> newNode = new Node<>(pred, e, succ);	
        //succ的上一个节点的引用设为新节点	
        succ.prev = newNode;	
        //如果插入节点的上一个节点引用为空	
        if (pred == null)	
        //新节点就是头节点	
            first = newNode;	
        else	
        //插入节点的下一个节点引用设为新节点	
            pred.next = newNode;	
        size++;	
        //同上	
        modCount++;	
    }

5.2 addAll()

在LinkedList中有两个addAll方法一个是 addAll(Collection c)一个是addAll(int index, Collection c),其实
addAll(Collection c)=addAll(int index, Collection c)
可以看下面的截图:640?wx_fmt=png源码如下:

640?wx_fmt=png现在开始分析源码:首先我们可以看到先调用了checkPositionIndex(index);方法来检查索引是否合法。然后将传进来的Collection转成Object数组

 Object[] a = c.toArray();

如果集合为空就直接返回false

if (numNew == 0)	
            return false;

如果插入的位置等于链表的长度就把新增加的元素放在尾部。

Node<E> pred, succ;	
        if (index == size) {	
            succ = null;	
            pred = last;	
        } else {	
            succ = node(index);	
            pred = succ.prev;	
        }

最后在循环要插入的元素。这里我们可以看到上面也有modCount。

5.3 addFirst()

addFirst是将元素插入到LinkedList的头部。如果现在的结构是下图的:640?wx_fmt=pngaddFirst执行的操作就是:640?wx_fmt=png红色是使用addFirst插入后的位置。一下是源码

640?wx_fmt=png调用了linkFirst方法。640?wx_fmt=png上面的操作时:

  • 首先执行final Node f = first;将头节点赋值给 f

  • final Node newNode = new Node<>(null, e, f);将指定元素构造成一个新节点,此节点的指向下一个节点的引用为头节点

        //将新节点设为头节点,那么原先的头节点 f 变为第二个节点	
        first = newNode;	
        //如果第二个节点为空,也就是原先链表是空	
         if (f == null)	
         //将这个新节点也设为尾节点(前面已经设为头节点了)	
          last = newNode;	
       else	
            f.prev = newNode;//将原先的头节点的上一个节点指向新节点	
       size++;//节点数加1	
       modCount++;//和ArrayList中一样记录修改次数

5.4 addLast方法

addLast是将元素插入到尾部如图(黄色元素):

640?wx_fmt=png黄色node是新增加。注意add也是把元素添加到尾部。我们来看一下源码:

640?wx_fmt=png这里看到addLast和add是调用的同一方法,这里就不在讲解。可以看上方的代码。


版权声明:本文为z694644032原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/z694644032/article/details/98702926

智能推荐

CSS3边框和圆角 学习打卡

课程介绍 1、CSS3圆角 2、CSS3盒阴影 3、CSS3边界图片 CSS3圆角 1、border-radius:一个最多可以指定四个border-*-radius属性的复合属性,为元素添加圆角边框 2、语法:border-radius:1-4 length|%/1-4 length|% 3、兼容:IE9+ firefox4+ chrome safari5+ opera CSS3指定每一个圆角 ...

(Java)反射的应用 - 取得类的结构

文章目录 一、基本概念 二、取得所实现的全部接口 三、取得父类 四、取得全部构造方法 五、取得全部方法 六、取得全部属性 一、基本概念 在反射机制中,还可以通过反射得到一个类的完整结构,这就需要使用 java.lang.reflect 包中的以下几个类: 这三个类都是 AccessibleObject 类的子类: 二、取得所实现的全部接口 要取得一个类所实现的全部接口,必须使用 Class 类中的...

ORM-外键关联基本使用

外键 在Mysql中,外键可以让表之间关系变得更加紧密, 在SQlAlchemy中, 通过ForeignKey类来实现,并且可以指定表的外键约束 FroeignKey的导入 在从表中条件一个模型类.字段(属性)即可 外键关联的代码和示例图 图说明 外键约束的删除 如果删除了主表中的数据, 从表的数据会怎么样? 需要设置 "RESTRICT" : 主表数据被删除, 会阻止删除 &...

放大镜效果

首先先写html样式 接下来是css部分 js部分 效果图...

Linux操作心得(1)

Ubuntu 16.04 (1)今天遇到一个蜜汁尴尬的情况,一本书上的示例,要求我建一个文件夹及子文件夹,然而明明创建的文件却没有显示 按书上此时应该出现一个文件夹,但并没有: 但可以进入,作为小白看不懂,后来发现是因为/XX指的是将文件建立在根目录了,因此不管怎样,就算用ls,或ll命令都查不到的,此时正确方法应该是去掉/backup前的/,如图就解决了文件夹的创建过程,还有一种傻瓜式方法就是直...

猜你喜欢

如何写出优美的 JavaScript 代码?

作者:尹锋 链接:https://www.zhihu.com/question/20635785/answer/223515216 1,避免使用 js 糟粕和鸡肋 这些年来,随着 HTML5 和 Node.js 的发展,JavaScript 在各个领域遍地开花,已经从“世界上最被误解的语言”变成了“世界上最流行的语言”。但是由于历史原因,JavaSc...

07-zookeeper的watcher机制原理

zookeeper的watcher机制原理 Watcher 的基本流程 zookeeper的watcher机制,总的来说可以分为三个过程: 客户端注册Watcher。 服务器处理Watcher。 客户端回调Watcher。 客户端注册 watcher有3种方式,getData、exists、getChildren。以如下代码为例,来分析整个触发机制的原理 基于zkclient客户端发起一个数据操作...

Linux搭建Nexus私服

Nexus是什么 Nexus是一个强大的Maven仓库管理器,它极大地简化了自己内部仓库的维护和外部仓库的访问。利用Nexus你可以只在一个地方就能够完全控制访问 和部署在你所维护仓库中的每个Artifact。Nexus是一套“开箱即用”的系统不需要数据库,它使用文件系统加Lucene来组织数据。简单来说,它就是我们自己维护管理的maven仓库,仅限本人或公司内部使用,他人...

【Elastic Stack上】Elastic Search快速入门,让你对ELK日志架构不再困惑

课程介绍 Elastic Stack简介 Elasticsearch的介绍与安装 Elasticsearch的快速入门 Elasticsearch的核心讲解中文分词 全文搜索 Elasticsearch集群 Java客户端讲解 1、Elastic Stack简介 如果你没有听说过Elastic Stack,那你一定听说过ELK,实际上ELK是三款软件的简称,分别是Elasticsearch、Log...

浅谈Java中==和equals()区别

Java基础 浅谈Java中==和equals()区别 == 运算符 equals(): 方法 浅谈Java中==和equals()区别 == 运算符 可以使用在基本数据类型变量和引用数据类型变量中 如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等(不一定要类型相同) 如果比较的是引用类型变量,比较的是两个变量的地址值是否相同,即两个引用是否指向同一个对象实体 equals(): 方法...