迭代器
作者:互联网
Iterator
Iterator是一个接口,主要用于遍历Collection集合中的元素,而不保留该对象的内部表示。
Collection实现了Iterator接口,所以在Collection中都有这样一个iterator()方法
其返回Iterator的具体实现。
Iterator接口内相关方法有
- hasNext()
返回值类型为boolean,功能:判断集合是否还有元素,如果返回 true 表示集合还有元素,返回 false 表示集合中没有元素;一般对集合的访问通过 while(hasNext()) 判断是否还需要遍历。
- next()
返回值类型为泛型接口定义的泛型类型,功能:获取集合中遍历的当前元素 ;一般先调用 hasNext() 方法判断是否存在元素,再调用 next() 获取元素,需要进行循环交替遍历集合中的元素。
- remove()
返回值类型为void,功能:删除集合中的元素。
迭代器使用
List<String> testlist = new ArrayList<>();
testlist.add("I");
testlist.add("Love");
testlist.add("HIT");
testlist.add("Very");
testlist.add("Much");
Iterator<String> myitr = testlist.iterator();
while(myitr.hasNext()) {
String str = myitr.next();
System.out.println(str);
}
在迭代器的遍历过程中先通过hastNext()方法判断是否有下一个元素,如果存在下一个元素再调用
next()方法获取元素,在这里next()方法先往后移动一个元素位置,再返回该位置的元素。因此,
在调用next()方法之前必须要调用hastNext()方法进行检测;如果没有调用并且没有下一个元素,
直接调用next()方法会抛出 NoSuchElementException异常。
关于迭代器触发异常
相信很多初学的伙伴们(me too)在使用迭代器时都经历过这样的异常。
下面就讲讲为什么会出现这样的异常,以及迭代器内部的实现原理。
在ArrayList集合的Iterator方法中,是通过返回Itr对象来获得迭代器的。Itr是ArrayList的一个内部类,它实现了Iterator接口,代码如下:
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();
}
}
其内部属性:
int cutsor:索引下标,表示下一个可以访问的元素的索引,默认值为0
int expectedModCount:集合迭代器的实现中元素修改次数,默认值等于modCount(原集合元素修改次数);
int lastRet: 索引下标,表示上一个元素的索引,默认值为 -1
针对于上图中代码和触发的异常,作出分析
当testlist调用itertor方法返回时,myitr中expectedModCount的值与testlist中modCount的值相等。值为5,因为之前testlist添加了五个字符串,做出了五次修改。
testlist的size为5.
进入循环条件调用hasNext方法判断是否有下一个元素: 可以看到此时cursor != size. 所以执行循环体
执行myitr.next(),然后需要先执行checkForComodification();
检查expectedModCount和modCount是否相等,不一致则抛出并发修改异常,此时一致则正常执行
然后游标cursor+1,然后返回第一个字符串“I”。
打印字符串
如此反复,直到遍历至字符串“HIT”。
当返回字符串为HIT时,符合条件,在集合中删除该元素
testlist的size变为4,testlist中modCount值+1,等于6。
执行下一次遍历,执行到 checkForComodification()时,判断expectedModCount和modCount不相等
则抛出了异常ConcurrentModificationException。
再看下面的例子:
此时却并未抛出异常,这是为何?
原因是遍历到字符串“HIT”时,将其删除。
而此时游标cursor已+1,等于3。testlist的size变为4-1为3。
此时二者相等,循环体条件不满足,不再执行下面的语句,自然不会抛出异常。
如何避免并发异常
当删除集合内部元素时,我们不要去使用集合的remove方法,而是去使用迭代器内部的remove方法
该remove方法可以同步集合与迭代器的修改,保证expectedModCount和modCount相等。
List<String> testlist = new ArrayList<>();
testlist.add("I");
testlist.add("Love");
testlist.add("HIT");
testlist.add("Very");
testlist.add("Much");
Iterator<String> myitr = testlist.iterator();
while(myitr.hasNext()) {
String str = myitr.next();
if (str.startsWith("H")) {
myitr.remove();
}
System.out.println(str);
}
System.out.println(testlist);
结果如下:
for each语句
其可以代替Iterator迭代器,可以把它看做简化版的Iterator,和迭代器本质一样,其实它的底层
实现就是Iterator迭代器,只能用于遍历集合或数组。
List<String> testlist = new ArrayList<>();
testlist.add("I");
testlist.add("Love");
testlist.add("HIT");
testlist.add("Very");
testlist.add("Much");
for (String str : testlist) {
System.out.println(str);
}
为了避免并发修改的错误,应该使用Iterator实现,并利用迭代器内部的remove方法。
标签:迭代,Iterator,元素,testlist,next,add 来源: https://www.cnblogs.com/clrain/p/16376759.html