通过使用迭代器,我们可以很轻松地遍历一个对象集合。
Java的迭代器接口为Iterator,可通过Iterable的iterator()方法获取。
Connection接口继承了Iterable接口,因此所有Connection的子类都可使用迭代器进行遍历。
Iterable接口仅有3个方法:
Iterator iterator();
default void forEach(Consumer super T> action)
default Spliterator spliterator()
iterator()
此方法返回一个Iterator对象,Iterator中有4个方法:
boolean hasNext(); // 判断迭代器中是否还有元素
E next(); // 获取下一个元素
default void remove(); // 删除最近一次从迭代器返回的元素,此方法在每次调用next()后只能调用一次
default void forEachRemaining(Consumer super E> action); // 对剩余元素进行遍历
我们通过以下代码来分析上述4个方法:
public static void run() {
List list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add("" + i);
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) { // [1]
String str = iterator.next(); // [2]
Log.d(TAG, "run remove:" + str);
iterator.remove(); // [3]
// iterator.remove(); // [4]
iterator.next(); // [5]
iterator.forEachRemaining(new Consumer() { // [6]
@Override
public void accept(String s) {
Log.d(TAG, "forEachRemaining:" + s);
}
});
}
list中保存了“1”~“4”共5个字符串,程序运行结果为:
D IterableTest:run remove:0
D IterableTest:forEachRemaining:2
D IterableTest:forEachRemaining:3
D IterableTest:forEachRemaining:4
[1]:判断是否有下一个元素,第一次执行到此处是肯定是有下一个元素的,于是进入while循环。
[2]:获取下一个元素,第一次进入while循环获取到的是字符串“0”。
[3]:删除最近一次获取到的元素,即[2]中取得的元素。
[4]:连续执行两次删除操作会报异常。
[5]:再次获取下一个元素,第一次进入while循环获取到的是字符串“1”。
[6]:对剩余元素进行遍历,由于前两个元素已被取出,因此仅对剩余的“2”~“4”进行遍历,且遍历完成后,第二次while判断hasNext()时,由于已经遍历完,因此返回false,退出while循环。
去掉[4]的注释后,程序运行报以下异常:
java.lang.IllegalStateException
at java.base/java.util.ArrayList$Itr.remove(ArrayList.java:976)
at com.xjn.javatest.IteratorTest.run(IteratorTest.java:22)
at test.com.xjn.javatest.IteratorTestTest.testRun(IteratorTestTest.java:32)
......
forEach(Consumer super T> action)
此方法实际上就是使用了增强型for循环,其源码如下:
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
通过以下代码进行测试:
public static void forEachTest() {
List list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("" + i);
}
list.forEach(element -> Log.d(TAG, "forEachTest:" + element)); // [1]
}
[1]:这里使用了lamba表达式,上例中的Iterator.forEachRemaining()同样支持这种写法。
运行结果为:
D IterableTest:forEachTest:0
D IterableTest:forEachTest:1
D IterableTest:forEachTest:2
spliterator()
可分割的迭代器(Splitable Iterator)。
分割元素,并返回分割出来的元素对应的Spliterator对象,即可分割的迭代器(Splitable Iterator)。
Spliterator包含的方法有:
boolean tryAdvance(Consumer super T> action); // 如果有剩余元素,则在此元素上执行对应动作,并返回true;否则返回false。
default void forEachRemaining(Consumer super T> action); // 遍历剩余所有元素
Spliterator trySplit(); // 从当前Spliterator分割一部分出来并返回
long estimateSize(); // 返回剩余元素个数
default long getExactSizeIfKnown(); // 若有特征SIZED,则返回estimateSize(),否则返回-1
int characteristics(); // 获取特征
default boolean hasCharacteristics(int characteristics); // 判断是否存在指定特征
default Comparator super T> getComparator(); // 如果元素是通过Comparator排序,则返回Comparator;如果是自然排序,返回null;否则抛出异常IllegalStateException。
以下例进行说明:
public static void spliteratorTest() {
List list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("" + i);
}
Spliterator split = list.spliterator();
split.tryAdvance(s -> Log.d(TAG, "spliteratorTest:" + s)); // [1]
try {
Log.d(TAG, "spliteratorTest Comparator:" + split.getComparator()); // [2]
} catch (IllegalStateException e) {
Log.e(TAG, "spliteratorTest Comparator e:" + e); // [3]
}
Log.d(TAG, "spliteratorTest ExactSizeIfKnown:" + split.getExactSizeIfKnown()); // [4]
Log.d(TAG, "spliteratorTest Size:" + split.estimateSize()); // [5]
Log.d(TAG, "spliteratorTest characteristics:" + String.format("0x%x", split.characteristics())); // [6]
Spliterator split1 = split.trySplit(); // [7]
split.forEachRemaining(s -> Log.d(TAG, "spliteratorTest split:" + s)); // [8]
split1.forEachRemaining(s -> Log.d(TAG, "spliteratorTest split1:" + s)); // [9]
}
[1]:通过tryAdvance()获取一个元素。
[2]~[3]:获取Comparator,由于ArrayList没有排序,因此抛出异常。
[4]~[6]:获取特性、剩余元素个数等
[7]:将Spliterator进行分割
[8]~[9]:分别遍历分割后的2个Spliterator剩余的所有元素。
输出结果为:
D IterableTest:spliteratorTest:0
E IterableTest:spliteratorTest Comparator e:java.lang.IllegalStateException
D IterableTest:spliteratorTest ExactSizeIfKnown:9
D IterableTest:spliteratorTest Size:9
D IterableTest:spliteratorTest characteristics:0x4050
D IterableTest:spliteratorTest split:5
D IterableTest:spliteratorTest split:6
D IterableTest:spliteratorTest split:7
D IterableTest:spliteratorTest split:8
D IterableTest:spliteratorTest split:9
D IterableTest:spliteratorTest split1:1
D IterableTest:spliteratorTest split1:2
D IterableTest:spliteratorTest split1:3
D IterableTest:spliteratorTest split1:4
特征有以下可选值,可以是多个值的组合:
DISTINCT = 0x00000001; // 元素集合中没有相同的元素
SORTED = 0x00000004; // 元素是有序的
ORDERED = 0x00000010; // 以为元素定义顺序,trySplit()、tryAdvance()等就按这个顺序进行遍历
SIZED = 0x00000040; // 元素个数有限,可返回具体的个数
NONNULL = 0x00000100; // 元素不能为null
IMMUTABLE = 0x00000400; // 元素不可变,即遍历过程禁止修改(新增、删除、修改)
CONCURRENT = 0x00001000; // 对元素的修改是线程安全的
SUBSIZED = 0x00004000; // 表示由trySplit()分割后的所有Spliterator都具有SIZED和SUBSIZED特征值
问:Iterable与Iterator的关系,为何不直接在Iterable加入迭代接口?
答:Iterable提供了获取迭代器的接口,同时也提供了一个forEach的接口(非迭代方式)。
Iterator则提供了更丰富的迭代方式。
之所以不直接在Iterable中提供Iterator中的迭代接口,是因为有些容器可能会提供多种迭代方式,那么就可以通过返回不同的Iterator实例来实现。
--end