您的位置:首页 > 编程语言 > Java开发

Collection容器初探之LinkedList

2017-09-13 18:07 309 查看
网上的LinkedList的源码分析大都为JDK1.6的版本,所以为双向循环链表的环形结构。但是JDK1.7的版本为双向链表。

先简单分析了解下JDK1.6版本的源码:

参考:

给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析

这篇博客分析的很彻底!!!赞。

JDK1.6~JDK1.7过程中,LinkedList从双向循环链表转换为双向链表

这次来简单了解和分析下JDK1.6的基于双向循环链表的实现:



继承自List,Deque,Cloneable,Serializible接口

public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable


双向循环链表的底层数据存储形式:

private transient Entry header = new Entry(null, null, null);
private transient int size = 0;


其中size为元素数目,header为链表的头节点,Entry为链表中的节点对象

经常使用的默认构造方法:

public LinkedList() {
//将header节点的前一节点和后一节点都设置为自身
header.next = header.previous = header;
}


Entry为LinkedList的静态内部类,定义了当前存储的节点和它相邻的两个节点:

private static class Entry {
E element;
Entry next;
Entry previous;

Entry(E element, Entry next, Entry previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}


插入:



/**
* 添加一个集合元素到list中
*/
public boolean addAll(Collection<? extends E> c) {
// 将集合元素添加到list最后的尾部
return addAll(size , c);
}

/**
* 在指定位置添加一个集合元素到list中
*/
public boolean addAll(int index, Collection<? extends E> c) {
// 越界检查
if (index < 0 || index > size)
throw new IndexOutOfBoundsException( "Index: "+index+
", Size: "+size );
Object[] a = c.toArray();
// 要插入元素的个数
int numNew = a.length ;
if (numNew==0)
return false;
modCount++;

// 找出要插入元素的前后节点
// 获取要插入index位置的下一个节点,如果index正好是lsit尾部的位置那么下一个节点就是header,否则需要查找index位置的节点
Entry<E> successor = (index== size ? header : entry(index));
// 获取要插入index位置的上一个节点,因为是插入,所以上一个点击就是未插入前下一个节点的上一个
Entry<E> predecessor = successor. previous;
// 循环插入
for (int i=0; i<numNew; i++) {
// 构造一个节点,确认自身的前后引用
Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
// 将插入位置上一个节点的下一个元素引用指向当前元素(这里不修改下一个节点的上一个元素引用,是因为下一个节点随着循环一直在变)
predecessor. next = e;
// 最后修改插入位置的上一个节点为自身,这里主要是为了下次遍历后续元素插入在当前节点的后面,确保这些元素本身的顺序
predecessor = e;
}
// 遍历完所有元素,最后修改下一个节点的上一个元素引用为遍历的最后一个元素
successor. previous = predecessor;

// 修改计数器
size += numNew;
return true;
}


删除:

/**
* 删除第一个匹配的指定元素
*/
public boolean remove(Object o) {
// 遍历链表找到要被删除的节点
if (o==null) {
for (Entry e = header .next; e != header; e = e.next ) {
if (e.element ==null) {
remove(e);
return true;
}
}
} else {
for (Entry e = header .next; e != header; e = e.next ) {
if (o.equals(e.element )) {
remove(e);
return true;
}
}
}
return false;
}

private E remove(Entry e) {
if (e == header )
throw new NoSuchElementException();

// 被删除的元素,供返回
E result = e. element;
// 下面修正前后对该节点的引用
// 将该节点的上一个节点的next指向该节点的下一个节点
e. previous.next = e.next;
// 将该节点的下一个节点的previous指向该节点的上一个节点
e. next.previous = e.previous;
// 修正该节点自身的前后引用
e. next = e.previous = null;
// 将自身置空,让gc可以尽快回收
e. element = null;
// 计数器减一
size--;
modCount++;
return result;
}


查询:

/**
* 查找指定索引位置的元素
*/
public E get( int index) {
return entry(index).element ;
}

/**
* 返回指定索引位置的节点
*/
private Entry<E> entry( int index) {
// 越界检查
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException( "Index: "+index+
", Size: "+size );
// 取出头结点
Entry<E> e = header;
// size>>1右移一位代表除以2,这里使用简单的二分方法,判断index与list的中间位置的距离
if (index < (size >> 1)) {
// 如果index距离list中间位置较近,则从头部向后遍历(next)
for (int i = 0; i <= index; i++)
e = e. next;
} else {
// 如果index距离list中间位置较远,则从头部向前遍历(previous)
for (int i = size; i > index; i--)
e = e. previous;
}
return e;
}


改动:

/**
* 修改指定位置索引位置的元素
*/
public E set( int index, E element) {
// 查找index位置的节点
Entry e = entry(index);
// 取出该节点的元素,供返回使用
E oldVal = e. element;
// 用新元素替换旧元素
e. element = element;
// 返回旧元素
return oldVal;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linkedlist jdk 链表 源码