LinkedList源码浅析
回顾集合体系中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
接口,因此存在功能相同的方法; - 实现了
Cloneable
和Serializable
接口;
参考文章
Java集合之LinkedList源码分析
LinkedList源码浅析相关推荐
- 从面试角度分析LinkedList源码
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 Linke ...
- LinkedList 源码分析
前言 上篇文章分析了 ArrayList 的源码,面试过程中经常会被面试官来问 LinkedList 和 ArrayList 的区别,这篇文章从源码的角度来看下 LinkedList 以后,再和上篇文 ...
- hashmap允许null键和值吗_hashMap底层源码浅析
来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...
- LinkedList源码剖析
1. LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当作链表来操作外,它还可以当作栈,队列和双端队列来使用. LinkedList同样是非线 ...
- Android Loader机制全面详解及源码浅析
原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...
- 内核启动流程分析(四)源码浅析
目录 kernel(四)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 kernel(四)源码浅析 建立 ...
- Java 集合系列(4): LinkedList源码深入解析1
戳上面的蓝字关注我们哦! 精彩内容 精选java等全套视频教程 精选java电子图书 大数据视频教程精选 java项目练习精选 概要 前面,我们已经学习了ArrayList,并了解了fail-fast ...
- java linkedlist源码_Java集合之LinkedList源码分析
一.LinkedList简介 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. ps:这里有一个问题,就是关于实现LinkedList的数据结构是否 ...
- harbor登录验证_Harbor 源码浅析
Harbor 源码浅析www.qikqiak.com Harbor 是一个CNCF基金会托管的开源的可信的云原生docker registry项目,可以用于存储.签名.扫描镜像内容,Harbor 通 ...
- Java类集框架 —— LinkedList源码分析
在JDK1.7之前,LinkedList是采用双向环形链表来实现的,在1.7及之后,Oracle将LinkedList做了优化,将环形链表改成了线性链表.本文对于LinkedList的源码分析基于JD ...
最新文章
- 简述Field,Attribute,Property的区别
- teamviewer企业版 添加计算机,teamviewer添加常用控制电脑列表的操作步骤
- leetcode 11
- JavaScript和jQuery的学习
- 点云法向量与点云平面拟合的关系(PCA)
- java框架ssh实验报告_基于SSH的实验报告提交系统
- 高并发下如何保证数据的一致性
- 《俄罗斯方块》正版授权手游开启预约:支持QQ、微信双平台
- STM32 ucosii 串口接收数据 遇到的问题及解决思路
- C++ 实现大小写转换
- 防火墙工作在哪个层_数据库安全关键技术之数据库防火墙技术
- 如何把flv格式转成mp4格式?
- 屏蔽html查看源代码,禁止查看网页源代码方法
- 【Flutter】应用开发笔记
- 华为交换机配置(一)
- Access to XMLHttpRequest at ‘http127.0.0.18000server‘ from origin ‘http127.0.0.15500‘ has
- 前端和Java,学哪个好?
- 无线井盖状态的监测及预警设备安装方式
- ARP代理(Proxy ARP)
- 为什么 Vue 更符合这个时代的大势所趋
热门文章
- 我的 Android 开发实战经验总结
- java开发购物系统菜单_Java控制台购物系统
- Arcgis(二) 绘制区域划分示意图——以重庆五大功能区为例
- 吴式太极拳之北王-王茂斋先生
- Linux查看mpp数据库地址,Linux环境搭建DM8 MPP双节点集群
- 关于 蓝天显卡 异形卡 的改inf文件上驱动说明
- nals\string-trim.js Replace Autoprefixer browsers option to Browserslist config. Use browserslis
- 最大公约数用c语言表达,c语言求最大公约数(用c语言编写求最大公约数)
- 2003系统服务器不设置密码,服务器2003怎么设置密码
- 微信浏览器apk下载的解决方案