让我们看看java.util.LinkedList的the source code(OpenJDK版本)
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o==null) {
/* snipped */
} else {
for (Entry e = header.next; e != header; e = e.next) {
if (o.equals(e.element))
return index;
index++;
}
}
return -1;
}
如您所见,这是一个线性搜索,就像 for-each 解决方案一样,所以它不是渐近更快的。看看你的数字如何随着更长的列表增长会很有趣,但它可能是一个常数因素变慢。
原因是 indexOf 在内部结构上工作,使用直接字段访问来迭代,而不是使用 Iterator<E> 的 for-each,其方法还必须额外检查诸如ConcurrentModificationException等
回到源头,你会发现LinkedList的Iterator<E>返回的E next()方法如下:
private class ListItr implements ListIterator<E> {
//...
public E next() {
checkForComodification();
if (nextIndex == size)
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.element;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这比LinkedList.contains 中的e = e.next; 更“繁忙”!一个LinkedList的iterator()其实就是一个ListIterator,功能更丰富。在您的 for-each 循环中不需要它们,但不幸的是,无论如何您都必须为它们付费。更不用说必须执行所有对 ConcurrentModificationException 的防御性检查,即使在您迭代列表时不会对列表进行任何修改。
结论
所以是的,使用 for-each(或者更直接地说,使用它的 iterator()/listIterator())将 LinkedList 作为客户端迭代比 LinkedList 本身可以在内部执行的操作更昂贵。这是意料之中的,这就是为什么首先提供contains。
在内部工作给LinkedList 带来了巨大的优势,因为:
- 它可以在防守检查中偷工减料,因为它知道自己没有违反任何不变量
- 它可以走捷径并使用其内部表示
那么你能从中学到什么? 熟悉 API! 看看已经提供了哪些功能;与您必须将它们复制为客户端相比,它们可能会更快。