前面提到在使用for循环遍历LinkedList时效率比用 for each 和 Iterator 遍历要低,且随着LinkedList元素增加其差距也越来越大。什么原因呢?
看LinkedList的3种遍历代码,List的get()方法源码,Iterator 的 next() 方法源码。
public static void main(String[] args) {
List<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < 10; i++) {
Random r = new Random();
Integer tmp = r.nextInt();
list.add(tmp);
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
for (Integer i : list) {
System.out.println(i);
}
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
}
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
分析:
LinkedList为双向链表。其for循环遍历逻辑是先判断要访问的下标 index 是位于 List 的前半部还是后半部,然后依次从第一个元素往后遍历直到index返回,或从最后一个元素依次往前遍历直到index返回。比如有5元素需要遍历,源码for循环的共需要执行 1 + 2 + 3 +2 +1 次。
使用for each遍历其底层是转换成 Iterator 进行迭代遍历的,其逻辑为开始取出第一个元素next存到lastReturned中,再将next的下一个元素赋值给next,地址下标加1,最后返回取出的lastReturned。再次进入时直接从第二个元素开始。所以总执行次数为元素个数。
结论:
在2种情况下LinkedList 使用 for 和 for each 两种方式进行遍历消耗时间并不会有什么区别。
(1)LinkedList本身元素很少。
(2)只需要遍历位于链表开始或者结尾部分的元素。
但随着元素增加和访问元素位置不确定性使用for循环进行遍历时将会很危险甚至造成服务器宕机。