JDK1.8源码LinkedList
引用博文链接 : https://www.cnblogs.com/leskang/p/6029780.html
LinkedList继承了 AbstractSequentialList抽象类,而不是像 ArrayList和 Vector那样实现 AbstractList,正如其名,它提供了对序列的连续访问的抽象:
LinkedList的底层是 Deque双向链表,实现了 Deque接口,而 Deque接口继承于 Queue接口,因此,在java中,如果要实现队列,一般都使用 LinkedList来实现。
LinkedList是非线程安全的,如果要想线程安全必须在创建的时候就采用线程安全的方式创建实例:
List list = Collections.synchronizedList(new LinkedList(...));
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{ //LinkedList中元素个数
transient int size = 0; //默认构造函数:创建一个空的链表
public LinkedList() {
} .....
}
Node节点:
Node节点 一共有三个属性:item代表节点值,prev代表节点的前一个节点,next代表节点的后一个节点。
每个结点都有一个前驱和后继节点。
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;
}
} //指向链表的第一个节点
transient Node<E> first; //指向链表的最后一个节点
transient Node<E> last;
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);//建立节点对象,前一个(perv属性)是last,后一个(next属性)是null,中间item数据是输入的参数e
last = newNode;
if (l == null)
first = newNode; //如果最后一个节点 为null表示链表为空,则添加数据时将新加的数据作为第一个节点
else
l.next = newNode; //如果链表不为空则将最后一个链表的next属性指向新添加的节点
size++; //链表长度自增1
modCount++;
}
LInkedList添加操作时每个新添加的对象都会被放到新建的Node对象中,Node对象中包含加入的对象和前后指针属性(pred和next,pred和next其实都是Node对象,直接存放的就是当前对象的前一个节点对象和后一个节点对象,最后一个及节点的next值为null),然后将原来最后一个last节点的next指针指向新加入的对象,即可!
void addFirst(E e)方法,在链表的头添加一个新节点对象;void add(int index, E element) ,在链表的index位置添加一个新节点对象;void addLast(E e),在链表的尾部添加一个新节点对象。
addAll:
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
} public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//判断index是否越界,越界则抛出异常
Object[] a = c.toArray();
int numNew = a.length;//要插入的集合的长度
if (numNew == 0)
return false;
Node<E> pred, succ;//声明pred和succ两个Node对象,用于标识要插入元素的前一个节点和后一个节点
if (index == size) { //如果size等于原数组长度则表示在结尾添加
succ = null;
pred = last;
} else {
succ = node(index);//index位置上的Node对象
pred = succ.prev;
}
for (Object o : a) { //遍历要插入的集合
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode; //如果要插入的位置的前一个节点为null表示是第一个节点,则直接将newNode赋给第一个节点
else
pred.next = newNode; //将要插入的集合元素节点对象赋给此位置原节点对象的前一个对象的后一个,即更改前一个节点对象的next指针指到新插入的节点上
pred = newNode;//更改指向后将新节点对象赋给pred作为下次循环中新插入节点的前一个对象节点,依次循环
}
//此时pred代表集合元素的插入完后的最后一个节点对象
if (succ == null) { //结尾添加的话在添加完集合元素后将最后一个集合的节点对象pred作为last
last = pred;
} else {
pred.next = succ;//将集合元素的最后一个节点对象的next指针指向原index位置上的Node对象
succ.prev = pred;//将原index位置上的pred指针对象指向集合的最后一个对象
}
size += numNew;
modCount++;
return true;
}
LinkedList在某个位置插入元素是通过将原位置节点的前一个节点的后一个指针指向新插入的元素,然后将原位置的节点的前一个指针指向新元素来实现的,相当于插入元素后后面的元素后移了,但是不是像ArrayList那样将所有插入位置后面的元素都后移,此处只是改变其前后节点的指向。所以LinkedList修改的速度比ArrayList快!
删除:
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
} E unlink(Node<E> x) {
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;//如果next为null表示是最后一个元素,直接将pred节点赋给last
} else {
next.prev = prev;
x.next = null;//将传入节点的后一个节点置空,使其后一个节点指针不指向任何元素
}
x.item = null;//将传入的节点对象上的对象置空,也就是整个Node对象中的属性都为空了,后面会被GC回收
size--;
modCount++;
return element;
} //查询指定节点
Node<E> node(int index) {
if (index < (size >> 1)) { //判断index是否小于size的一半,如果小于则从头遍历节点,否则从结尾遍历节点
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; //从first第一个节点开始,依次将后一个节点赋给x,直到x等于index上的节点
return x; //返回index位置上的Node对象,下同理
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
LinkedList删除操作是通过将index位置上的前一个节点的next执行index位置的后一个节点,同时将后一个节点的pred指向前一个节点,然后将index位置上的Node节点对象属性都置空来实现的,置空后的Node对象会被GC垃圾回收期回收掉。
E removeFirst() ,删除链表第一个节点对象, E removeLast() ,删除链表最后一个节点对象。
public boolean remove(Object o) { //删除具体节点对象item
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;
}
替换:
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);//获取index位置上的节点对象
E oldVal = x.item;
x.item = element;//将新插入的元素直接赋给此位置上节点对象的item属性即可
return oldVal;
}
LinkedList中的实际数据保存在节点对象的item属性中,节点的另外两个属性不变,这再次说明LinkedList修改的速度比ArrayList修改速度快!
查询:
public E get(int index) {
checkElementIndex(index);
return node(index).item; //获取节点对象的item属性
}
查询操作是通过Node<E> node(int index)方法实现的,该方法是通过先判断index位置是在整个链表的前一半还是后一半来遍历前一半或者后一半元素来获取index位置上的元素,
说明LinkedList查询速度比ArrayList慢!
E getFirst() ,查询链表第一个节点对象的item, E getLast() ,查询链表最后一个节点对象的item,
包含:
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 {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -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++;
}
clear()会把节点所有属性都置为null,确保资源得到释放,只是清空了LinkedList对于里面所含有对象指针的释放。
LinkedList只是把自己中每个对象的引用设为null没错,那些不再被引用的对象是会被回收了,但是如果有一些对象外部还在继续用的话,也是回收不了的。例如:
public static void main(String[] args) {
LinkedList<String> linklist = new LinkedList<>();
String se ="gg";
linklist.add("a");
linklist.add("b");
linklist.add(se);
linklist.clear();
System.out.println("长度:"+linklist.size());
System.out.println("内容:"+linklist.toString());
System.out.println("se:"+se);
}
结果:
长度:0
内容:[]
se:gg
Queue operations
//获取但不移除链表的头部第一个节点对象的item
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;//如果第一个节点对象为null,返回null
} //获取但不移除链表的头部第一个节点对象的item
public E element() {
return getFirst();
} public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();//如果第一个节点对象为null,返回异常信息
return f.item;
} //获取并且移除链表的头部第一个节点对象的item
public E poll() {
final Node<E> f = first;
return (f == null) ? null : 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; //第一个节点对象item,next置为null
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null; //下一个节点对象的prev置为null
size--;
modCount++;
return element;
} //获取并且移除链表的头部第一个节点对象的item
public E remove() {
return removeFirst();
} public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
} //在链表的尾部添加一个新节点对象
public boolean offer(E e) {
return add(e);
}
Deque operations 双向链表操作
LinkedList implements Deque<E>,interface Deque<E> extends Queue<E>,所以Deque operation的很多方法和Queue operation的方法一样,例如E peekFirst() 与E peek() 功能一样。
//从链表头部遍历,移除第一出现节点对象的item==o的节点
public boolean removeFirstOccurrence(Object o) {
return remove(o);
} //从链表尾部遍历,移除第一出现节点对象的item==o的节点
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
ListIterator<E> listIterator(int index):
//返回此列表中的元素的列表迭代器(按适当顺序:从列表中指定位置开始)
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index); //调用内部类ListItr的匿名对象
} //把ListIterator接口送给内部类实现是为了与Iterator接口兼容,因为ListIterator接口继承自Iterator接口
private class ListItr implements ListIterator<E> { //实现了ListIterator接口
private Node<E> lastReturned; //指向上一个返回得到的元素
private Node<E> next; //指向下一个未涉足的元素
private int nextIndex;
// modCount :已从结构上修改 此列表的次数。从结构上修改是指更改列表的大小,
//或者打乱列表,从而使正在进行的迭代产生错误的结果。
//在迭代期间面临并发修改时,它提供了快速失败 行为,而不是非确定性行为。
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;
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;
} public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException(); Node<E> lastNext = lastReturned.next;
unlink(lastReturned); //删除外部元素modCount++
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++; //所以内部的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++;
} public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
} //外部结构修改则迭代快速失败fast-fails
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ListIterator<E> listIterator(int index),得到从index开始的迭代器(可以正向,也可以反向)
正向:
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
ListIterator<String> listit = list.listIterator(2);
while(listit.hasNext()) {
System.out.println("第一次" + listit.next());
}
}
结果:
第一次abc3
第一次abc4
反向:
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
list.add("abc5");
ListIterator<String> listit = list.listIterator(2);
while(listit.hasPrevious()){
System.out.println(listit.previous());
}
}
结果:
abc2
abc1
listIterator可以实现对象的删除,添加,但是Iterator可以遍历,删除
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
ListIterator<String> listit = list.listIterator();
while(listit.hasNext()){
if(listit.next().equals("abc3")){
listit.remove();
listit.add("itcast");
}
}
System.out.println(list);
}
结果:
[abc1, abc2, itcast, abc4]
Iterator:
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
Iterator<String> listit = list.iterator();
while(listit.hasNext()){
if(listit.next().equals("abc3")){
listit.remove();
list.add("new");
break; //添加成功以后直接break;不继续判断就可以不出现异常
}
}
System.out.println(list);
}
部分功能:
//反向迭代器
public Iterator<E> descendingIterator() {
return new DescendingIterator();
} private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
} @SuppressWarnings("unchecked")
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
} //clone链表但是节点对象是添加,而不是clone
public Object clone() {
LinkedList<E> clone = superClone(); // Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0; // Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item); return clone;
} //包含链表节点对象item的数组
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
Spliterator 是 Java 8 引入的新接口,顾名思义,Spliterator 可以理解为 Iterator 的 Split 版本(但用途要丰富很多)。
使用 Iterator 的时候,我们可以顺序地遍历容器中的元素,使用 Spliterator 的时候,我们可以将元素分割成多份,分别交于不于的线程去遍历,以提高效率。
使用 Spliterator 每次可以处理某个元素集合中的一个元素 — 不是从 Spliterator 中获取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法对元素应用操作。
但 Spliterator 还可以用于估计其中保存的元素数量,而且还可以像细胞分裂一样变为一分为二。这些新增加的能力让流并行处理代码可以很方便地将工作分布到多个可用线程上完成。
转自 http://blog.sina.com.cn/s/blog_3fe961ae0102wxdb.html
JDK1.8源码LinkedList的更多相关文章
- JDK1.8源码学习-LinkedList
JDK1.8源码学习-LinkedList 目录 一.LinkedList简介 LinkedList是一个继承于AbstractSequentialList的双向链表,是可以在任意位置进行插入和移除操 ...
- 集合之HashSet(含JDK1.8源码分析)
一.前言 我们已经分析了List接口下的ArrayList和LinkedList,以及Map接口下的HashMap.LinkedHashMap.TreeMap,接下来看的是Set接口下HashSet和 ...
- 【集合框架】JDK1.8源码分析HashSet && LinkedHashSet(八)
一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...
- 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)
一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...
- JDK1.8源码阅读系列之三:Vector
本篇随笔主要描述的是我阅读 Vector 源码期间的对于 Vector 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出- 先来看一下 Vector 的继承图: 可以看出,Vector 的直 ...
- 【集合框架】JDK1.8源码分析之HashMap(一) 转载
[集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...
- 【集合框架】JDK1.8源码分析之ArrayList详解(一)
[集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...
- 集合之TreeSet(含JDK1.8源码分析)
一.前言 前面分析了Set接口下的hashSet和linkedHashSet,下面接着来看treeSet,treeSet的底层实现是基于treeMap的. 四个关注点在treeSet上的答案 二.tr ...
- 集合之LinkedHashSet(含JDK1.8源码分析)
一.前言 上篇已经分析了Set接口下HashSet,我们发现其操作都是基于hashMap的,接下来看LinkedHashSet,其底层实现都是基于linkedHashMap的. 二.linkedHas ...
随机推荐
- 【转】Latex编译报错后中断编译并改正,然后重复出现不明原因报错的解决方法
转自:https://www.douban.com/note/419828344/ 目录: 一.问题描述 二.测试情况(可以跳过,直接看建议) 三.建议 四.参考资料 正文: 问题描述: 错漏某个符号 ...
- Fortinet Security Fabric
Fortinet Security Fabric 这个世界从不固步自封.在技术方面,这意味着解决方案供应商必须保持不断创新和探索才能实现生存与发展. 在网络安全领域,这更是至理名言.许多黑客都是才华横 ...
- 【XSY2307】树的难题
Description Solution 看到这种路径统计问题,一般就想到要用点分治去做. 对于每个重心\(u\),统计经过\(u\)的合法的路径之中的最大值. 第一类路径是从\(u\)出发的,直接逐 ...
- BZOJ4036 [HAOI2015]按位或 【minmax容斥 + 期望 + FWT】
题目链接 BZOJ4036 题解 好套路的题啊,,, 我们要求的,实际上是一个集合\(n\)个\(1\)中最晚出现的\(1\)的期望时间 显然\(minmax\)容斥 \[E(max\{S\}) = ...
- 【bzoj3926】 Zjoi2015—诸神眷顾的幻想乡
http://www.lydsy.com/JudgeOnline/problem.php?id=3926 (题目链接) 题意 给出一棵树,每个节点有一个编号,范围在${[0,9]}$.一个序列是指树上 ...
- 正确理解 LEAL (Load Effective Address) 指令
LEAL: leal S, D -> D ← &S 在 CSAPP (Computer Systems: A Programmer’s Perspective) 中,对 LE ...
- bzoj 1825: [JSOI2010]蔬菜庆典
1825: [JSOI2010]蔬菜庆典 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 112 Solved: 45[Submit][Status][ ...
- MySQL 第七篇:视图、触发器、事务、存储过程、函数
一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...
- Tcp协议三次握手四次挥手
一.什么是TCP TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的.可靠的. 基于IP的传输层协议.TCP在IP报文的协议号是6. 二.什 ...
- java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解
java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解 我用的是office word 2016版 XWPFRun是XWPFDocument中的一段文本对象(就是一段文 ...