其他分享
首页 > 其他分享> > 迭代器

迭代器

作者:互联网

Iterator

Iterator是一个接口,主要用于遍历Collection集合中的元素,而不保留该对象的内部表示。

Collection实现了Iterator接口,所以在Collection中都有这样一个iterator()方法

其返回Iterator的具体实现。

Iterator接口内相关方法有

  1. hasNext()

返回值类型为boolean,功能:判断集合是否还有元素,如果返回 true 表示集合还有元素,返回 false 表示集合中没有元素;一般对集合的访问通过 while(hasNext()) 判断是否还需要遍历。

  1. next()

返回值类型为泛型接口定义的泛型类型,功能:获取集合中遍历的当前元素 ;一般先调用 hasNext() 方法判断是否存在元素,再调用 next() 获取元素,需要进行循环交替遍历集合中的元素。

  1. 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);
		}

image

在迭代器的遍历过程中先通过hastNext()方法判断是否有下一个元素,如果存在下一个元素再调用

next()方法获取元素,在这里next()方法先往后移动一个元素位置,再返回该位置的元素。因此,

在调用next()方法之前必须要调用hastNext()方法进行检测;如果没有调用并且没有下一个元素,

直接调用next()方法会抛出 NoSuchElementException异常。

关于迭代器触发异常

相信很多初学的伙伴们(me too)在使用迭代器时都经历过这样的异常。
image

下面就讲讲为什么会出现这样的异常,以及迭代器内部的实现原理。

在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。

再看下面的例子:
image

此时却并未抛出异常,这是为何?

原因是遍历到字符串“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);

结果如下:

image

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