我是靠谱客的博主 尊敬月光,最近开发中收集的这篇文章主要介绍一般 for 循环遍历和迭代器遍历 LinkedList 区别,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

先说区别:

  • 普通for循环:每次遍历一个索引的元素之前,都要访问之前所有的索引。

  • 每次访问一个元素后,都会用游标记录当前访问元素的位置,遍历一个元素,记录一个位置。

普通 for 循环

普通 for 循环遍历方式如下:

LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.add("C");
linkedList.add("D");
for(int i = 0 ; i < linkedList.size() ; i++){
System.out.print(linkedList.get(i)+" ");
}

需要注意的是, get(int index) 方法每次都要遍历该索引之前的所有元素,这句话这么理解:

比如上面的一个 LinkedList 集合,放入了 A、B、C、D 四个元素。总共需要四次遍历:

  • 第一次遍历打印 A:只需遍历一次。
  • 第二次遍历打印 B:需要先找到 A,然后再找到 B 打印。
  • 第三次遍历打印 C:需要先找到 A,然后找到 B,最后找到 C 打印。
  • 第四次遍历打印 D:需要先找到 A,然后找到 B,然后找到 C,最后找到 D。

也就是说每次调用 get(int index) 方法时,都是从最前面开始依次往后遍历。这样的话如果集合元素很多,越查找到后面(当然此处的get方法进行了优化,查找前半部分从前面开始遍历,查找后半部分从后面开始遍历,但是需要的时间还是很多)花费的时间越多。那么如何改进呢?

迭代器

迭代器遍历如下:

LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.add("C");
linkedList.add("D");
// 从前往后打印
Iterator<String> listIt = linkedList.listIterator();
while(listIt.hasNext()){
System.out.print(listIt.next()+" ");
// A B C D
}
// 从后往前打印
// 通过适配器模式实现的接口,作用是倒叙打印链表
Iterator<String> it = linkedList.descendingIterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
// D C B A
}

在 LinkedList 集合中也有一个内部类 ListItr,方法实现大体上也差不多,通过移动游标指向每一次要遍历的元素,不用在遍历某个元素之前都要从头开始。其方法实现也比较简单:

public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {
return nextIndex > 0;
}
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

这里需要重点注意的是 modCount 字段,前面在增加和删除元素的时候,都会进行自增操作 modCount,这是因为如果想一边迭代,一边用集合自带的方法进行删除或者新增操作,都会抛出异常。(使用迭代器的增删方法不会抛异常)

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

比如:

LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.add("C");
linkedList.add("D");
Iterator<String> listIt = linkedList.listIterator();
while(listIt.hasNext()){
System.out.print(listIt.next()+" ");
// A B C D
// linkedList.remove();
// 此处会抛出异常
listIt.remove();
// 这样可以进行删除操作
}

迭代器的另一种形式就是使用 foreach 循环,底层实现也是使用的迭代器:

LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.add("C");
linkedList.add("D");
for(String str : linkedList){
System.out.print(str + "");
}

迭代器和 for 循环性能差异测试

LinkedList<Integer> linkedList = new LinkedList<>();
for(int i = 0 ; i < 10000 ; i++){
// 向链表中添加一万个元素
linkedList.add(i);
}
long beginTimeFor = System.currentTimeMillis();
for(int i = 0 ; i < 10000 ; i++){
System.out.print(linkedList.get(i));
}
long endTimeFor = System.currentTimeMillis();
System.out.println("使用普通for循环遍历10000个元素需要的时间:"+ (endTimeFor - beginTimeFor));
long beginTimeIte = System.currentTimeMillis();
Iterator<Integer> it = linkedList.listIterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
long endTimeIte = System.currentTimeMillis();
System.out.println("使用迭代器遍历10000个元素需要的时间:"+ (endTimeIte - beginTimeIte));

打印结果:

使用普通for循环遍历10000个元素需要的时间:100
使用迭代器遍历10000个元素需要的时间:24

可以看出当打印10000个元素时,两者的时间大约相差3倍,当元素个数更多时,性能差距将会更大。

最后

以上就是尊敬月光为你收集整理的一般 for 循环遍历和迭代器遍历 LinkedList 区别的全部内容,希望文章能够帮你解决一般 for 循环遍历和迭代器遍历 LinkedList 区别所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(64)

评论列表共有 0 条评论

立即
投稿
返回
顶部