AbstractList的Itr和ListItr源码

Posted on 2018-03-10 11:54  林浩开发小屋  阅读(279)  评论(0)    收藏  举报
 1     private class Itr implements Iterator<E> {
 2         /**
 3          * Index of element to be returned by subsequent call to next.
 4          */
 5         int cursor = 0;
 6 
 7         /**
 8          * Index of element returned by most recent call to next or
 9          * previous.  Reset to -1 if this element is deleted by a call
10          * to remove.
11          */
12         int lastRet = -1;
13 
14         /**
15          * The modCount value that the iterator believes that the backing
16          * List should have.  If this expectation is violated, the iterator
17          * has detected concurrent modification.
18          */
19         int expectedModCount = modCount;
20 
21         public boolean hasNext() {
22             return cursor != size();
23         }
24 
25         public E next() {
26             checkForComodification();
27             try {
28                 int i = cursor;
29                 E next = get(i);
30                 lastRet = i;
31                 cursor = i + 1;
32                 return next;
33             } catch (IndexOutOfBoundsException e) {
34                 checkForComodification();
35                 throw new NoSuchElementException();
36             }
37         }
38 
39         public void remove() {
40             if (lastRet < 0)
41                 throw new IllegalStateException();
42             checkForComodification();
43 
44             try {
45                 AbstractList.this.remove(lastRet);
46                 if (lastRet < cursor)
47                     cursor--;
48                 lastRet = -1;
49                 expectedModCount = modCount;
50             } catch (IndexOutOfBoundsException e) {
51                 throw new ConcurrentModificationException();
52             }
53         }
54 
55         final void checkForComodification() {
56             if (modCount != expectedModCount)
57                 throw new ConcurrentModificationException();
58         }
59     }

 

1. 如何判断当前迭代器的位置,及如何移动? 
首先对于每一个迭代器,保存了三个变量。一个是cursor,一个是lastRet,一个是expectedModCount。 
cursor对于每次调用next方法就增加一次。并且将上一个cursor的值传给lastRet(用于可能的删除操作),将刚刚跳过的第i个元素作为返回值。这里得到刚刚跳过的元素的方法是使用了AbstractList类的get方法。

对于如何判断迭代到尾的hasNext方法,采用的是比较cursor的值和size的大小。如果两者相等则说明迭代到尾了。

 

2. 如何对迭代的元素进行删除,及删除后的影响? 

由于java的迭代器没有办法进行随机访问,因此如果需要删除元素也只能删除刚刚跳过的元素(AbstractList.this.remove(lastRet);)。

这样也导致每次remove之前必定有一个next。如果连续进行两次remove则会抛出异常。因为并不知道删除的元素是哪一个。

判断删除的迭代器是否是在cursor之前,如果是的话还需要将cursor减一。其实这个也很有意思。实际上这里应该是单向的迭代,为何还需要进行判断而不是直接减一。可能的解释是这里还有一个继承了Itr的ListItr,而它包含了向前迭代的可能,因此如何换成一个不包含向前迭代的实现就不需要判断了。因此这里的写法实际上是依赖于实现的。当然你可以选择在写ListItr方法的时候重写remove方法。不过后文可以看到这里的实现非常漂亮没有覆盖覆盖任何方法。

因为向后遍历的话,lastRet这个值是刚才迭代的元素,迭代完cursor增加了1,如果删除刚才那个元素,cursor就要减回去。

3. 如何防止多个迭代器之间的相互影响? 
要实现多个迭代器之间的互不影响的访问,或者说如果有迭代器对对象进行了结构性的修改其他迭代器可以立即检测出问题并抛出异常(也就是所谓的fail-fast),这里采用的是这样的方式: 
首先对每个需要迭代的对象(这里就是ArrayList对象)保存一个modCount的数字。这个数字在每次迭代器进行结构性修改的时候加1。换句话说,这个变量表示的是对象的修改次数,因此需要放在Itr类外面,AbstractList类里面。此外这个变量是transient的,也就说序列化的时候是不需要储存的。 
其次对每个迭代器保存一个expectedModCount ,来记录这个迭代器对对象进行结构性修改的次数。这样每次迭代器进结构性修改的时候都将expectedModCount 和modCount进行对比,如果两种相等则说明没有其他迭代器修改了对象,可以进行。如果不相等则说明有迭代进行了修改,立刻抛出异常。

ListItr

Itr看完了其实ListItr就没有什么特别需要说明的了。只是继承了继承了Itr,并且增加了几个方法个人认为并没有特别需要说明的。基本方法和Itr的方法类似。

但是有一个很有意思的问题是。为何在AbstractList中同时实现了Itr和ListItr。因为很显然Itr实现的是Iterator接口,按道理说应该在更早的Collection集合中实现。我自己考虑了两种可能的原因

第一个是实际上在整个java实现中抽象类里面,这里指直接继承自Collection类的几个抽象类中。AbstractList是唯一一个实现了Itr的类。其他的抽象类并没有这么复杂。因此如果在Collection类中实现了,那就无法使用private的内部类,这样可能会破坏封装性。但是如果不使用private就没办法使用ListItr继承Collection中的继承类。这样会很麻烦。

第二个问题是实际上Itr的实现还是依赖具体实现的。比如说如果在不可寻址的地方无法使用get或者remove删除指定的cursor,因此其他地方仅仅给出接口就行不需要具体实现,具体实现需要结合实际情况来看。

 

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3