[关闭]
@pastqing 2015-11-16T15:37:15.000000Z 字数 3539 阅读 2823

Java集合框架——List(二)

java java-collection-framwork


一、LinkedList源码分析(JDK7)

LinkedList即链表, 相对于顺序表, 链表存储数据不需要使用地址连续的内存单元。减少了修改容器结构而带来的移动元素的问题,顺序访问相对高效。

1、结点(Node)的定义

JDK中的LinkedList是双向链表, 每个结点分别存有上一个结点和下一个结点的信息。它的定义如下:

  1. private static class Node<E> {
  2. E item;
  3. Node<E> next;
  4. Node<E> prev;
  5. Node<E> (Node<E> prev, E element, Node<E> next) {
  6. this.item = element;
  7. this.next = next;
  8. this.prev = prev;
  9. }
  10. }

2、LinkedList构造以及初始化

  1. transient int size = 0;
  2. transient Node<E> first;
  3. transient Node<E> last;
  1. public LinkedList() {}

或者根据其他容器进行构造, 后面我们会自己写一个构造一个有序的链表

  1. public LinkedList(Collection<? extends E> c) {
  2. this();
  3. addAll(c);
  4. }

这里给出一点补充, 关于泛型修饰符? super T 与 ? extends T的区别,参见这篇文章泛型中? super T和? extends T的区别

3、LinkedList的结构操作

  1. private void linkFirst(E e) {
  2. final Node<E> f = first;
  3. final Node<E> newNode = new Node<>(null, e, f);
  4. first = newNode;
  5. //判断是否是空链表
  6. if (f == null)
  7. last = newNode;
  8. else
  9. f.prev = newNode;
  10. size++;
  11. modCount++;
  12. }
  1. void linkLast(E e) {
  2. final Node<E> l = last;
  3. final Node<E> newNode = new Node<>(l, e, null);
  4. last = newNode;
  5. if (l == null)
  6. first = newNode;
  7. else
  8. l.next = newNode;
  9. size++;
  10. modCount++;
  11. }
  1. void linkBefore(E e, Node<E> succ) {
  2. //确定当然结点非空
  3. final Node<E> pred = succ.prev;
  4. final Node<E> newNode = new Node<>(pred, e, succ);
  5. succ.prev = newNode;
  6. //判断当前结点是否是第一个结点
  7. if (pred == null)
  8. first = newNode;
  9. else
  10. pred.next = newNode;
  11. size++;
  12. modCount++;
  13. }
  1. private E unlinkFirst(Node<E> f) {
  2. // assert f == first && f != null;
  3. final E element = f.item;
  4. final Node<E> next = f.next;
  5. f.item = null;
  6. f.next = null; // help GC
  7. first = next;
  8. if (next == null)
  9. last = null;
  10. else
  11. next.prev = null;
  12. size--;
  13. modCount++;
  14. return element;
  15. }
  1. private E unlinkLast(Node<E> l) {
  2. //保证l==last 并且l != null
  3. final E element = l.item;
  4. final Node<E> prev = l.prev;
  5. l.item = null;
  6. l.prev = null; // help GC
  7. last = prev;
  8. if (prev == null)
  9. first = null;
  10. else
  11. prev.next = null;
  12. size--;
  13. modCount++;
  14. return element;
  15. }

4、保持List接口与Deque的一致性

  1. Node<E> node(int index) {
  2. //确保index的正确性
  3. if (index < (size >> 1)) {
  4. Node<E> x = first;
  5. for (int i = 0; i < index; i++)
  6. x = x.next;
  7. return x;
  8. } else {
  9. Node<E> x = last;
  10. for (int i = size - 1; i > index; i--)
  11. x = x.prev;
  12. return x;
  13. }
  14. }

index 属于前半部分的计数, 从头遍历查找。index属于后半部分的计数, 从末尾遍历查找。充分利用双向链表的特点。
因此,add(int index, T t), get(int), set(int)等方法就可以很容易的实现。

3、LinkedList的遍历

既然LinkedList是双向链表, 自然就可以前后遍历。与ArrayList同样, 涉及到多线程操作容器的时候LinkedList也会出现fail-fast问题。
对于fail-fast问题上篇已经讲解过, 这里就不说了。

关于迭代器,LinkedList有listIterator双向迭代器, 和DescendingIterator逆序迭代器。都很简单。源码不在分析

如果遍历元素的话, 随机访问的代价是比较大得。

二、LinkedList,ArrayList, Vector总结

1、LinkedList与ArrayList

2、ArrayList与Vector

参考文献:
java集合类详解

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注