LinkedList
非线程安全的可重复元素顺序列表
继承AbstractSequentialList;其父类为AbstractList骨架实现,该类要求子类必须重写listIterator(int index)方法(AbstractList已提供骨架实现);
实现了Cloneable,实现浅克隆;
实现了序列化接口,并自定义了readObject、writeObject方法
实现了Deque接口,提供了双端队列的相关方法,可以做为双端队列或栈使用
JDK1.6版本的LinkedList顺序列表采用双向循环链表实现,存在固定的header链表头元素,下一个为首元素,上一个为末元素
JDK1.7版本的LinkedList顺序列表采用双向链表实现,分别定义了first、last元素,以便进行正向及反向查找
该数据结构,进行下标查询操作时需要从首元素或末元素开始逐个查找(算法时间复杂度O(n));
插入、删除到指定位置时,需要先进行位置查找到指定位置元素,然后进行新增及位置链接设置操作(算法时间复杂度O(n))
仅在较前位置插入删除会较ArrayList快,由于ArrayList采用了数组快速移动方法,在数据量大时比ArrayList插入、删除慢
重点方法:
1、数据结构及构造器
(1)元素定义
JDK1.6
private static class Entry<E> {//内部静态类进行双向链表的元素定义
E element;//当前元素的内容
Entry<E> next;//指向下一个元素
Entry<E> previous;//指向上一个元素
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
JDK1.8(1.7将类名调整为Node以便与map的Entry区分,构造顺序进行了调整)
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;
}
}
(2)首尾节点记录及构造方法,与ArrayList一样,除了无参构造外也提供了拷贝构造器
JDK1.6,只定义空的header头节点,上一个是列表最后元素,下一个是列表首个元素
private transient Entry<E> header = new Entry<E>(null, null, null);//链表头
public LinkedList() {//无参构造
header.next = header.previous = header;//将链表头的next、previous都指向自己
}
public LinkedList(Collection<? extends E> c) {//拷贝构造器,支持任意集合;支持子类
this();//调用无参构造,先建立链表头
addAll(c);//然后将集合中的元素新增进来
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
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++;
Entry<E> successor = (index==size ? header : entry(index));//确认插入的后置元素,如果在末端插入则为链表头,否则需取得指定位置的元素
Entry<E> predecessor = successor.previous;//根据后置元素,确认插入的前置元素
for (int i=0; i<numNew; i++) {//循环数组
Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);//新建元素,next指向后置元素,previous指向前置元素(除最后一个外,后置元素不需要设置,保证previous对即可)
predecessor.next = e;//前置元素的next指向新建元素(从第2次循环开始,完成了上一个新增元素的next设置)
predecessor = e;//将新建元素做为下一个循环的前置元素
}
successor.previous = predecessor;//后置元素的previous设置为最后一个数组对应的新建元素
size += numNew;//增加列表长度
return true;
}
JDK1.8(1.7将数据结构调整为首末两个双向链表,首元素的上一个为null,末元素的下一个为null)
transient Node<E> first;//首元素
transient Node<E> last;//末元素
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
(3)插入操作,删除也类似
JDK1.6
public void addFirst(E e) {//实现双端队列接口,插入首元素
addBefore(e, header.next);
}
public void addLast(E e) {//实现双端队列接口,插入末元素
addBefore(e, header);
}
private Entry<E> addBefore(E e, Entry<E> entry) {//在前面插入元素
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);//新建元素
newEntry.previous.next = newEntry;//将上一个的next指向新增元素
newEntry.next.previous = newEntry;//将下一个的previous指向新增元素
size++;//列表长度增加
modCount++;
return newEntry;
}
JDK1.8
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {//将元素设置为首个元素
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;//设置为首元素
if (f == null)//第一次增加,首末元素都设置为新增元素
last = newNode;
else
f.prev = newNode;//原首元素的prev指向新增元素
size++;
modCount++;
}
public void addLast(E e) {
linkLast(e);
}
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;//原末元素的next指向新增元素
size++;
modCount++;
}
void linkBefore(E e, Node<E> succ) {//将元素插入到某个元素之前
// assert succ != null;//未进行空指针判断
final Node<E> pred = succ.prev;//上个元素
final Node<E> newNode = new Node<>(pred, e, succ);//新增元素,设置指向
succ.prev = newNode;//将某个元素的prev设置为新增元素
if (pred == null)//如果某个元素的原始上个元素为空,则为队首,将首元素设置为新增元素
first = newNode;
else
pred.next = newNode;//上个元素next指向新增元素
size++;
modCount++;
}
(4)查找元素
JDK1.6
private Entry<E> entry(int index) {//取得指定位置的元素
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) {//判断查找顺序,如果序号在前一半则从前往后查找
for (int i = 0; i <= index; i++)
e = e.next;
} else {//从后往前查找
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
JDK1.8
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;
}
}
2、迭代器模式
LinkedList使用内部类实现了具体的fast-fail迭代器,并且保持iterator与listIterator创建相同的ListItr迭代器。fail-fast 迭代器,这意味着它假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast 迭代器检测到在迭代过程中进行了更改操作(链表结构的更改),那么会抛出 ConcurrentModificationException 。
JDK1.6版本(1.8与此类似,只是数据结构不同导致变化,不做列举)
public ListIterator<E> listIterator(int index) {//按父类要求重写listIterator
return new ListItr(index);
}
private class ListItr implements ListIterator<E> {//内部类实现具体的迭代器
private Entry<E> lastReturned = header;//最近返回元素;默认为链表头,next、previous方法会变动该元素,remove、add方法调用后重置为链表头
private Entry<E> next;//下一个元素
private int nextIndex;//下一个位置
private int expectedModCount = modCount;
ListItr(int index) {//迭代器构造,建立基于双向循环链表的迭代器
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
if (index < (size >> 1)) {//如果序号在前一半则从前往后建立
next = header.next;
for (nextIndex=0; nextIndex<index; nextIndex++)
next = next.next;
} else {//否则从后往前建立
next = header;
for (nextIndex=size; nextIndex>index; nextIndex--)
next = next.previous;
}
}
public boolean hasNext() {
return nextIndex != size;
}
public E next() {//下一个元素
checkForComodification();
if (nextIndex == size)
throw new NoSuchElementException();
lastReturned = next;//最近返回元素设置为下一元素
next = next.next;//next移动到下一元素
nextIndex++;//下一个位置更新
return lastReturned.element;
}
public boolean hasPrevious() {
return nextIndex != 0;//默认0,第0个元素没有上一元素
}
public E previous() {//上一个元素
if (nextIndex == 0)
throw new NoSuchElementException();
lastReturned = next = next.previous;//最近返回元素设置为上一元素,next移动到上一元素
nextIndex--;//下一个位置更新
checkForComodification();//其他都先检查,应当写到前面,1.7已修正
return lastReturned.element;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex-1;
}
public void remove() {//删除最近返回元素
checkForComodification();
Entry<E> lastNext = lastReturned.next;
try {
LinkedList.this.remove(lastReturned);//清空元素
} catch (NoSuchElementException e) {
throw new IllegalStateException();
}
if (next==lastReturned)
next = lastNext;//如果next被删除,重置next
else
nextIndex--;//否则减少index(此情况只有上一次调用next()发生,目前位置肯定在lastReturned后面)
lastReturned = header;//重置最近返回元素为链表头
expectedModCount++;
}
public void set(E e) {//替换最近返回元素
if (lastReturned == header)//链表头不允许替换
throw new IllegalStateException();
checkForComodification();
lastReturned.element = e;//set方法由于不改变链表结构,不更新expectedModCount/modCount,但仍需check
}
public void add(E e) {//在下个元素前方增加元素
checkForComodification();
lastReturned = header;
addBefore(e, next);
nextIndex++;
expectedModCount++;
}
final void checkForComodification() {//fast-fail判断,失败抛异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
3、几个双端队列方法及区别
(1)addFirst/offerFirtst;addLast/offerLast,均为插入列表的首末元素,add方法没有返回值,offer方法返回插入成功失败
(2)removeFirst/pollFirst;removeLast/pollLast,均为删除列表的首末元素,并且都返回删除元素数据,但空列表时remove抛异常,poll返回null
(3)getFirst/peekFirst;getLast/peekLast,均为取得列表的首末元素,并且都返回删除元素数据,但空列表时get抛异常,peek返回null
(4)push调用的addFirst;pop调用的removeFirst
4、实现双端队列特殊的反向迭代器
JDK1.6与JDK1.8一致
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();
}
}