JDK8集合类源码解析 - LinkedList
linkedList主要要注意以下几点:
1构造器
2 添加add(E e)
3 获取get(int index)
4 删除 remove(E e),remove(int index)
5 判断对象是否存在:contain(E e)
Jdk8中的linkedList基于双向链表来实现的
双向链表每个结点包含数据域,一个前指针和一个后指针,分别指向前驱结点和后继结点(如果有前驱/后继的话,没有就是null)。
另外,双向链表还有一个first指针,指向头节点,和last指针,指向尾节点。
接下来我们来看看源码
transient int size = 0;
transient Node<E> first; //整个链表的头结点
transient Node<E> last; //整个链表的尾结点
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;
}
}
如图所示

1构造器
有两个构造器,分别是空的和从一个集合创建。

注意:因为基于链表创建,理论上无容量的限制,不需要扩容的操作。
2 添加add(E e)
默认添加是在链表尾部添加
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
//创建一个新结点 并且把尾结点设为当前结点的前驱
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)//如果整个链表为空
first = newNode; //把当前结点设为头结点
else //不为空
l.next = newNode; //把当前结点设为尾结点的后继
size++;
modCount++;
}
3 获取get(int index)
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
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;
}
}
注意:1跟arraylist不同的是,linkedlist查找指定下标元素需要遍历
2 >> 1 (除以2) <<1 (乘以2)
4 删除 remove(E e),remove(int index)
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) { //先溢出第一个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) {
// 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;
}
5 判断对象是否存在:contain(E e)
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o == null) {
//从头开始找 找到第一个null
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
//从头开始找 看有没有等于item的
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
总结:1 Jdk8中的linkedList基于双向链表来实现的,插入删除快,查询慢,适合读少写多的情况
2 linkedList还有一些有趣的方法,可以实现队列
3添加元素时不用扩容
4 线程不安全
JDK8集合类源码解析 - LinkedList的更多相关文章
- JDK8集合类源码解析 - HashSet
HashSet 特点:不允许放入重复元素 查看源码,发现HashSet是基于HashMap来实现的,对HashMap做了一次“封装”. private transient HashMap<E,O ...
- JDK8集合类源码解析 - HashMap
java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap.Hashtable.LinkedHashMap和TreeMap HashMap ...
- JDK8集合类源码解析 - ArrayList
ArrayList主要要注意以下几点: 1构造方法 2添加add(E e) 3 获取 get(int index) 4 删除 remove(int index) , remove(Objec ...
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- 从源码解析LinkedList集合
上篇文章我们介绍了ArrayList类的基本的使用及其内部的一些方法的实现原理,但是这种集合类型虽然可以随机访问数据,但是如果需要删除中间的元素就需要移动一半的元素的位置,效率低下.并且它内 ...
- Java集合类源码解析:ArrayList
目录 前言 源码解析 基本成员变量 添加元素 查询元素 修改元素 删除元素 为什么用 "transient" 修饰数组变量 总结 前言 今天学习一个Java集合类使用最多的类 Ar ...
- Java集合类源码解析:AbstractMap
目录 引言 源码解析 抽象函数entrySet() 两个集合视图 操作方法 两个子类 参考: 引言 今天学习一个Java集合的一个抽象类 AbstractMap ,AbstractMap 是Map接口 ...
- Java集合类源码解析:LinkedHashMap
前言 今天继续学习关于Map家族的另一个类 LinkedHashMap .先说明一下,LinkedHashMap 是继承于 HashMap 的,所以本文只针对 LinkedHashMap 的特性学习, ...
随机推荐
- 关于Windows下无法在MySQL安装目录找到配置文件my.ini
目前5.7版本的MySQL的配置文件my.ini位于: C:\ProgramData\MySQL\MySQL Server 5.7
- c/c++ 中的重要函数
1,strtod: 函数原型: #include <cstdlib> double strtod(const char *nptr, char **endptr); strtod 原型 名 ...
- kafka集群压力测试--基础。
1.生产者测试 kafka-producer-perf-test.bat --num-records 1000000 --topic test --record-size 200 --throughp ...
- Docker 网络不通的解决方法
表现是: docker主机内部网络正常,与其它主机的连接失效,其它主机不能连接docker主机上映射的端口,docker内部也无法连接外部主机. 执行docker info,可以看到一些警告. 可在不 ...
- 最小k度限制生成树
[题目描述] 给你一个图,n个点,m条边,求一颗生成树满足如下条件: (1)结点1的度不超过k. (2)在(1)条件下所求生成树最小. [算法引入] 最小k度限制生成树,就是指有特殊的某一点的度不能超 ...
- Linux下安装和使用nginx
浏览器和服务器的关系 NGINX nginx是什么 nginx是一个开源的,支持高性能,高并发的www服务和代理服务软件. nginx不但是一个优秀的web服务软件,还可以作为反向代理,负载均衡,以及 ...
- POJ-2251.DungeonMaster(三维BFS)
做题时需要注意,爬楼有向上和向下爬之分... 本题大意:输入 l, r, c, 分别代表地牢的楼层数和每层地牢的长和宽,地牢由rock and point and source and key组成,你 ...
- stm32中adc的常规通道和注入通道的区别
STM32的每个ADC模块通过内部的模拟多路开关,可以切换到不同的输入通道并进行转换.STM32特别地加入了多种成组转换的模式,可以由程序设置好之后,对多个模拟通道自动地进行逐个地采样转换. 有2种划 ...
- [剑指Offer]27-二叉树的镜像
题目链接 https://www.nowcoder.com/practice/564f4c26aa584921bc75623e48ca3011?tpId=13&tqId=11171&t ...
- c#: PointToClient与PointToScreen
起因: 很早就见过这两个方法,却未曾深入研究,偶尔一用,却纠结于其转换方法,遂写Demo以求验证,记录认知以做备忘. 其为Control类的内置方法,原型为: // // 摘要: // 将指定屏幕点的 ...