Java Iterator(迭代器)小笔记
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/253
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList和 HashSet 等集合。Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。
Java中的Iterator功能比较简单,并且只能单向移动:
使用方法iterator()要求容器返回一个Iterator。 注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:
import java.util.Iterator; // 引入 Iterator 类
1.获取一个迭代器并循环集合元素
集合想获取一个迭代器可以使用 iterator() 方法
package com.joshua317;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
//创建集合
ArrayList<String> names = new ArrayList<>();
String[] namesArr = {"小明","二蛋","joshua317"};
names.addAll(Arrays.asList(namesArr));
System.out.println(names);
//获取迭代器
Iterator<String> iterator = names.iterator();
//遍历集合元素,让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环
while (iterator.hasNext()) {
String name = iterator.next();
System.out.println(name);
}
}
}
2.删除集合里面的元素
要删除集合中的元素可以使用 remove() 方法。
package com.joshua317;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
//创建集合
ArrayList<String> names = new ArrayList<>();
String[] namesArr = {"小明","二蛋","joshua317"};
names.addAll(Arrays.asList(namesArr));
System.out.println(names);
//获取迭代器
Iterator<String> iterator = names.iterator();
//遍历集合元素,让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环
while (iterator.hasNext()) {
String name = iterator.next();
//删除集合里面字符串为"joshua317"的元素
if (name.equals("joshua317")) {
iterator.remove();
}
}
}
}
3.扩展-迭代器的原理
所有迭代器都最终实现接口Iterator,Iterator接口中包含三个基本方法,next(), hasNext(), remove(),其中对于List的遍历删除只能用Iterator的remove方法;JDK1.8中Iterator接口的源码如下:
package java.util;
import java.util.function.Consumer;
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
接下来,我们基于ArrayList的Iterator的实现分析terator的原理(基于JDK1.8)
在ArrayList类中有个方法iterator(),此方法将返回一个iterator的实现,这里可以看出实现类叫Itr,通过其它源码可知,此类是AarryList的内部类,即ArryList的Iterator实现在ArrayList内部;
public Iterator<E> iterator() {
return new Itr();
}
然后看下ArrayList中实现类Itr类的源码
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
从上面代码中可以看出,对于Iterator的实现中主要有几个变量cursor,lastRest, expectedModCount三个变量,其中cursor将记录下一个位置,lastRet记录当前位置,expectedModCount记录没有修改的List的版本号。
问题抛出:List中在iterator遍历的时候,为什么不能随便添加和删除元素吗?
在iterator遍历的时候抛出异常都是checkForComodification作的,根本原因是modCout和expectedModCount不相等,导致抛出异常。
我们来看下ArrayList的add和remove方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
从代码中可以看出只要对ArrayList作了添加或删除操作都会增加modCount版本号,这样在迭代期间,会不断检查modCount和迭代器持有的expectedModCount两者是否相等,如果不相等就抛出异常了。
关于modCount,API解释如下:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
也就是说,modCount记录修改此列表的次数:包括改变列表的结构,改变列表的大小,打乱列表的顺序等使正在进行迭代产生错误的结果。
注意:仅仅设置元素的值并不是结构的修改
我们知道的是ArrayList是线程不安全的,如果在使用迭代器的过程中有其他的线程修改了List就会抛出ConcurrentModificationException
package com.joshua317;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
//创建集合
ArrayList<String> names = new ArrayList<>();
String[] namesArr = {"小明","二蛋","joshua317"};
names.addAll(Arrays.asList(namesArr));
//获取迭代器
Iterator<String> iterator = names.iterator();
//遍历集合元素,让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环
while (iterator.hasNext()) {
String name = iterator.next();
names.add("小红");
}
}
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at com.joshua317.Main.main(Main.java:19)
虽然在迭代器迭代期间不能对ArrayList作任何增删操作,但是可以通过iterator的remove作删除操作,从之前的代码可以看出,在iterator的remove()中有一行代码,expectedModCount = modCount; 这个赋值操作保证了iterator的remove是可用性的。
当然,iterator期间不能增删的根本原因是ArrayList遍历会不准,就像遍历数组的时候改变了数组的长度一样。
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/253