Java中Iterator(迭代器)实现原理

在Java中遍历List时会用到Java提供的Iterator,Iterator十分好用,原因是:

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

  Java中的Iterator功能比较简单,并且只能单向移动:

  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

  (2) 使用next()获得序列中的下一个元素。

  (3) 使用hasNext()检查序列中是否还有元素。

  (4) 使用remove()将迭代器新返回的元素删除。

只要看看下面这个例子就一清二楚了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;
public class Muster {
 
     public static void main(String[] args) {
         ArrayList list = new ArrayList();
         list.add( "a" );
         list.add( "b" );
         list.add( "c" );
         Iterator it = list.iterator();
         while (it.hasNext()){
             String str = (String) it.next();
             System.out.println(str);
         }
     }
}

运行结果:

a
b
c

可以看到,Iterator可以不用管底层数据具体是怎样存储的,都能够通过next()遍历整个List。

但是,具体是怎么实现的呢?背后机制究竟如何呢?

这里我们来看看Java里AbstractList实现Iterator的源代码

?
1 . public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { // List接口实现了Collection<E>, Iterable<E>
2
3 .    protected AbstractList() { 
4 .    } 
5 .   
6 .    ... 
7
8 .    public Iterator<E> iterator() { 
9 .    return new Itr();  // 这里返回一个迭代器
10 .    } 
11
12 .    private class Itr implements Iterator<E> {  // 内部类Itr实现迭代器
13 .      
14 .    int cursor = 0
15 .    int lastRet = - 1
16 .    int expectedModCount = modCount; 
17
18 .    public boolean hasNext() {  // 实现hasNext方法
19 .            return cursor != size(); 
20 .    } 
21
22 .    public E next() {  // 实现next方法
23 .            checkForComodification(); 
24 .        try
25 .        E next = get(cursor); 
26 .        lastRet = cursor++; 
27 .        return next; 
28 .        } catch (IndexOutOfBoundsException e) { 
29 .        checkForComodification(); 
30 .        throw new NoSuchElementException(); 
31 .        } 
32 .    } 
33
34 .    public void remove() {  // 实现remove方法
35 .        if (lastRet == - 1
36 .        throw new IllegalStateException(); 
37 .            checkForComodification(); 
38
39 .        try
40 .        AbstractList. this .remove(lastRet); 
41 .        if (lastRet < cursor) 
42 .            cursor--; 
43 .        lastRet = - 1
44 .        expectedModCount = modCount; 
45 .        } catch (IndexOutOfBoundsException e) { 
46 .        throw new ConcurrentModificationException(); 
47 .        } 
48 .    } 
49
50 .    final void checkForComodification() { 
51 .        if (modCount != expectedModCount) 
52 .        throw new ConcurrentModificationException(); 
53 .    } 
54 .    } 
55 .}

可以看到,实现next()是通过get(cursor),然后cursor++,通过这样实现遍历。

这部分代码不难看懂,唯一难懂的是remove操作里涉及到的expectedModCount = modCount;

在网上查到说这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。

从源代码里可以看到增删操作都会使modCount++,通过和expectedModCount的对比,迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!
在第一个例子基础上添加一条语句:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.*;
public class Muster {
 
     public static void main(String[] args) {
         ArrayList list = new ArrayList();
         list.add( "a" );
         list.add( "b" );
         list.add( "c" );
         Iterator it = list.iterator();
         while (it.hasNext()){
             String str = (String) it.next();
             System.out.println(str);
             list.add( "s" );        //添加一个add方法
         }
     }
}

运行结果:

a
Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  at java.util.ArrayList$Itr.next(Unknown Source)
  at com.hasse.Muster.main(Muster.java:11)

这就会抛出一个下面的异常,迭代终止。
 
关于modCount,API解释如下:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
也就是说,modCount记录修改此列表的次数:包括 改变列表的结构,改变列表的大小,打乱列表的顺序等使正在进行迭代产生错误的结果。
Tips:仅仅设置元素的值并不是结构的修改
我们知道的是ArrayList是线程不安全的,如果在使用迭代器的过程中有其他的线程修改了List就会抛出ConcurrentModificationException,这就是 Fail-Fast机制。

在Java中遍历List时会用到Java提供的Iterator,Iterator十分好用,原因是:

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

  Java中的Iterator功能比较简单,并且只能单向移动:

  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

  (2) 使用next()获得序列中的下一个元素。

  (3) 使用hasNext()检查序列中是否还有元素。

  (4) 使用remove()将迭代器新返回的元素删除。

只要看看下面这个例子就一清二楚了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;
public class Muster {
 
     public static void main(String[] args) {
         ArrayList list = new ArrayList();
         list.add( "a" );
         list.add( "b" );
         list.add( "c" );
         Iterator it = list.iterator();
         while (it.hasNext()){
             String str = (String) it.next();
             System.out.println(str);
         }
     }
}

运行结果:

a
b
c

可以看到,Iterator可以不用管底层数据具体是怎样存储的,都能够通过next()遍历整个List。

但是,具体是怎么实现的呢?背后机制究竟如何呢?

这里我们来看看Java里AbstractList实现Iterator的源代码

?
1 . public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { // List接口实现了Collection<E>, Iterable<E>
2
3 .    protected AbstractList() { 
4 .    } 
5 .   
6 .    ... 
7
8 .    public Iterator<E> iterator() { 
9 .    return new Itr();  // 这里返回一个迭代器
10 .    } 
11
12 .    private class Itr implements Iterator<E> {  // 内部类Itr实现迭代器
13 .      
14 .    int cursor = 0
15 .    int lastRet = - 1
16 .    int expectedModCount = modCount; 
17
18 .    public boolean hasNext() {  // 实现hasNext方法
19 .            return cursor != size(); 
20 .    } 
21
22 .    public E next() {  // 实现next方法
23 .            checkForComodification(); 
24 .        try
25 .        E next = get(cursor); 
26 .        lastRet = cursor++; 
27 .        return next; 
28 .        } catch (IndexOutOfBoundsException e) { 
29 .        checkForComodification(); 
30 .        throw new NoSuchElementException(); 
31 .        } 
32 .    } 
33
34 .    public void remove() {  // 实现remove方法
35 .        if (lastRet == - 1
36 .        throw new IllegalStateException(); 
37 .            checkForComodification(); 
38
39 .        try
40 .        AbstractList. this .remove(lastRet); 
41 .        if (lastRet < cursor) 
42 .            cursor--; 
43 .        lastRet = - 1
44 .        expectedModCount = modCount; 
45 .        } catch (IndexOutOfBoundsException e) { 
46 .        throw new ConcurrentModificationException(); 
47 .        } 
48 .    } 
49
50 .    final void checkForComodification() { 
51 .        if (modCount != expectedModCount) 
52 .        throw new ConcurrentModificationException(); 
53 .    } 
54 .    } 
55 .}

可以看到,实现next()是通过get(cursor),然后cursor++,通过这样实现遍历。

这部分代码不难看懂,唯一难懂的是remove操作里涉及到的expectedModCount = modCount;

在网上查到说这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。

从源代码里可以看到增删操作都会使modCount++,通过和expectedModCount的对比,迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!
在第一个例子基础上添加一条语句:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.*;
public class Muster {
 
     public static void main(String[] args) {
         ArrayList list = new ArrayList();
         list.add( "a" );
         list.add( "b" );
         list.add( "c" );
         Iterator it = list.iterator();
         while (it.hasNext()){
             String str = (String) it.next();
             System.out.println(str);
             list.add( "s" );        //添加一个add方法
         }
     }
}

运行结果:

a
Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
  at java.util.ArrayList$Itr.next(Unknown Source)
  at com.hasse.Muster.main(Muster.java:11)

这就会抛出一个下面的异常,迭代终止。
 
关于modCount,API解释如下:
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
也就是说,modCount记录修改此列表的次数:包括 改变列表的结构,改变列表的大小,打乱列表的顺序等使正在进行迭代产生错误的结果。
Tips:仅仅设置元素的值并不是结构的修改
我们知道的是ArrayList是线程不安全的,如果在使用迭代器的过程中有其他的线程修改了List就会抛出ConcurrentModificationException,这就是 Fail-Fast机制。

猜你喜欢

转载自www.cnblogs.com/xiongmozhou/p/10085105.html