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

【JDK1.6源码阅读】LinkedList

2014-11-19 23:10 344 查看

1.概述

LinkedList是List的接口实现类,实现了所有可选的list操作且允许所有的元素(包括null值)。除了实现List接口之外,LinkedList类提供了统一的命名方法为了在列表的头部和尾部get、remove、insert元素。这些操作允许链表可以作为stack(栈)、Queue(队列)、Deque(双端队列)。

LinkedList也实现了Deque接口,为add、poll(轮询?)和其他stack、deque方法提供了先进先出(first-in-first-out)队列操作。

所有的这些操作实现作为双向列表是可被预估到的。,无论是不是接近指定的索引,这些操作都会从表头或表尾开始轮询链表。

注意,这些实现并不是同步的。如果多个线程同时访问一个链表,至少有一个线程结构上修改链表,那必须表面上看起来是同步的。(结构上的修改是指添加或删除一个或多个元素,仅仅设置一个元素的值并不是结构上的修改)。这可以非常典型地通过同步自然封装列表的某些对象达到。

如果没有这样的对象存在,列表就应该用Collections.synchronizedLis方法t来封装。为了偶然地不同步访问列表,这个应该在创建的时候就被做了。

List list = Collections.synchronizedList(new LinkedList(...));


LinkedList类的迭代器和迭代方法返回的迭代器都是快速失败的(fail-fast),所以在迭代器被创立之后,如果对列表进行结构性的修改,除非通过迭代器本身的remove方法,迭代器都会抛出一个ConcurrentModificationException异常。因此,面对并发修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

2.数据结构

<span style="white-space:pre">	</span>private transient Entry<E> header = new Entry<E>(null, null, null);
<span style="white-space:pre">	</span>private transient int size = 0;


<span style="white-space:pre">	</span>private static class Entry<E> {
<span style="white-space:pre">	</span>E element;
<span style="white-space:pre">	</span>Entry<E> next;
<span style="white-space:pre">	</span>Entry<E> previous;

<span style="white-space:pre">	</span>Entry(E element, Entry<E> next, Entry<E> previous) {
<span style="white-space:pre">	</span>  this.element = element;
<span style="white-space:pre">	</span>  this.next = next;
<span style="white-space:pre">	</span>  this.previous = previous;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span> }


从源码上可以看出,LinkedList就是一个双向链表。实体定义为元素、上一entry、下一entry。

LinkedList有一个头entry,头元素的next指向第一个entry,previous指向最后一个entry。

3.存取操作

(1)contains
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o==null) {
for (Entry e = header.next; e != header; e = e.next) {
if (e.element==null)
return index;
index++;
}
} else {
for (Entry e = header.next; e != header; e = e.next) {
if (o.equals(e.element))
return index;
index++;
}
}
return -1;
}
判断一个对象是否在linkedList中,就是找出这个对象在linkedList中的索引位置,如果找不到,返回-1.
(2)add:插入列表尾部
public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
add(E e):默认插入头entry之前,创建一个待插入的新entry,next指向头entry,previous指向连表末尾。插入的操作就是,把原链表末尾entry的next指向新entry,头entry的previous指向新entry,同时把size增加1,modCount增加1。
(3)add:指定位置插入
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
}


add(int index, E element):如果待插入位置的索引等于链表的大小,就是要插入链表的尾部,与add(E e)方法相同。
如果不是插入尾部,而是插入链表其他位置,就要根据索引找出所在位置的entry(查找方法,下面会有讲解)。创建一个待插入的新entry,next指向原index位置的entry,previous指向原index-1位置的entry;原index-1位置的entry的next指向新entry,原index位置的previous指向新entry。这样index-1位置的entry没变,index位置变成了新entry,原index位置的entry变成了index+1。
(4)remove
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();

E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}




移除列表中首先出现的指定的元素,如果没出现,列表将不会变化。
从头entry开始查找,查找到与指定元素相等的entry,然后移除这个entry;把待移除entry的上一个entry的next指向entry的下一个entry,待移除的entry的下一个entry的previous指向待移除entry的上一个entry,然后把待移除的entry置为null,size大小减1,modCount增加1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: