LinkedList的源码分析(基于jdk1.8)
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)的更多相关文章
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
- CopyOnWriteArrayList 源码分析 基于jdk1.8
CopyOnWriteArrayList 源码分析: 1:成员属性: final transient ReentrantLock lock = new ReentrantLock(); //内部是 ...
- HashMap源码分析-基于JDK1.8
hashMap数据结构 类注释 HashMap的几个重要的字段 hash和tableSizeFor方法 HashMap的数据结构 由上图可知,HashMap的基本数据结构是数组和单向链表或红黑树. 以 ...
- ArrayList 源码分析 基于jdk1.8:
1:数据结构: transient Object[] elementData; //说明内部维护的数据结构是一个Object[] 数组 成员属性: private static final int ...
- ArrayList的源码分析(基于jdk1.8)
1.初始化 transient Object[] elementData; //实际存储元素的数组 private static final Object[] DEFAULTCAPACITY_EMPT ...
- AtomicInteger源码分析——基于CAS的乐观锁实现
AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...
- LinkedList 的源码分析
LinkedList是基于双向链表数据结构来存储数据的,以下是对LinkedList 的 属性,构造器 ,add(E e),remove(index),get(Index),set(inde,e)进 ...
- 并发-AtomicInteger源码分析—基于CAS的乐观锁实现
AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...
- Spring IoC 源码分析 (基于注解) 之 包扫描
在上篇文章Spring IoC 源码分析 (基于注解) 一我们分析到,我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫 ...
随机推荐
- redis3.2.8安装与简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存中的 ...
- RMAN备份等级详解
(2011-06-27 11:38:28) 转载▼ 标签: 杂谈 分类: oracle 在rman增量备份中,有差异增量和累积增量的概念 1.概念 差异增量:是备份上级及同级备份以来所有变化的数据块, ...
- Asp ose.Tota l for .NET 2015
How to license Aspose.Total for .NET products Add "License.cs" [C#] OR "License.vb&qu ...
- Angular4 微信的坑
1.不要重置对象的引用!(重置只应该在组件或服务的初始化时) why:会使页面产生闪烁 2.不要给图片绑定一个空的值或空的图片引用(如果值从服务器异步过来,那应该在初始化时给它一个默认值) why:会 ...
- yii2.0 Activeform表单部分组件使用方法 [ 2.0 版本 ]
文本框:textInput(); 密码框:passwordInput(); 单选框:radio(),radioList(); 复选框:checkbox(),checkboxList(); 下拉框:dr ...
- vbs notepad输入中文字符
结合网上的写法,总结了一下 Set wshobj=WScript.CreateObject("WScript.Shell") #code就是想输入的中文或中英文的结合code=&q ...
- 工作好搭档(一):松林 SL-B3 人体工学椅
本人从事码农这行职业,已经整整十年零九天,十年一觉如旧梦,仿佛昨天还在SARS. 2008年,我累到腰痛,脖子痛,怎么休息也不见好,去中医院检查,医生诊断,坐的太久,坐姿不对,运动少,轻度颈椎,腰肌劳 ...
- zt C++ list 类学习笔记
C++ list 类学习笔记 分类: C++ 2011-09-29 00:12 7819人阅读 评论(0) 收藏 举报 listc++iteratorvectorcconstructor 双向循环链表 ...
- JAVA中高访问量高并发的问题怎么解决?
尽量使用缓存,包括用户缓存,信息缓存等,多花点内存来做缓存,可以大量减少与数据库的交互,提高性能. 用jprofiler等工具找出性能瓶颈,减少额外的开销. 优化数据库查询语句,减少直接使用hiber ...
- Linux硬盘扩容
1.安装gparted分区工具 2.从虚拟机设置新的虚拟硬盘 3.挂载分区 sudo mount -t ext4 /dev/sdb1 /home/zhoushuo/zsdf -hsudo chmod ...