迭代器

发布于 — 2019 年 08 月 28 日
#Java

迭代器

在java中主要有两种迭代器,IteratorListIterator。这两个都是接口。先来看一下这两个接口有什么区别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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());
    }
}

Iterator主要有四个方法。判断有没有下一个元素、获取下一个元素,删除元素和forEachRemaining方法。

再来看一下ListIterator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public interface ListIterator<E> extends Iterator<E> {

    boolean hasNext();

    E next();

    boolean hasPrevious();

    E previous();

    int nextIndex();

    int previousIndex();

    void remove();

    void set(E e);

    void add(E e);
}

我们可以看到他是继承了Iterator。除了上面的两个方法还多了好几个方法。判断是否有上一个元素,获取上一个元素的值,获取上一个元素的索引,获取上一个元素的索引。除了移除还有添加和更新的方法。

他们在不同的类里面都有自己的实现,之前看ArrayListHashMap的时候把这一块给跳过了,现在来看一下他们是如何实现的。

ArrayList

他里面实现了上面说过的两个接口,先来看一下实现的Iterator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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();
        /** 将下一个的索引赋给i,如果下一个索引比集合长度还大,则抛出异常 **/
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
        	/** 这里又进行了一次判断,判断索引值与元素长度的关系 **/
            throw new ConcurrentModificationException();
        /**然后将下一个的索引进行加一**/
        cursor = i + 1;
        /**返回这个位置上的元素,并且将lastRet赋值为i **/
        return (E) elementData[lastRet = i];
    }

    /**这个方法是移除刚刚获取的那个元素**/
    public void remove() {
        /**如果lastRet小于0,则表示刚刚没有获取过元素**/
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            /**调用了ArrayList的根据索引删除元素方法**/
            ArrayList.this.remove(lastRet);
            /**将下一个元素索引赋值为上一个索引,并且将最后返回的索引置为-1**/
            cursor = lastRet;
            lastRet = -1;
            /**因为进行了删除,所以modCount进行了修改,这里要再次赋值到expectedModCount上**/
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

	/**
	* 检查 
	* 在开始的时候将 expectedModCount初始化为modCount的值
	* 如果在初始化后集合进行了 添加或删除元素的操作 则modCount会改变
	* 在这里进行判断,有没有进行修改的操作。
	* 例:[1,2,3,4,5]。从开始用迭代器,判断了第一位有值(现在是1),在调用next()方法之前进行了删除,把1删除了。
	* 这时再调用next()会返回2。这是不对的。这个判断就是为了这种情况的。
	*/
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

从上面的代码可以看出

  • 对于移除元素来说,只能移除刚刚获取的那一个元素。如果刚刚没获取元素获取则抛出异常,如果要删除获取的前两个元素也不可以。

然后再来看一下实现的ListIterator接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private class ListItr extends Itr implements ListIterator<E> {
    /**它的构造方法必须传入一个参数**/
    ListItr(int index) {
        super();
        cursor = index;
    }
	/**判断有没有上一个元素,如果下一个返回元素不是第一个元素则表示有上一个元素**/
    public boolean hasPrevious() {
        return cursor != 0;
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor - 1;
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    }
	/**调用了ArrayList的set方法,传入的坐标是刚刚返回值的坐标**/
    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.set(lastRet, e);
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
	/**添加一个元素,因为多了一个元素,所以下一个元素的索引要加1,并且刚刚没有返回元素,lastRet置为-1**/
    public void add(E e) {
        checkForComodification();

        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

LinkedList

也是将这两个接口都实现了,但是从代码里可以看出实现的Iterator最终还是调用了ListIterator方法

不过他后面的调用nexthasNext方法是调用是是否有上一个值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
private class DescendingIterator implements Iterator<E> {
    // 这个类是在下面声明的 实现了ListIterator的类
    private final ListItr itr = new ListItr(size());
    public boolean hasNext() {
        return itr.hasPrevious();
    }
    public E next() {
        return itr.previous();
    }
    public void remove() {
        itr.remove();
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

private class ListItr implements ListIterator<E> {
    private Node<E> lastReturned;
    private Node<E> next;
    private int nextIndex;
    private int expectedModCount = modCount;
	/**
	* 上面调用方法时传入了 size()方法的返回值相当于传入了size变量
	* 如果传入的值等于size则 next赋值为null 不然置为该位置上的元素
	* 然后让 nextIndex置为传入的值
	**/
    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++;
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

总结

从这里可以看出之前定义的modCount的作用,这个变量在迭代器中作为一个判断。

forEachRemaining方法暂时还没弄明白他的作用,而且它的作用应该并不是为了在迭代器中使用,应该有其他方面的作用,这个就放到后面再来看。