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迭代器接口用于访问集合中的每一个元素,该接口拥有4个方法。其中forEachRemaining方法是JDK8 新加入的方法。
forEachRemaining(Consumer<? super E> action):为每个剩余元素执行给定的操作,直到所有的元素都已经被处理或行动将抛出一个异常。
通过反复调用next方法,就可以对集合完成访问操作。但是如果到达集合末尾,next方法将会抛出一个NoSuchElementException。
因此,需要在调用next方法之前,调用hasNext方法做判断。如果迭代器对象还有多个可供访问的元素,则这个方法返回true。
使用方式:请求一个迭代器,并在hasNext返回true时反复地调用next方法。
Collection<String> collection = ...;
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
//do something with element
}
当然也可以使用更加简练的foreach语句来遍历:
for (String element : collection) {
//do something with element
}
而编译器也只是简单地把foreach循环翻译成带有迭代器的循环。
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
for(String s : list) {
System.out.println(s);
}
}
}
对于如上的代码,使用javap反编译出来的结果:
D:\N3verL4nd\Desktop>javac Test.java
D:\N3verL4nd\Desktop>javap -c Test
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 111
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #6 // String 222
20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: ldc #7 // String 333
29: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
34: pop
35: aload_1
36: invokeinterface #8, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
41: astore_2
42: aload_2
43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
48: ifeq 71
51: aload_2
52: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
57: checkcast #11 // class java/lang/String
60: astore_3
61: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
64: aload_3
65: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
68: goto 42
71: return
}
D:\N3verL4nd\Desktop>
可以看到foreach确实被翻译成了Iterable的处理方式。
说白了,foreach在Java的实现就是一个语法糖罢了。
另外,只有对象实现了Iterable接口,才可以使用foreach。
public interface Iterable<T> {
Iterator<T> iterator();//返回一个在一组T类型的元素上进行迭代的迭代器
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Collection接口扩展了Iterable接口,所以对于标准库中的任何集合都可以使用foreach循环。
在Java SE 8中我们可以使用forEach方法,该方法接受一个lambda表达式,而不用再写for/while循环。
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.forEach(s -> System.out.println(s));
list.forEach(System.out::println);
对于spliterator方法:
Spliterator是Java8引入的新接口,顾名思义,Spliterator可以理解为Iterator的Split版本(但用途要丰富很多)。使用Iterator的时候,我们可以顺序地遍历容器中的元素,使用Spliterator的时候,我们可以将元素分割成多份,分别交于不同的线程去遍历,以提高效率。使用 Spliterator 每次可以处理某个元素集合中的一个元素 — 不是从 Spliterator 中获取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法对元素应用操作。但 Spliterator 还可以用于估计其中保存的元素数量,而且还可以像细胞分裂一样变为一分为二。这些新增加的能力让并行处理代码可以很方便地将工作分布到多个可用线程上完成。
Java集合类库中的迭代器与其他类库中的迭代器在概念上有很大的不同。应该将Java迭代器认为是位于两个元素之间。
当调用next时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用。
Iterator接口的remove方法将会删除上次调用next方法时返回的元素。只有在调用完next方法后才可以调用remove方法,且只能调用一次。
Iterator在功能上可以完全替代Enumeration。后者目前还保留在Java标准库里纯粹是为了兼容老API(例如Hashtable、Vector、Stack等老的collection类型)。Iterator相比Enumeration有以下区别:前者的方法名比后者简明扼要;前者添加了一个可选的remove()方法(“可选”意味着一个实现Iterator接口的类可以选择不实现remove()方法);前者是“fail-fast”的——如果它在遍历过程中,底下的容器发生了结构变化(例如add或者remove了元素),则它会抛出ConcurrentModificationException;后者没有这种检查机制;前者可以配合Iterable<E>接口用于Java 5的for-each循环中。