LinkedList:
继承关系分析:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable

这里的Cloneable,Serializable,List这三个接口就不多赘述了,之前在介绍ArrayList的时候已经说过了。主要分析下AbstractSequentialList跟Deque

  1. AbstractSequentialList

    AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些函数。LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”

  2. Deque

    实现了Deque接口,代表LinkedList能被当作双端队列使用

字段分析:
// 长度
transient int size = 0; // 头节点
transient Node<E> first; // 尾节点
transient Node<E> last; // 继承了AbstractSequentialList,AbstractSequentialList又继承了AbstractList
protected transient int modCount = 0;
构造函数分析:
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
方法分析:
  1. add,我们主要看这两个重载的方法

    // 往末尾添加元素
    public boolean add(E e) {
    linkLast(e);
    return true;
    }
    // 往头部添加
    public void addFirst(E var1) {
    this.linkFirst(var1);
    }
    // 往指定位置添加元素
    public void add(int index, E element) {
    // 判断角标是否越界
    checkPositionIndex(index);
    // 说明是往尾部添加元素
    if (index == size)
    linkLast(element);
    else
    // 往集合中添加元素
    linkBefore(element, node(index));
    }

    跟踪方法到,linkLast(e)。

    void linkLast(E e) {
    // 申明一个Node,保存last的引用
    final Node<E> l = last;
    // 创建一个新节点,pre指向集合的last,next指针指向null,也即是集合的尾指针
    final Node<E> newNode = new Node<>(l, e, null);
    // 集合的尾指针指向新节点
    last = newNode;
    if (l == null)
    // 说明集合刚刚初始化,一个元素都没有,添加的是第一个元素
    first = newNode;
    else
    // 集合中原来的最后一个节点的next指针指向了新节点
    l.next = newNode;
    // 集合长度加1
    size++;
    // 集合修改次数加1
    modCount++;
    }

    用图形的方式我们分析下这个过程:

    假设我们现在有一个三个元素的集合,如下:

    现在我们要调用add方法往集合的尾部添加一个元素:

    1. 创建一个节点

    2. 需要将这个节点的pre指针指向当前集合的最后一个元素,同时将当前集合的最后一个元素的next指针指向这个新增的节点

  1. 不要忘了,在添加后,我们集合的last指针也变成了我们新增的这个元素的next指针

上面我们分析了往尾部添加一个元素的情况,往头部添加一个元素就不多追诉了,那么往集合中添加一个元素呢?

我们先看下源码:

首先调用了node方法

Node<E> node(int 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;
}
}

之后调用linkBefore方法

// e:要添加的节点
// succ: 当前位置上的节点
// 这个方法主要就是将e添加到succ节点的前一个节点位置上
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 用pred指针指向succ的前一个节点
final Node<E> pred = succ.prev;
// 创建一个新节点,pre指针指向succ的前一个节点,next指针指向succ,节点元素为e
final Node<E> newNode = new Node<>(pred, e, succ);
// succ的pre指针指向新增的节点
succ.prev = newNode;
// 如果succ.prev==null,说明succ是头节点,所以添加后,我们新增的节点就是头节点
if (pred == null)
first = newNode;
else
// 否则的话,就将succ的前一个节点的next指针指向新增的节点
pred.next = newNode;
// 集合长度+1
size++;
// 修改次数加+1
modCount++;
}

​ 我们也用图形的方式来描述下这个过程:

​ 假设我们现在有一个三个元素的集合,如下:

我们要在index=1的位置上新增一个元素,也就是调用add(1,“新元素”)

  1. 我们要获取到index=1的这个位置上的元素,通过源码我们知道,会通过头节点遍历到第二个节点后,返回给我们这个节点,也就是node方法

  2. 因为我们要在index=1的位置上插入元素,所以,我们要将index=0位置上的元素的next指针指向我们新增的元素,同时我们要新增的节点的pre指针指向头节点,另外,新增节点的next指针指向原index=1上位置的节点,index=1位置上的节点的pre指针指向我们新增的节点

  3. get方

    public E get(int index) {
    checkElementIndex(index);
    // get就是调用我们之前分析过的node方法
    // 获取指定位置上的节点,然后返回节点保存的元素
    return node(index).item;
    }
  4. 对比ArrayList我们也分析下迭代器

    跟踪源码我们可以发现,核心就是下面这个内部类

    private class ListItr implements ListIterator<E> {
    // 记录当前迭代器最后一个返回的节点
    private Node<E> lastReturned;
    // 下一个迭代的节点
    private Node<E> next;
    // 下一个迭代的节点的索引位置
    private int nextIndex;
    // 快速失败机制
    private int expectedModCount = modCount;
    // 创建一个从指定位置开始迭代的迭代器
    ListItr(int index) {
    // assert isPositionIndex(index);
    next = (index == size) ? null : node(index);
    nextIndex = index;
    } public boolean hasNext() {
    return nextIndex < size;
    }
    // 返回准备迭代的元素
    public E next() {
    checkForComodification();
    if (!hasNext())
    throw new NoSuchElementException();
    // 记录返回的节点
    lastReturned = next;
    // 记录迭代器将要迭代的下一个节点
    next = next.next;
    // 索引+1
    nextIndex++;
    return lastReturned.item;
    } public boolean hasPrevious() {
    return nextIndex > 0;
    } public E previous() {
    checkForComodification();
    if (!hasPrevious())
    throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev;
    nextIndex--;
    return lastReturned.item;
    } public int nextIndex() {
    return nextIndex;
    } public int previousIndex() {
    return nextIndex - 1;
    }
    // 分析下这个方法中的unlink方法
    public void remove() {
    checkForComodification();
    if (lastReturned == null)
    throw new IllegalStateException(); Node<E> lastNext = lastReturned.next;
    // 移除上一次迭代的节点
    unlink(lastReturned);
    // 说明通过previous()方法迭代
    if (next == lastReturned)
    // 这个时候nextIndex不用发生变化
    next = lastNext;
    else
    // 因为移除了一个元素,所以nextIndex需要减1
    nextIndex--;
    lastReturned = null;
    expectedModCount++;
    } public void set(E e) {
    if (lastReturned == null)
    throw new IllegalStateException();
    checkForComodification();
    lastReturned.item = e;
    } public void add(E e) {
    // 快速失败机制检查
    checkForComodification();
    lastReturned = null;
    if (next == null)
    linkLast(e);
    else
    linkBefore(e, next);
    nextIndex++;
    expectedModCount++;
    }
    // 用于将元素从集合中移除
    // 其实做的就是,将这个节点的pre指针指向的节点的next指针指向这个节点的next指针指向的节点
    // 同时将这个节点的next指针指向的节点的pre指针指向这个节点的pre指针指向的节点
    E unlink(Node<E> x) {
    // 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;
    } 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;
    }

    图片描述如下:

java读源码 之 list源码分析(LinkedList)的更多相关文章

  1. Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析

    Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...

  2. Java读源码之ReentrantLock

    前言 ReentrantLock 可重入锁,应该是除了 synchronized 关键字外用的最多的线程同步手段了,虽然JVM维护者疯狂优化 synchronized 使其已经拥有了很好的性能.但 R ...

  3. Java读源码之ReentrantLock(2)

    前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...

  4. Java读源码之CountDownLatch

    前言 相信大家都挺熟悉 CountDownLatch 的,顾名思义就是一个栅栏,其主要作用是多线程环境下,让多个线程在栅栏门口等待,所有线程到齐后,栅栏打开程序继续执行. 案例 用一个最简单的案例引出 ...

  5. 源码分析— java读写锁ReentrantReadWriteLock

    前言 今天看Jraft的时候发现了很多地方都用到了读写锁,所以心血来潮想要分析以下读写锁是怎么实现的. 先上一个doc里面的例子: class CachedData { Object data; vo ...

  6. 【转载】深度解读 java 线程池设计思想及源码实现

    总览 开篇来一些废话.下图是 java 线程池几个相关类的继承结构: 先简单说说这个继承结构,Executor 位于最顶层,也是最简单的,就一个 execute(Runnable runnable) ...

  7. Java并发指南12:深度解读 java 线程池设计思想及源码实现

    ​深度解读 java 线程池设计思想及源码实现 转自 https://javadoop.com/2017/09/05/java-thread-pool/hmsr=toutiao.io&utm_ ...

  8. 转:微信开发之使用java获取签名signature(贴源码,附工程)

    微信开发之使用java获取签名signature(贴源码,附工程) 标签: 微信signature获取签名 2015-12-29 22:15 6954人阅读 评论(3) 收藏 举报  分类: 微信开发 ...

  9. java基础进阶一:String源码和String常量池

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...

  10. Java禁止浏览器有缓存的源码

    Java禁止浏览器有缓存的源码 import java.io.IOException; import javax.servlet.Filter; import javax.servlet.Filter ...

随机推荐

  1. IE各版本CSS Hack(兼容性处理)语法速查表

    为了兼容IE各个版本,需要在CSS中添加额外的代码,比如以前常用的_width.之所以工作,是因为浏览器会忽略不能解析的样式规则,因此举个例子来说,把_width写在width下面,对于非IE浏览器会 ...

  2. stand up meeting 11/30/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云   完善了UI的各项功能,弹窗的显示格式等方面的规范:解决logic部分调用该dll的问题:解决鼠标事件的捕捉中~     ...

  3. vue组件之间值传递四种方法汇总

    1.父组件获取子组件的数据和方法 $refs 子组件: <template> <div class="header"> <h3>{{ zz }} ...

  4. 【题解】P4570 [BJWC2011]元素 - 线性基 - 贪心

    P4570 [BJWC2011]元素 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 给你 \(n\) 个二元组 \( ...

  5. C# 基础知识系列- 13 常见类库介绍(一)

    0. 前言 每篇一个前言,介绍一下这一篇的内容.之前的内容都是针对某些知识点进行的介绍,这篇内容介绍一下实际开发中常用的一些类和命名空间.这一篇是个连续剧,大概有个三四集.嗯,就是这样. 1. Sys ...

  6. 基于 HTML WebGL 的会展中心智能监控系统

    前言 随着近几年物联网.万物互联等诸多概念的大行其道,智慧城市的概念也早已经被人们耳熟能详,而作为城市的组成部分,智慧建筑也是重中之重,智慧园区,智慧小区等也如雨后春笋般的相继出现. 智慧建筑是指通过 ...

  7. TokenMismatchException Laravel

    随便写写:很久没写了,今天闲的. 1.错误原因:出现这个错误肯定是因为你在Laravel开启了csrf防御,但是你post提交过去的字段中没有生成_token. 2.如果你提交的字段中带有了_toke ...

  8. 手把手教你使用Python爬取西刺代理数据(下篇)

    /1 前言/ 前几天小编发布了手把手教你使用Python爬取西次代理数据(上篇),木有赶上车的小伙伴,可以戳进去看看.今天小编带大家进行网页结构的分析以及网页数据的提取,具体步骤如下. /2 首页分析 ...

  9. javascript-数组简单的认识

    一起组团(什么是数组) 我们知道变量用来存储数据,一个变量只能存储一个内容.假设你想存储10个人的姓名或者存储20个人的数学成绩,就需要10个或20个变量来存储,如果需要存储更多数据,那就会变的更麻烦 ...

  10. 百度云BCH配置说明

    百度云虚拟空间(BCH)  来源:https://www.cnblogs.com/llll/p/7930029.html 参考资料:https://cloud.baidu.com/doc/BCH/Ge ...