回顾集合体系中LinkedList的源码实现,查看的源码为Android系统中的java源码实现,和JDK中的实现可能有一定区别。

  • 成员变量
  • 构造方法
  • 元素节点Node
  • add方法
  • push方法
  • get方法
  • set方法
  • remove方法
  • pop方法
  • size方法
  • isEmpty方法
  • indexOf方法
  • lastIndexOf方法
  • contain方法
  • clear方法
  • 总结

成员变量

    //存储的元素数量transient int size = 0;//队首元素节点transient Node<E> first;//队尾元素节点transient Node<E> last;

构造方法

//无参数构造public LinkedList() {}//传入集合的构造器LinkedList(Collection<? extends E> c) {this();addAll(c);//最终调用了addAll方法,后面分析}

元素节点Node

使用Node类实现双链表设计:

    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;}}

add方法

//添加指定元素到队尾public boolean add(E e) {linkLast(e);//调用linkLast方法添加return true;}//将指定元素链接到队尾void linkLast(E e) {final Node<E> l = last;//当前的队尾元素//将当前的队尾last对象和要添加的元素e封装成newNode,使其//newNode的prev成员变量持有last对象,这样完成向前的链接final Node<E> newNode = new Node<>(l, e, null);last = newNode;//然后将newNode赋值给last,完成队尾更新if (l == null)//如果last对象是null,说明还没有任何元素添加进来first = newNode;//直接将newNode赋值给first,也就是队首节点elsel.next = newNode;//不是null,就把last的next的引用指向newNode,完成向后的链接size++;//size自增modCount++;//修改标识自增}

在指定位置插入元素:

    //在指定的索引插入元素 public void add(int index, E element) {checkPositionIndex(index);//检查索引是否合法if (index == size)//如果index和size相同,那么直接插入队尾linkLast(element);else//如果index和size不相同,那么调用linkBefore插入linkBefore(element, node(index));}//取出指定索引存储的Node信息Node<E> node(int index) {// assert isElementIndex(index);//如果index小于size/2,那就从0遍历到index-1,返回xif (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {//如果index大于size/2,那就从size-1向前遍历到index+1,返回xNode<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}//在指定的节点前插入给定的元素void linkBefore(E e, Node<E> succ) {// assert succ != null;final Node<E> pred = succ.prev;//先取出节点的前一个节点对象//然后将前后节点和当前元素封装成newNode,建立双向链接final Node<E> newNode = new Node<>(pred, e, succ);succ.prev = newNode;//然后把newNode赋值给被插入对象的prev引用,建立向前引用if (pred == null)//如果pred对象为null,说明到了队首,赋值给firstfirst = newNode;else//如果不为null,那么将pred的next节点赋值为newNode,完成向后引用pred.next = newNode;size++;//size自增modCount++;//修改标识自增}

在队尾添加集合的方法:

    public boolean addAll(Collection<? extends E> c) {return addAll(size, c);}//在指定的索引插入一个集合public boolean addAll(int index, Collection<? extends E> c) {checkPositionIndex(index);//检查索引合法性//转换为数组判断长度是否合法,为0直接返回falseObject[] a = c.toArray();int numNew = a.length;if (numNew == 0)return false;//定义向前和向后的引用    Node<E> pred, succ;if (index == size) {//如果index==size说明是要插入到队尾succ = null;//所以向后的引用就没有了pred = last;//向前的引用就是当前的last} else {//不等的话,就先取出index对应的节点succ = node(index);//向后引用直接使用该节点pred = succ.prev;//向前的引用赋值该节点的prev引用}for (Object o : a) {//循环插入的数组@SuppressWarnings("unchecked") E e = (E) o;//封装newNode对象,完成向前引用Node<E> newNode = new Node<>(pred, e, null);if (pred == null)//如果pred是null,说明到了队首first = newNode;//将newNode直接赋值给firstelse//如果pred不为Null,那么将newNode赋值给pred.next,完成向后链接pred.next = newNode;pred = newNode;//然后将newNode赋值给pred,继续下一轮链接,直到完成}//succ为null,说明是插入队尾的,将最后的pred赋值给lastif (succ == null) {last = pred;} else {//不为Nullpred.next = succ;//pred的向后引用指向succsucc.prev = pred;//succ的向前引用指向pred}size += numNew;//size增长modCount++;//修改标识自增return true;}

push方法

push方法是将指定元素添加到队首

    public void push(E e) {addFirst(e);}public void addFirst(E e) {linkFirst(e);}//最终调用该方法将元素添加到队首   private void linkFirst(E e) {final Node<E> f = first;//取队首节点封装到newNode final Node<E> newNode = new Node<>(null, e, f);first = newNode;//将newNode赋值到first队首if (f == null)//如果f也就是原来的队首是Null,说明没有之前没有任何元素last = newNode;//这样将newNode也赋值给lastelse//否则就把原来队首的向前引用指向newNodef.prev = newNode;size++;//size自增modCount++;//修改标识自增}

get方法

获取知道索引的元素的方法:

    public E get(int index) {checkElementIndex(index);//检测索引是否合法return node(index).item;//调用了node方法,取出节点包含的元素}

获取队首元素方法:

    public E getFirst() {final Node<E> f = first;if (f == null)//为null就抛出异常throw new NoSuchElementException();return f.item;//返回的就是first节点包含的元素}

获取队尾元素方法:

    public E getLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l.item;//返回的就是last节点包含的元素}

set方法

    public E set(int index, E element) {checkElementIndex(index);//检测索引合法性Node<E> x = node(index);//取出index指向的nodeE oldVal = x.item;//取出node包含的元素x.item = element;//直接赋值新元素return oldVal;//返回旧元素}

remove方法

移除指定索引的元素的方法:

    public E remove(int index) {checkElementIndex(index);return unlink(node(index));//先调用node方法取出对应的节点,然后调用unlink方法}E unlink(Node<E> x) {// assert x != null;//取出待移除的node包含的元素,和前后的引用节点final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;if (prev == null) {//如果向前引用prev为Null,说明移除的是队首元素first = next;//将向后的引用next赋值给first} else {//不为Null,就将prev的向后引用赋值next,完成向后的链接prev.next = next;x.prev = null;//原始的prev置Null}if (next == null) {//如果next为null,说明移除的是队尾last = prev;//就将prev赋值给last} else {//不是队尾就把next的向前引用赋值为prev,完成向前的链接next.prev = prev;x.next = null;//原始的next赋值为Null}x.item = null;//然后把原始的元素置nullsize--;//size自减modCount++;//修改标识自增return element;//返回旧元素}

pop方法

该方法就是移除队首元素,并返回移除的元素:

    public E pop() {return removeFirst();}public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);}//最终调用该方法来移除队首元素private E unlinkFirst(Node<E> f) {// assert f == first && f != null;final E element = f.item;//取出包含的旧元素final Node<E> next = f.next;//取出队首的向后引用的next节点f.item = null;//将原始元素置nullf.next = null; // help GC next也置null,GC负责回收first = next;//然后队首first置为nextif (next == null)//如果next是null说明之前只包含一个元素last = null;//那么移除后last也置Nullelse//否则,将新队首向前的引用置nullnext.prev = null;size--;//size自减modCount++;//修改标识自增return element;//返回旧元素}

size方法

    public int size() {return size;//直接返回size}

isEmpty方法

该方法在抽象类AbstractCollection中定义:

    public boolean isEmpty() {return size() == 0;//最终也是判断的size}

indexOf方法

找出指定元素出现的第一个索引方法:

    public int indexOf(Object o) {int index = 0;if (o == null) {//如果o为Null,从前往后循环,找出第一个为Null的元素,返回其索引for (Node<E> x = first; x != null; x = x.next) {if (x.item == null)return index;index++;}} else {//如果o不为Null,从前往后循环,找出第一个和O相同的元素,返回其索引for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item))return index;index++;}}return -1;//找不到返回-1}

lastIndexOf方法

找出指定元素出现的最后出现的索引方法:

    public int lastIndexOf(Object o) {int index = size;if (o == null) {//如果o为Null,从后往前循环,找出第一个为Null的元素,返回其索引for (Node<E> x = last; x != null; x = x.prev) {index--;if (x.item == null)return index;}} else {//如果o不为Null,从后往前循环,找出第一个和O相同的元素,返回其索引for (Node<E> x = last; x != null; x = x.prev) {index--;if (o.equals(x.item))return index;}}return -1;//找不到返回-1}

contain方法

    public boolean contains(Object o) {return indexOf(o) != -1;//其实也是调用的indexOf方法}

clear方法

    public void clear() {// Clearing all of the links between nodes is "unnecessary", but:// - helps a generational GC if the discarded nodes inhabit//   more than one generation// - is sure to free memory even if there is a reachable Iterator//就是从前往后遍历,将所有的引用置为Nullfor (Node<E> x = first; x != null; ) {Node<E> next = x.next;x.item = null;x.next = null;x.prev = null;x = next;}first = last = null;//最后将队首和队尾置nullsize = 0;//size为0modCount++;//修改标识自增}

总结

  • 由于LinkedList为双链表设计,ArrayList本质为数组实现,因此比较ArrayList主要区别:

转自 温布利往事:Java集合之LinkedList源码分析

ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间,就存储密度来说,ArrayList是优于LinkedList的。  
  总之,当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

  • 由于LinkedList实现了Deque双端队列接口,和List接口,因此存在功能相同的方法;
  • 实现了CloneableSerializable接口;

参考文章
Java集合之LinkedList源码分析

LinkedList源码浅析相关推荐

  1. 从面试角度分析LinkedList源码

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 Linke ...

  2. LinkedList 源码分析

    前言 上篇文章分析了 ArrayList 的源码,面试过程中经常会被面试官来问 LinkedList 和 ArrayList 的区别,这篇文章从源码的角度来看下 LinkedList 以后,再和上篇文 ...

  3. hashmap允许null键和值吗_hashMap底层源码浅析

    来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...

  4. LinkedList源码剖析

    1. LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当作链表来操作外,它还可以当作栈,队列和双端队列来使用. LinkedList同样是非线 ...

  5. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  6. 内核启动流程分析(四)源码浅析

    目录 kernel(四)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 kernel(四)源码浅析 建立 ...

  7. Java 集合系列(4): LinkedList源码深入解析1

    戳上面的蓝字关注我们哦! 精彩内容 精选java等全套视频教程 精选java电子图书 大数据视频教程精选 java项目练习精选 概要 前面,我们已经学习了ArrayList,并了解了fail-fast ...

  8. java linkedlist源码_Java集合之LinkedList源码分析

    一.LinkedList简介 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. ps:这里有一个问题,就是关于实现LinkedList的数据结构是否 ...

  9. harbor登录验证_Harbor 源码浅析

    Harbor 源码浅析​www.qikqiak.com Harbor 是一个CNCF基金会托管的开源的可信的云原生docker registry项目,可以用于存储.签名.扫描镜像内容,Harbor 通 ...

  10. Java类集框架 —— LinkedList源码分析

    在JDK1.7之前,LinkedList是采用双向环形链表来实现的,在1.7及之后,Oracle将LinkedList做了优化,将环形链表改成了线性链表.本文对于LinkedList的源码分析基于JD ...

最新文章

  1. 简述Field,Attribute,Property的区别
  2. teamviewer企业版 添加计算机,teamviewer添加常用控制电脑列表的操作步骤
  3. leetcode 11
  4. JavaScript和jQuery的学习
  5. 点云法向量与点云平面拟合的关系(PCA)
  6. java框架ssh实验报告_基于SSH的实验报告提交系统
  7. 高并发下如何保证数据的一致性
  8. 《俄罗斯方块》正版授权手游开启预约:支持QQ、微信双平台
  9. STM32 ucosii 串口接收数据 遇到的问题及解决思路
  10. C++ 实现大小写转换
  11. 防火墙工作在哪个层_数据库安全关键技术之数据库防火墙技术
  12. 如何把flv格式转成mp4格式?
  13. 屏蔽html查看源代码,禁止查看网页源代码方法
  14. 【Flutter】应用开发笔记
  15. 华为交换机配置(一)
  16. Access to XMLHttpRequest at ‘http127.0.0.18000server‘ from origin ‘http127.0.0.15500‘ has
  17. 前端和Java,学哪个好?
  18. 无线井盖状态的监测及预警设备安装方式
  19. ARP代理(Proxy ARP)
  20. 为什么 Vue 更符合这个时代的大势所趋

热门文章

  1. 我的 Android 开发实战经验总结
  2. java开发购物系统菜单_Java控制台购物系统
  3. Arcgis(二) 绘制区域划分示意图——以重庆五大功能区为例
  4. 吴式太极拳之北王-王茂斋先生
  5. Linux查看mpp数据库地址,Linux环境搭建DM8 MPP双节点集群
  6. 关于 蓝天显卡 异形卡 的改inf文件上驱动说明
  7. nals\string-trim.js Replace Autoprefixer browsers option to Browserslist config. Use browserslis
  8. 最大公约数用c语言表达,c语言求最大公约数(用c语言编写求最大公约数)
  9. 2003系统服务器不设置密码,服务器2003怎么设置密码
  10. 微信浏览器apk下载的解决方案