LinkedList的基本结构是双向链接的直线结构。

链表的构造函数有两个,其中空构造函数什么都没做,就是一个空实现。

    /**
* Constructs an empty list.
*/
public LinkedList() {
} /**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

另外就是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;
}
}

可以发现顺序是前驱,值,后继。

继续使用断点调试的方法阅读源码

 LinkedList linkedList = new LinkedList();
linkedList.add(1);

目前还是空链表,我们准备加1.

public boolean add(E e) {
linkLast(e);
return true;
}

继续跳转到linkLast(e)中

    /**
* 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;//last指向newNode
if (l == null)//因为刚开始的last就是空,也就是一开始就是空链表
first = newNode;那么first也指向newNode
else
l.next = newNode;
size++;
modCount++;
}

首先last指向l,l此时为null,而l也指向了这个null。newNode为前驱和后继均为null,值为1的节点,。那么此时头节点就是当前节点就是尾节点。要不然的话,我们在执行

  linkedList.add(2);

此时再到

void linkLast(E e) {
final Node<E> l = last;//此时last指向的是值为1的节点,也是链表的尾节点
final Node<E> newNode = new Node<>(l, e, null);//创造新的节点,新节点的前驱是原先的尾戒点,
last = newNode;//然后将last指向新的节点,也就是后移last。
if (l == null)
first = newNode;
else//此时l不为空
l.next = newNode;//所以l的下一个连上新的节点,这样就加入成功了
size++;
modCount++;
}

LinkedList的get()方法,下标是从0开始算的

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

首先越界检查,然后跳到node()方法。

    /**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index); if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

这段代码就很有意思,我们都知道链表增删都很快,但是查询较慢。此处的查询做了优化。也就是说如果索引小于链表长度的一半,那么就从头开始找。反之从后找。这样就提高了效率。

接下来就是add(int index, E element)方法。进断点调试

LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.add(3,9);
 public void add(int index, E element) {
checkPositionIndex(index); if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}

可以看到首先是越界检查。然后是判断是否是直接插入队尾。如果是插入队尾。如果不是,则先node(idnex)查询出索引处的节点。然后再进入到linkBefore

    void linkBefore(E e, Node<E> succ) {此时succ为插入处的节点,也就是值为4的节点。e值为9
// assert succ != null;
final Node<E> pred = succ.prev;//保存4的前驱,
final Node<E> newNode = new Node<>(pred, e, succ);//此时新的节点的前驱要为4的前驱,后继为4.
succ.prev = newNode;//此时再连上4之后的节点,也就是4的前驱是新节点。
if (pred == null)//这里是链接3的后继。如果4的前驱是空,那么就是在对头插入数据,那么first就是新的节点
first = newNode;
else//否则,3的next就是新的节点,此时就全部连上了
pred.next = newNode;
size++;
modCount++;
}

接下来就是remove(),默认删除第一个节点。

public E remove() {
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;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}

首先是保存first。存储头节点的后续节点next。然后令头节点值为空,头节点断开。再移动first到next。如果只有头节点,那么last也为空,此时链表为空。如果不是,则next的前驱为null,那么就删除了头节点了。

接下来是remove(int index)方法。

  public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}

一样,先检查越界,然后查找索引处的节点,再到unlink函数中

E unlink(Node<E> x) {//此时x为值为3的节点
// 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;//那么就代表删除的是头节点,所以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;
}

由于调试是

 LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.add(3,9);
linkedList.get(3);
// linkedList.remove();//默认删除第一个节点
linkedList.remove(2);//删除第二个节点

所以上述unlink中的注释就可以看懂了

接下来将indexof(Object o)

public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}

这就是查找节点对象的索引,如果没找到就返回-1.

最后将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
for (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;
size = 0;
modCount++;
}

循环依次断开每个头节点。然后fist和last都指向null,大小为0.

总结:1.LinkedList是非线程安全的

2.他的删除增加效率很高,但是查询较慢。

LinkedList源码个人解读的更多相关文章

  1. 【源码阅读】Java集合之二 - LinkedList源码深度解读

    Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章; 本文是第二篇LinkedList. ---@pdai JDK版 ...

  2. LinkedList 源码解读

    LinkedList 源码解读 基于jdk1.7.0_80 public class LinkedList<E> extends AbstractSequentialList<E&g ...

  3. LinkedList源码解读

    一.内部类Node数据结构 在讲解LinkedList源码之前,首先我们需要了解一个内部类.内部类Node来表示集合中的节点,元素的值赋值给item属性,节点的next属性指向下一个节点,节点的pre ...

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

    注:本系列文章中用到的jdk版本均为java8 LinkedList类图如下: LinkedList底层是由双向链表实现的.链表好比火车,每节车厢包含了车厢和连接下一节车厢的连接点.而双向链表的每个节 ...

  5. 给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析

    LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明.   1.链表的概念      链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链 ...

  6. LinkedList源码解析

    LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明.1.链表的概念链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链表又分为单向链表和 ...

  7. ArrayList和LinkedList源码

    1 ArrayList 1.1 父类 java.lang.Object 继承者 java.util.AbstractCollection<E> 继承者 java.util.Abstract ...

  8. 转:【Java集合源码剖析】LinkedList源码剖析

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票 ...

  9. java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

    java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...

随机推荐

  1. Google PageSpeed Insights : 网站性能优化检测工具

    1 1 https://developers.google.com/speed/pagespeed/insights/ PageSpeed Insights 使您的网页在所有设备上都能快速加载. 分析 ...

  2. how to publish a dart package using Github Actions?

    how to publish a dart package using Github Actions? dart package flutter package Github Actions publ ...

  3. 图解 Webpack 4.x 热更新原理

    图解 Webpack 4.x 热更新原理 Webpack HMR ️ module.hot & module.hot.accept if (module.hot) { module.hot.a ...

  4. Scratch & Flappy Turtle & Flappy Bird & Game

    Scratch & Flappy Turtle & Flappy Bird & Game Flappy Turtle Game https://scratch.mit.edu/ ...

  5. HTML5 tag you don't know

    HTML5 tag you don't know a collection of html5 tag very little be used in practices semantic element ...

  6. website & blogs & about me & contact

    website & blogs & about me & contact demos https://davidwalsh.name/about-david-walsh htt ...

  7. WEB 用视频替换GIF动画

    原文 download ffmpeg gif to video 转化后文件大小大大降低 $ ffmpeg -i my-animation.gif -b:v 0 -crf 25 -f mp4 -vcod ...

  8. JDK环境解析,安装和目的

    目录 1. JDK环境解析 1.1 JVM 1.2 JRE 1.3 JDK 2. JDK安装 2.1 为什么使用JDK8 2.1.1 更新 2.1.2 稳定 2.1.3 需求 2.2 安装JDK 2. ...

  9. halo博客安装教程,一款优秀的java开源博客系统

    整理了一下,决定用宝塔来管理反代和ssl自动续签,这样比较适合小白. 前置要求 会ssh远程连接.域名已经解析到服务器ip上即可, 安装步骤 按照下面一步一步来,应该是木有问题的哦 ssh连接好,依次 ...

  10. 方案设计:基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 如何保证代码质量? 业务提需求,产品定方案,研发做实现,测试验流程.四种角色的相互配 ...