Iterator & Iterable

增强 for 循环:

Set<Integer> set = new TreeSet<>();
for (Integer num : set) {
    System.out.println(num);
}

这是个语法糖,编译成字节码后,编译器会替换成 iterator:解语法糖 (desugar)

  • 首先调用 Set.iterator
  • 然后调用 Iterator.hasNext
  • 最后调用 Iterator.next

注意:数组类型的增强 for 循环不太一样,是使用 for-i 来实现的。

L1
    LINENUMBER 9 L1
    ALOAD 1
    INVOKEINTERFACE java/util/Set.iterator ()Ljava/util/Iterator; (itf)
    ASTORE 2
L2
    FRAME APPEND [java/util/Set java/util/Iterator]
    ALOAD 2
    INVOKEINTERFACE java/util/Iterator.hasNext ()Z (itf)
    IFEQ L3
    ALOAD 2
    INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object; (itf)
    CHECKCAST java/lang/Integer
    ASTORE 3
L4
    LINENUMBER 10 L4
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V

语法糖:

  • 自动拆装箱也是一种语法糖,编译器会替换成 Integer.valueOf() 等

为什么需要迭代器:

对于不同的集合,都拥有各自的遍历模式。

那有没有一种方法,可以顺序的访问集合中的各个元素,而又不需要暴露该对象的内部表示。

设计模式:迭代器模式

public interface Iterator<E> {
	// 返回true如果迭代对象还存在元素
    boolean hasNext();

    // 返回迭代对象中的下一个元素。如果不存在则抛出NoSuchElementException
    E next();

    // 删除此迭代器返回的最后一个元素(可选)。此方法在每次调用next时只能调用一次。
    // 如果在迭代过程中,使用除了此方法以外的任何方式修改了底层集合,那么迭代器的行为是未指定的
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    
    // 为每个剩余元素执行给定的操作,直到处理完所有元素或操作引发异常。
    // 如果指定了迭代的顺序,则按照迭代的顺序执行操作。
    // 操作引发的异常将被转发给调用者。
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

fail-fast 机制

如果在迭代器被创建之后,集合的结构被除了迭代器自己的 remove 和 add 方法以外的其他方式修改了,那么迭代器将会抛出 ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。

ArrayList的一段 JavaDoc:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

This class is a member of the Java Collections Framework.

注意:faild-fast 只能检测在迭代过程中是否有其他线程修改了集合,因为这类集合本身就不是线程安全的,所以不要依赖它来保证程序的正确性。

猜你喜欢

转载自www.cnblogs.com/demojie/p/12596147.html
今日推荐