LinkedList采用的是双向链表模式,而每一个节点就是一个LinkedList类的一个私有静态的内部类Entry,Entry含有三个成员:E element (E就是申明变量时需要的泛型参数)、Entry next、Entry previous。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
继承至AbstractSequentialList(此类提供了List接口的骨干实现,从而最大限度减少了实现受“连续访问”数据存储支持的此接口的所需工作)
在实现的接口中Cloneable表示LinkedList能够被复制,实现java.io.Serializable表示此类能被序列化
private transient Entry<E> header = new Entry<E>(null, null, null); private transient int size = 0;
关键字transient的作用是表示一个域不是该对象串行化(序列化)的一部分,当这个对象串行化的时候transient申明的变量不包括在串行化的表示中,而非transient的变量会被包括进去。
/** * Constructs an empty list. //构造一个空链表 */ public LinkedList() { header.next = header.previous = header; } public LinkedList(Collection<? extends E> c) { this(); //首先构造一个空链表 addAll(c); //将初始化的Collection加入到此链表中 }
LinkedList提供像链表中增加元素的方法有很多,例如:add(e)、add(int,e)、addAll(int,Collection)、addAll(Collection),只需分析add(int,e)以及addAll(int,Collection)即可,其余的都是基于这两个函数实现的。
public void add(int index, E element) { addBefore(element, (index==size ? header : entry(index))); }
entry(index)将在链表中返回位置是index的链表元素,因为链表不像数组可以随机读取,对于链表来说得到某一位置的元素只能一个一个遍历得到位置,然后将前后位置元素的索引改变,而LinkedList是一个双向链表,在寻找插入位置的时候,会判断是位于前半部分还是后半部分,如果是前半部分则从第一个往后遍历,如果是后半部分则有最后一个往前遍历,然后将位置属于index的元素返回。
addBefore(E e,Entry entry)
private Entry<E> addBefore(E e, Entry<E> entry) { //将newEntry的next指向entry,previous指向尾部entry的尾部 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous); //将entry前面元素的后驱指向newEntry newEntry.previous.next = newEntry; //将entry元素的前驱指向newEntry newEntry.next.previous = newEntry; size++; modCount++; return newEntry; }
对于add(E e)则是调用方法addBefore(e,header)
public boolean addAll(int index, Collection<? extends E> c) { //验证链表的长度与要插入的位置的大小 if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); //将要插入的Collection转换成数组 Object[] a = c.toArray(); int numNew = a.length; if (numNew==0) return false; modCount++;//是AbstractList中得一个字段,用来记录list结构变化的次数 //得到index位置的entry Entry<E> successor = (index==size ? header : entry(index)); Entry<E> predecessor = successor.previous; //依次将c插入原链表中 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; }
仅分析remove(Entry<E> e),因为其余有关删除的操作都是基于这个函数实现的
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; }
与ArrayList不同的是LinkedList实现了自己的迭代器,实现了ListIterator接口,ListIterator接口继承至Iterator接口,LinkedList,没有直接使用AbstractList虚类中的实现,LinkedList内部自定义了一个内部类ListItr,该类实现了ListIterator接口,专门对LinkedList进行迭代,但是要注意的是因为ArrayList与LinkedList的迭代器的功能和特点都是一样的,所以符合ArrayList迭代器的特点都符合LinkedList的特点。AbstractList实现了两个迭代器分别是Itr以及ListItr,其中ListItr更适用于LinkedList,因为LinkedList是一个双向链表。以下是LinkedList中的:ListIterator的实现:
private class LinkedList implements ListIterator<E> { //最近一次next或previous操作所返回的元素,初始为头结点,表示链表的开始与结尾 private Entry<E> lastReturned = header; //next操作所返回的节点 private Entry<E> next; //记录当前next指向的entry的位置,与next保持一致 private int nextIndex; //检测外界是否改变结合的结构 private int expectedModCount = modCount; //得到第index位置的元素(entry) ListItr(int index) { //此方法运行完毕next指向的就是index位置的元素 if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); if (index < (size >> 1)) { //当index小于size的一半是在前半部找 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的结果是next与lastReturned不相等 nextIndex++; return lastReturned.element; } public boolean hasPrevious() { return nextIndex != 0; } public E previous() { if (nextIndex == 0) throw new NoSuchElementException(); //执行previous的结果是next与lastReturned不相等 lastReturned = next = next.previous; nextIndex--; checkForComodification(); 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; else nextIndex--; //每次remove操作之后就会将lastReturned指向header lastReturned = header; expectedModCount++; } public void set(E e) { if (lastReturned == header) throw new IllegalStateException(); checkForComodification(); lastReturned.element = e; } public void add(E e) { checkForComodification(); //每次add操作之后就会将lastReturned指向header lastReturned = header; addBefore(e, next); nextIndex++; expectedModCount++; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
从源码中可以看出:
1. 刚实例化的迭代器lastReturned是指向header节点的,是不能马上进行删除跟修改操作的,需要首先进行后移或者前移操作;
2. 只要lastReturned指向header是不能进行删除以及修改操作的,所以以下成立:
可以对迭代器进行连续的添加或者修改,但是不能进行连续的删除,因为删除后lastReturned指向header是不能再删除的;
进行添加操作后不能立刻进行删除或者修改操作;
进行删除操作操作后可以进行添加操作,但是不能进行修改操作;
进行修改后是可以进行删除和增加操作的;