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. Python Web Framework All In One

    Python Web Framework All In One Django and Flask are the top Python web frameworks so far. Django ht ...

  2. CSS selector All In One

    CSS selector All In One CSS selector https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors ...

  3. web 前端工具: Gulp 使用教程

    1 1 1 Gulp 使用教程: 1 1 1 1 1 1 1 1 ERROR: ./app.js 当前目录路径: ./ 当前目录路径: ./ 1 1 1 1 1 参考资源: http://webpac ...

  4. how to measure function performance in javascript

    how to measure function performance in javascript Performance API Performance Timeline API Navigatio ...

  5. robots.txt

    robots.txt A robots.txt file tells search engine crawlers which pages or files the crawler can or ca ...

  6. js in depth: event loop & micro-task, macro-task & stack, queue, heap & thread, process

    js in depth: event loop & micro-task, macro-task & stack, queue, heap & thread, process ...

  7. vue常用方法封装-一键安装使用(赠送免费工具)

    相信大家在使用vue开发过程中一定遇到了各种方法的整理收集,每次遇到新的问题都需要找到合适的方法 这里我给大家封装了一些vue项目中常用到的方法合集,免费提供费大家 因此,jsoften横空出世,不为 ...

  8. 教你玩转CSS border(边框)

    边框样式 边框样式属性指定要显示什么样的边界. border-style属性用来定义边框的样式 border-style的值 代码演示: <!DOCTYPE html> <html ...

  9. 手把手教你Centos7 部署 gitlab社区版

    一.前置说明: 操作系统:Centos 7 物理内存:>=2G 本人亲测,如果安装低版本的gitlab,比如我这里所使用的v8.17.0,物理内存1G,swap 2G虚拟内存即可部署.高版本的所 ...

  10. LayUI之数据表格扩展

    1.点击一行 选中 以下代码需要在表格渲染完成时加载. 1)当单击表格行时,把单选按钮设为选中状态 //当单击表格行时,把单选按钮设为选中状态 $(document).on("click&q ...