@pastqing
2015-11-16T07:37:15.000000Z
字数 3539
阅读 3043
java java-collection-framwork
LinkedList即链表, 相对于顺序表, 链表存储数据不需要使用地址连续的内存单元。减少了修改容器结构而带来的移动元素的问题,顺序访问相对高效。
JDK中的LinkedList是双向链表, 每个结点分别存有上一个结点和下一个结点的信息。它的定义如下:
private static class Node<E> {E item;Node<E> next;Node<E> prev;Node<E> (Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}
transient int size = 0;transient Node<E> first;transient Node<E> last;
public LinkedList() {}
或者根据其他容器进行构造, 后面我们会自己写一个构造一个有序的链表
public LinkedList(Collection<? extends E> c) {this();addAll(c);}
这里给出一点补充, 关于泛型修饰符? super T 与 ? extends T的区别,参见这篇文章泛型中? super T和? extends T的区别
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;elsef.prev = newNode;size++;modCount++;}
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;elsel.next = newNode;size++;modCount++;}
void linkBefore(E e, Node<E> succ) {//确定当然结点非空final Node<E> pred = succ.prev;final Node<E> newNode = new Node<>(pred, e, succ);succ.prev = newNode;//判断当前结点是否是第一个结点if (pred == null)first = newNode;elsepred.next = newNode;size++;modCount++;}
private E unlinkFirst(Node<E> f) {// assert f == first && f != null;final E element = f.item;final Node<E> next = f.next;f.item = null;f.next = null; // help GCfirst = next;if (next == null)last = null;elsenext.prev = null;size--;modCount++;return element;}
private E unlinkLast(Node<E> l) {//保证l==last 并且l != nullfinal E element = l.item;final Node<E> prev = l.prev;l.item = null;l.prev = null; // help GClast = prev;if (prev == null)first = null;elseprev.next = null;size--;modCount++;return element;}
Node<E> node(int index) {//确保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;}}
index 属于前半部分的计数, 从头遍历查找。index属于后半部分的计数, 从末尾遍历查找。充分利用双向链表的特点。
因此,add(int index, T t), get(int), set(int)等方法就可以很容易的实现。
既然LinkedList是双向链表, 自然就可以前后遍历。与ArrayList同样, 涉及到多线程操作容器的时候LinkedList也会出现fail-fast问题。
对于fail-fast问题上篇已经讲解过, 这里就不说了。
关于迭代器,LinkedList有listIterator双向迭代器, 和DescendingIterator逆序迭代器。都很简单。源码不在分析
如果遍历元素的话, 随机访问的代价是比较大得。
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist都可以。而如果移动一个指定位置的数据花费的时间为0(n-i)n为总长度,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据所花费的时间为0(1),而查询一个指定位置的数据时花费的时间为0(i)。
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!
参考文献:
java集合类详解