看到了Java核心技术一书的集合的迭代器,写篇文章记录一下
interface Iterator<E>,有四个方法:hasNext(),next(),remove(),forEachRemaining(),最后一个方法是JDK1.8新增的
接下来使用迭代器中的方法遍历一个collection
1.传统方式,使用hasNext(),next()
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("111");
collection.add("222");
collection.add("333");
collection.add("444");
//使用迭代器遍历集合
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
next()源码:
private class Itr implements Iterator<E> {
int cursor;// index of next element to return,要返回的下一个元素的索引,初始值0
//返回最后一个元素的索引;如果没有返回,则返回-1
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
// prevent creating a synthetic constructor
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];
}
}
第一次循环后:cursor变成了1,lastRet变成了0
第二次循环后:cursor变成了2,lastRet变成了1
第三次循环后:cursor变成了3,lastRet变成了2
第四次循环后:cursor变成了4,lastRet变成了3
//使用迭代器遍历集合
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
//System.out.println(iterator.next());
iterator.remove();
System.out.println(iterator.next());
}
正确的写法是,将上面注释掉的代码恢复
remove()源码
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();
}
}
每次都先判断lastRet的值是否小于0,在next()中lastRet的值才会被修改为>=0,所以要先调用next(),删除完成后lastRet又变成了-1,一次next()只能remove()一次
2.forEachRemaining(),对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。注意是剩余两个字,很重要,看一个例子
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("111");
collection.add("222");
collection.add("333");
collection.add("444");
//使用迭代器遍历集合
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("-------------------");
//使用iterator的forEachRemaining
iterator.forEachRemaining(item ->{
System.out.println(item);
//if ("333".equals(item))iterator.remove();
});
}
输出如下:
111
222
333
444
-------------------
forEachRemaining没有遍历出任何元素,因为iterator里面的元素已经被第一种方法全部遍历出来了,iterator已经没有剩余元素了,看一下源码中参数的变化
将cursor变量的值赋给变量i,i<size才进行遍历, cursor为什么会等于4呢,是因为while循环的next()赋的值,此时在调用iterator的forEachRemaining方法就不会走if判断,所以剩余也是由cursor控制的。
forEachRemaining遍历过程中不能调用remove(),会报java.lang.IllegalStateException,例如下面的代码
Collection<String> collection = new ArrayList<>();
collection.add("111");
collection.add("222");
collection.add("333");
collection.add("444");
//使用迭代器遍历集合
Iterator<String> iterator = collection.iterator();
// while (iterator.hasNext()){
// System.out.println(iterator.next());
// }
System.out.println("-------------------");
//使用iterator的forEachRemaining
iterator.forEachRemaining(item ->{
System.out.println(item);
if ("333".equals(item))iterator.remove();
});
从上面的源码可以看到,forEachRemaining遍历的时候并没有使用next(),使用的是for循环遍历,并且需要等待全部遍历完成才会修改lastRet的值,当在遍历的过程中调用remove(),此时的lastRet仍旧为初始值-1,remove()有一个判断:
if (lastRet < 0)
throw new IllegalStateException();
所以就抛出异常。