@pastqing
2015-11-16T15:37:15.000000Z
字数 3539
阅读 2823
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;
else
f.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;
else
l.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;
else
pred.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 GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
private E unlinkLast(Node<E> l) {
//保证l==last 并且l != null
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.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集合类详解