概述
先说区别:
-
普通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 区别所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复