java1.8 常用集合源码学习:LinkedList

来源:互联网 发布:3d studio max mac版 编辑:程序博客网 时间:2024/06/11 16:15
1、api
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 getremove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列双端队列
此类实现 Deque 接口,为 addpoll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list = Collections.synchronizedList(new LinkedList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 Java Collections Framework 的成员。

2、源码学习

使用size记录list的大小
transient intsize=0;

list的头结点
transientNode<E>first;

list尾节点
transientNode<E>last;

用给定的集合创建一个新list
publicLinkedList(Collection<?extendsE> c) {
this();
addAll(c);
}

最终调用了addAll方法,这个方法在指定的位置将集合c插入当前的list。
public booleanaddAll(intindex,Collection<?extendsE> c) {
//首先调用checkPositionIndex方法检测索引是否越界(方法内部判断index >= 0 && index <= size),如果越界抛出IndexOutOfBoundsException异常。
checkPositionIndex(index);
//调用集合的toArray方法创建一个数组,并且记录数组大小numNew,如果numNew0则直接退出方法返回false
Object[] a = c.toArray();
intnumNew = a.length;
if(numNew ==0)
return false;
//predsucc分别代表index位置的前节点的元素和当前节点的元素
//新集合的所有元素都会插入到pred的后面,如果succ不为null(如果插入位置index恰好等于list的长度size,则succnull,即没有当前元素),则在新集合全部插入完毕后,会将succ重新放到最后一个新插入元素的后面
Node<E> pred,succ;
if(index ==size) {
succ =null;
pred =last;
}else{
succ = node(index);
pred = succ.prev;
}
//遍历新集合的数组,
for(Object o : a) {
//创建一个新的Node,将前一个节点的引用pred和数据e存入
@SuppressWarnings("unchecked")Ee = (E) o;
Node<E> newNode = newNode<>(pred,e, null);
if(pred ==null)
first= newNode;
else
pred.next= newNode;
//pred指向新创建的Node
pred = newNode;
}
//新集合数据全部插入完毕后,将succ放到最新插入的节点后,如果succnull,则简单地把last节点指向最新插入的节点
if(succ ==null) {
last= pred;
}else{
pred.next= succ;
succ.prev= pred;
}
//更新list的长度和modCount
size+= numNew;
modCount++;
return true;
}

将一个新元素e放到list的最前端
private voidlinkFirst(Ee) {
//取得当前的队首节点f
finalNode<E> f = first;
//创建新节点newNodw,将newNodw的后节点指向f
finalNode<E> newNode = newNode<>(null,e,f);
//newNode指定为新的队首元素
first= newNode;
//如果原本list为空列表,则队尾节点也指向newNode,否则将旧队首的前一节点指向newNodw
if(f ==null)
last= newNode;
else
f.prev= newNode;
//更新sizemodCount
size++;
modCount++;
}

linkLast和linkFirst实现非常类似,把新元素加到list的尾部
voidlinkLast(Ee) {
finalNode<E> l = last;
finalNode<E> newNode = newNode<>(l,e, null);
last= newNode;
if(l ==null)
first= newNode;
else
l.next= newNode;
size++;
modCount++;
}

将元素e插入到指定节点succ的前面
voidlinkBefore(Ee,Node<E> succ) {
// assert succ != null;
//取得当前元素succ的前节点pred
finalNode<E> pred = succ.prev;
//使用元素e创建新节点newNodenewNode的前节点指向succ的前节点,newNode的后节点指向succ,也就是将newNode放在了succ的前面
finalNode<E> newNode = newNode<>(pred,e,succ);
//succ的前节点指向newNode
succ.prev= newNode;
//succ的前节点的后节点指向newNode,到这里,就完全完成了插入
if(pred ==null)
first= newNode;
else
pred.next= newNode;
//更新sizemodCount
size++;
modCount++;
}

删除指定节点f以前的所有节点,并且返回f节点存储的数据,这个方法用于删除头节点
privateEunlinkFirst(Node<E> f) {
// assert f == first && f != null;
//取得f存储的元素,用于返回值
finalEelement = f.item;
//取得f的下一个节点next
finalNode<E> next = f.next;
//f所存数据和f的下一节点都置位null
f.item=null;
f.next=null;// help GC
first= next;
//next节点的前节点置位null,即从list中完全去掉了f节点
if(next ==null)
last=null;
else
next.prev=null;
//更新sizemodCount,返回f存储的数据
size--;
modCount++;
returnelement;
}

unlinkLast和unlinkFirst方法非常类似,只是unlinkLast方法用于删除list中指定节点以后的所有节点并返回指定节点所存数据,这个方法用于删除尾节点
privateEunlinkLast(Node<E> l) {
// assert l == last && l != null;
finalEelement = l.item;
finalNode<E> prev = l.prev;
l.item=null;
l.prev=null;// help GC
last= prev;
if(prev ==null)
first=null;
else
prev.next=null;
size--;
modCount++;
returnelement;
}

从list链表中删除x节点,并返回x节点存储的数据
Eunlink(Node<E> x) {
// assert x != null;
//取得x节点存储的数据,用于返回值
finalEelement = x.item;
//取得x节点的后节点和前节点,用于直接将他们连接在一起
finalNode<E> next = x.next;
finalNode<E> prev = x.prev;
//x的前节点的向后的指针直接指向x的后节点(跳过x
if(prev ==null) {
first= next;
}else{
prev.next= next;
x.prev=null;
}
//x的后节点的向前的指针直接指向x的前节点(跳过x
if(next ==null) {
last= prev;
}else{
next.prev= prev;
x.next=null;
}
//更新sizemodCount,并返回x中的数据
x.item=null;
size--;
modCount++;
returnelement;
}

判断list中是否包含元素o,调用了indexOf方法
public booleancontains(Object o) {
returnindexOf(o) != -1;
}

indexOf方法直接从头至尾遍历了list中的节点去寻找指定的Object。lastIndexOf方法和此方法类似,只是从后向前遍历
public intindexOf(Object o) {
intindex =0;
if(o ==null) {
for(Node<E> x = first;x !=null;x = x.next) {
if(x.item==null)
returnindex;
index++;
}
}else{
for(Node<E> x = first;x !=null;x = x.next) {
if(o.equals(x.item))
returnindex;
index++;
}
}
return-1;
}

remove方法也是从头至尾遍历list,找到需要删除的Object,然后调用unlink方法从列表中将其删除
public booleanremove(Object o) {
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;
}

从指定位置取数据,内部调用了node方法
publicEget(intindex) {
checkElementIndex(index);
returnnode(index).item;
}

修改指定位置数据,内部调用了node方法
publicEset(intindex,Eelement) {
checkElementIndex(index);
Node<E> x = node(index);
EoldVal = x.item;
x.item= element;
returnoldVal;
}

在指定位置增加一个元素,内部调用了node方法、linkLast方法、linkBefore方法
public voidadd(intindex,Eelement) {
checkPositionIndex(index);

if(index ==size)
linkLast(element);
else
linkBefore(element,node(index));
}

取得指定位置的非null节点,
Node<E>node(intindex) {
// assert isElementIndex(index);
//如果index索引比size的一半小,则从前向后查找,反之则从后向前查找
if(index < (size>>1)) {
//从首节点向后遍历
Node<E> x = first;
for(inti =0;i < index;i++)
x = x.next;
returnx;
}else{
//从尾节点向前遍历
Node<E> x = last;
for(inti =size-1;i > index;i--)
x = x.prev;
returnx;
}
}

Node类包含向前和向后的指针,以及存储的数据
private static classNode<E> {
Eitem;
Node<E>next;
Node<E>prev;

Node(Node<E> prev,Eelement,Node<E> next) {
this.item= element;
this.next= next;
this.prev= prev;
}
}

按链表顺序转换成数组
publicObject[]toArray() {
Object[] result =newObject[size];
inti =0;
for(Node<E> x = first;x !=null;x = x.next)
result[i++] = x.item;
returnresult;
}

spliterator相关以后统一说