1.初始化

public LinkedList() {
}

并未开辟任何类似于数组一样的存储空间,那么链表是如何存储元素的呢?

2.Node类型

存储到链表中的元素会被封装为一个Node类型的结点。并且链表只需记录第一个结点的位置和最后一个结点的位置。然后每一个结点,前后连接,就可以串起来变成一整个链表。

transient Node<E> first;//指向链表的第一个结点

transient Node<E> last;//指向链表的最后一个结点

//LinkedList中有一个内部类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;
}
}

3.添加元素

public boolean add(E e) {
//默认链接到链表末尾
linkLast(e);
return true;
}
void linkLast(E e) {
//用l记录当前链表的最后一个结点对象
final Node<E> l = last; //创建一个新结点对象,并且指定当前新结点的前一个结点为l
final Node<E> newNode = new Node<>(l, e, null); //当前新结点就变成了链表的最后一个结点
last = newNode; if (l == null)
     //如果当前链表是空的,那么新结点对象,同时也是链表的第一个结点
first = newNode;
else
//如果当前链表不是空的,那么最后一个结点的next就指向当前新结点
l.next = newNode; //元素个数增加
size++; //修改次数增加
modCount++;
}

4.删除元素

public boolean remove(Object o) {
//分o是否是null讨论,从头到尾找到要删除的元素o对应的Node结点对象,然后删除
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
final E element = x.item;
//用next记录被删除结点的后一个结点
final Node<E> next = x.next;
//用prev记录被删除结点的前一个结点
final Node<E> prev = x.prev; if (prev == null) {
//如果删除的是第一个结点,那么被删除的结点的后一个结点将成为第一个结点
first = next;
} else {
//否则被删除结点的前一个结点的next应该指向被删除结点的后一个结点
prev.next = next;
//断开被删除结点与前一个结点的关系
x.prev = null;
} if (next == null) {
//如果删除的是最后一个结点,那么被删除结点的前一个结点将变成最后一个结点
last = prev;
} else {
//否则被删除结点的后一个结点的prev应该指向被删除结点的额前一个结点
next.prev = prev; //断开被删除结点与后一个结点的关系
x.next = null;
}
//彻底把被删除结点变成垃圾对象
x.item = null; //元素个数减少
size--; //修改次数增加
modCount++;
return element;
}

5.指定位置插入元素

public void add(int index, E element) {
//检查索引位置的合理性
checkPositionIndex(index); if (index == size)
//如果位置是在最后,那么链接到链表的最后
linkLast(element);
else
//否则在链表中间插入
//node(index)表示找到index位置的Node对象
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
// pred记录被插入位置的前一个结点
final Node<E> pred = succ.prev;
//构建一个新结点
final Node<E> newNode = new Node<>(pred, e, succ);
//把新结点插入到succ的前面
succ.prev = newNode;
//如果被插入点是链表的开头,那么新结点变成了链表头
if (pred == null)
first = newNode;
else
//否则pred的next就变成了新结点
pred.next = newNode;
//元素个数增加
size++;
//修改次数增加
modCount++;
}

6.总结

对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高。因为不涉及到移动元素,只需要修改前后结点的关系。也不需要涉及到扩容

此类虽然提供按照索引查找与操作的方法,但是效率不高,如果需要按索引操作,那么建议使用动态数组

LinkedList的源码分析(基于jdk1.8)的更多相关文章

  1. HashMap 源码分析 基于jdk1.8分析

    HashMap 源码分析  基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table;  //这里维护了一个 Node的数组结构: 下面看看Node的数 ...

  2. CopyOnWriteArrayList 源码分析 基于jdk1.8

    CopyOnWriteArrayList  源码分析: 1:成员属性: final transient ReentrantLock lock = new ReentrantLock();  //内部是 ...

  3. HashMap源码分析-基于JDK1.8

    hashMap数据结构 类注释 HashMap的几个重要的字段 hash和tableSizeFor方法 HashMap的数据结构 由上图可知,HashMap的基本数据结构是数组和单向链表或红黑树. 以 ...

  4. ArrayList 源码分析 基于jdk1.8:

    1:数据结构: transient Object[] elementData;  //说明内部维护的数据结构是一个Object[] 数组 成员属性: private static final int ...

  5. ArrayList的源码分析(基于jdk1.8)

    1.初始化 transient Object[] elementData; //实际存储元素的数组 private static final Object[] DEFAULTCAPACITY_EMPT ...

  6. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

  7. LinkedList 的源码分析

    LinkedList是基于双向链表数据结构来存储数据的,以下是对LinkedList  的 属性,构造器 ,add(E e),remove(index),get(Index),set(inde,e)进 ...

  8. 并发-AtomicInteger源码分析—基于CAS的乐观锁实现

    AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...

  9. Spring IoC 源码分析 (基于注解) 之 包扫描

    在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...

随机推荐

  1. javascript面向对象的写法02

    面向对象特性的初步实现 1.封装 利用作用域封装变量 作用域的概念是一样的,for语句,if语句等这些作用域内定义的变量只能作用域内访问,函数内定义的变量只能函数内访问. function Class ...

  2. linux压缩

    zip:打包 :zip something.zip something (目录请加 -r 参数)解包:unzip something指定路径:-d 参数tar:打包:tar -zcvf somethi ...

  3. 测试mysql性能工具

    mysqlslap mysqlslap可以模拟服务器的负载,并输出计时信息.它包含在MySQL 5.1 的发行包中,应该在MySQL 4.1或者更新的版本中都可以使用.测试时可以执行并发连接数,并指定 ...

  4. 关于 Web 安全,99% 的网站都忽略了这些

    Web安全是一个如何强调都不为过的事情,我们发现国内的众多网站都没有实现全站https,对于其他安全策略的实践更是很少,本文的目的并非讨论安全和攻击的细节,而是从策略的角度引发对安全的思考和重视. 1 ...

  5. 如何让LoadRunner实现多个场景运行?

    如何让LoadRunner实现多个场景运行? 发布时间: 2013-11-29 10:59    作者: stevenlee    来源: 51Testing软件测试网博客 字体:  小  中  大  ...

  6. February 16 2017 Week 7 Thursday

    Other men live to eat, while I eat to live. 很多人为食而生存,而我为生存而食. Just the same, either you eat to live ...

  7. mac 删除自带 ABC 输入法的方法

    首先需要关闭 mac 系统的 SIP ,不然删不掉,不会关的可以查看我的另一篇文章:mac 关闭系统完整性保护 SIP(System Integrity Protection)的方法 . 关闭 SIP ...

  8. Jmeter--随机生成浮点数

    post接口中需要随机生成目标对象的地理位置: 所以就使用了javaScript函数,如下所示: { "Location": { "Altitude": ${_ ...

  9. HDU 1009 FatMouse' Trade(简单贪心)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1009 FatMouse' Trade Time Limit: 2000/1000 MS (Java/O ...

  10. 【Node.js学习笔记】使用Gulp项目自动化构建工具

    刚接触node.js,对前端的一些东西还不是很清楚,据说Gulp这东西很强大,先来看看从网上抄的一段关于自动化构建的描述: 在为数众多的中小型软件作坊中,不存在自动化构建和发布工具.构建.交付准备环境 ...