【源码解析】JDK源码之LinkedList
2017-04-18 10:00
330 查看
LinkedList源码,基于JDK1.6.43
有序的列表,其内部是一个双向循环链表的实现,其在JavaDocs中说明既可以作为栈来使用也可以作为队列来使用。
用于保存链表的头指针
private transient Entry<E> header = new Entry<E>(null, null, null);
用于保存链表的大小
private transient int size = 0;
默认构造函数,初始化链表的前后指针,构成初始的环路
另一个构造器,传入一个已有的集合,根据已有的集合构建列表,首先调用默认的构造器,然后添加已有集合中的所有元素,添加方法后续说明。
为了便于说明,提前来看一下一个内部类Entry,其中有三个属性,element用于存储数据,next是后指针,previous是前指针。另外一个有参的构造方法。
获得首个元素,实现很简单,就是返回头指针的后指针指向的元素的元素值。
同理可得获得最后一个元素的代码实现。
这是一个私有方法,不会被外部直接调用,目的是删除一个元素。删除的时候就是双向循环链表的删除,前后指针的移动,以及为了垃圾回收而写的前后指针置为null,最后返回被删除的元素。
下面两段源码不解释。
实际中会被外部调用的删除方法,特殊在于对于null值的判断采用不同的分支。实际调用的依然是查找到位置后调用私有的remove方法。
判断元素的位置,和上面的方法类似,依然是对null值的特殊处理。
这里有一个非常有趣的私有方法,entry(index)用于获得某个索引的Entry对象。为什么说有趣呢?因为在判断完索引是否越界后进行了一步判断,就是索引位置在列表中的大体位置,是在前一半还是后一半,然后采用不同的遍历顺序,这样可以加快一倍的查找速度。
下面的公共方法就是调用了上面的私有方法,实现快速的对某个索引位置元素的删除。
下面的设置方法也是调用上面的私有方法,先查找到元素,再进行元素值的替换覆盖。
那这样get方法也很好理解了吧。
清空操作不能直接修改头指针的前后指针,这样指针虽然可以置空,但是其链表中的元素依然在内存中占有一定的位置,所以代码中的实现是优秀的,遍历链表将每个元素置为null。保证的后续gc的工作。
下面是一个私有方法,从方法名中可以看到方法的作用是在e的元素之前添加entry元素。实际就是链表的插入操作,但是要保证head头指针的后指针指向最先插入的元素,head头指针的前指针指向最后插入的元素。
众望所归的add方法,调用上面的私有方法addBefore。
这个方法用于添加一个元素到列表的最前面,实际就是在头指针的后指针之前添加元素。
下面的方法在列表末尾添加元素,其实和普通的add方法相同,但是没有返回值。
下面的方法用于在某个index后加入一个已有列表的集合,特殊在于index位置指针的处理,只是在最后一行代码中进行指针的修改指向。
不带index参数的addAll方法就是在列表的最末尾插入元素,所以index就是大小size。
下面是源码中为了实现队列功能而实现的方法,比较简单就不贴代码了。
下面是其内部自带的迭代器,这个迭代器也会抛出ConcurrentModificationException异常。其实现方法和其他的迭代器相同,依然是比对修改的次数。特殊在于他的构造函数是有参数的,需要传入一个index,就是你要迭代的范围。
序列化和反序列化就不说了。
下面这个方法会返回一个反向的迭代器,是在jdk1.6时新增的方法。大家看看就好。
有序的列表,其内部是一个双向循环链表的实现,其在JavaDocs中说明既可以作为栈来使用也可以作为队列来使用。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
用于保存链表的头指针
private transient Entry<E> header = new Entry<E>(null, null, null);
用于保存链表的大小
private transient int size = 0;
默认构造函数,初始化链表的前后指针,构成初始的环路
public LinkedList() { header.next = header.previous = header; }
另一个构造器,传入一个已有的集合,根据已有的集合构建列表,首先调用默认的构造器,然后添加已有集合中的所有元素,添加方法后续说明。
public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
为了便于说明,提前来看一下一个内部类Entry,其中有三个属性,element用于存储数据,next是后指针,previous是前指针。另外一个有参的构造方法。
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; } }
获得首个元素,实现很简单,就是返回头指针的后指针指向的元素的元素值。
public E getFirst() { if (size == 0) throw new NoSuchElementException(); return header.next.element; }
同理可得获得最后一个元素的代码实现。
public E getLast() { if (size == 0) throw new NoSuchElementException(); return header.previous.element; }
这是一个私有方法,不会被外部直接调用,目的是删除一个元素。删除的时候就是双向循环链表的删除,前后指针的移动,以及为了垃圾回收而写的前后指针置为null,最后返回被删除的元素。
private E remove(Entry<E> e) { if (e == header) throw new NoSuchElementException(); E result = e.element; e 4000 .previous.next = e.next; e.next.previous = e.previous; e.next = e.previous = null; e.element = null; size--; modCount++; return result; }
下面两段源码不解释。
public E removeFirst() { return remove(header.next); } public E removeLast() { return remove(header.previous); }
实际中会被外部调用的删除方法,特殊在于对于null值的判断采用不同的分支。实际调用的依然是查找到位置后调用私有的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; }
判断元素的位置,和上面的方法类似,依然是对null值的特殊处理。
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; }
这里有一个非常有趣的私有方法,entry(index)用于获得某个索引的Entry对象。为什么说有趣呢?因为在判断完索引是否越界后进行了一步判断,就是索引位置在列表中的大体位置,是在前一半还是后一半,然后采用不同的遍历顺序,这样可以加快一倍的查找速度。
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; }
下面的公共方法就是调用了上面的私有方法,实现快速的对某个索引位置元素的删除。
public E remove(int index) { return remove(entry(index)); }
下面的设置方法也是调用上面的私有方法,先查找到元素,再进行元素值的替换覆盖。
public E set(int index, E element) { Entry<E> e = entry(index); E oldVal = e.element; e.element = element; return oldVal; }
那这样get方法也很好理解了吧。
public E get(int index) { return entry(index).element; }
清空操作不能直接修改头指针的前后指针,这样指针虽然可以置空,但是其链表中的元素依然在内存中占有一定的位置,所以代码中的实现是优秀的,遍历链表将每个元素置为null。保证的后续gc的工作。
public void clear() { Entry<E> e = header.next; while (e != header) { Entry<E> next = e.next; e.next = e.previous = null; e.element = null; e = next; } header.next = header.previous = header; size = 0; modCount++; }
下面是一个私有方法,从方法名中可以看到方法的作用是在e的元素之前添加entry元素。实际就是链表的插入操作,但是要保证head头指针的后指针指向最先插入的元素,head头指针的前指针指向最后插入的元素。
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方法,调用上面的私有方法addBefore。
public boolean add(E e) { addBefore(e, header); return true; }
这个方法用于添加一个元素到列表的最前面,实际就是在头指针的后指针之前添加元素。
public void addFirst(E e) { addBefore(e, header.next); }
下面的方法在列表末尾添加元素,其实和普通的add方法相同,但是没有返回值。
public void addLast(E e) { addBefore(e, header); }
下面的方法用于在某个index后加入一个已有列表的集合,特殊在于index位置指针的处理,只是在最后一行代码中进行指针的修改指向。
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); predecessor.next = e; predecessor = e; } successor.previous = predecessor; size += numNew; return true; }
不带index参数的addAll方法就是在列表的最末尾插入元素,所以index就是大小size。
public boolean addAll(Collection<? extends E> c) { return addAll(size, c); }
下面是源码中为了实现队列功能而实现的方法,比较简单就不贴代码了。
下面是其内部自带的迭代器,这个迭代器也会抛出ConcurrentModificationException异常。其实现方法和其他的迭代器相同,依然是比对修改的次数。特殊在于他的构造函数是有参数的,需要传入一个index,就是你要迭代的范围。
private class ListItr implements ListIterator<E> { ListItr(int index)
序列化和反序列化就不说了。
下面这个方法会返回一个反向的迭代器,是在jdk1.6时新增的方法。大家看看就好。
public Iterator<E> descendingIterator() { return new DescendingIterator(); } private class DescendingIterator implements Iterator { final ListItr itr = new ListItr(size()); public boolean hasNext() { return itr.hasPrevious(); } public E next() { return itr.previous(); } public void remove() { itr.remove(); } }
相关文章推荐
- LinkedList源码解析——JDK1.8
- JDK源码解析之LinkedList
- JDK源码解析之LinkedList
- LinkedList源码解析 给jdk写注释系列之jdk1.6容器(2)
- LinkedList源码解析(jdk1.8)
- Java集合框架--LinkedList源码解析(JDK1.7)
- JDK之LinkedList源码解析
- LinkedList源码解析(基于JDK1.7)
- JDK 1.7源码阅读笔记(三)集合类之LinkedList
- (8) Java源码分析 ---- LinkedList (对应数据结构中线性表中的双向循环链表,JDK1.6)
- Java Collections Framework之LinkedList源码分析(基于JDK1.6)
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
- JDK源码阅读——ArrayList\LinkedList
- Java Collections Framework之Queue(LinkedList实现)源码分析(基于JDK1.6)
- JDK源码分析之集合03LinkedList
- Java Collection Framework 之 LinkedList 源码解析
- 【集合框架】JDK1.8源码分析之LinkedList(七)
- JDK源码阅读LinkedList
- ArrayList LinkedList源码解析
- LinkedList源码解析