功能简介
Iterable
接口作为Collection
的根接口,负责实现所有集合类的for-each语句功能。
Iterable
类定义如下:
/**
* 实现此接口允许对象成为“for-each loop”语句的目标。
*/
public interface Iterable<T> {
Iterator<T> iterator();
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);
}
}
tips:default
是JDK 1.8 新加的关键字,Java允许该关键字注明的方法在接口中有默认实现。
下面针对每个方法进行详细的介绍。
方法介绍
- iterator
Iterator<T> iterator();
该方法用来返回一个迭代器对象,是Iterable
的主要方法。
回想我们使用迭代器的场景,是不是开头会有这样一段语句(假如迭代ArrayList
):
Iterator<ArrayList> iterator = arrayList.iterator();
其实,在每个集合类的内部都对iterator()
方法进行了覆写以实现自身的迭代操作。
所以当调用集合的iterator()
,会返回该集合所实现的特定迭代器对象。
而作为根类型的Iterable
接口提供的这个方法,恰恰就是方便各个集合对其重写实现各自的迭代器。
这也是一种设计模式的思想——迭代器模式,对该设计模式的详细介绍可以参考另一篇文章:
Java 设计模式通关之路——迭代器模式
- forEach
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
该方法用来对每个集合的元素进行遍历,可以看到其底层实现也是用的for-each
语句,那么这两者有什么区别呢?
作为Java8新加的方法,该方法支持对集合中的元素做一些自定义的操作。
可以看到,传入的类型是Consumer
类型,而Consumer
类型是Java8新增的一个消费型函数式接口,其目的是为了支持Java8新增的Lombda表达式,通过方法的实现来执行具体操作。
这里简单说一下Consumer
的用法便于理解,你可以这样去使用Consumer
:
Consumer<Integer> sop = x -> System.out.println("This number is " + x);
sop.accept(1);
/* Output
This number is 1
*/
也就是说,一个Consumer
对象实例可以代表一个Lombda表达式所进行的操作。
而accept()
方法就是对传入的指定类型执行所定义的操作。
回到forEach()
方法中来,该方法的入参需要表明你要对该集合中的元素所要执行的自定义操作。
而在for
循环中对集合的每个元素执行给定的操作,直到遍历完毕或者引发异常。
- spliterator
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
该方法主要用于对集合元素进行分割遍历,通常每个集合中对其进行了自身的默认实现。
Spliterator
是一个可分割迭代器,目的是为了并行遍历元素。
可以看一下对ArrayList
集合进行分割迭代的结果:
List<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(6);
arrayList.add(7);
arrayList.add(8);
arrayList.add(9);
Spliterator<Integer> spl = arrayList.spliterator();
spl.forEachRemaining(value -> System.out.println("Init : " + value));
/* Output
Init : 1
Init : 2
Init : 3
Init : 4
Init : 5
Init : 6
Init : 7
Init : 8
Init : 9
*/
未分割时,分割迭代器将集合元素全部迭代出来。
而进行一次分割之后的结果呢:
··· // 省略赋值操作
Spliterator<Integer> spl = arrayList.spliterator();
Spliterator<Integer> spl1 = spl.trySplit();
spl1.forEachRemaining(value -> System.out.println("First : " + value));
/* Output
First : 1
First : 2
First : 3
First : 4
*/
可以看到,只迭代了一半的元素,而分割两次的结果呢:
··· // 省略赋值操作
Spliterator<Integer> spl = arrayList.spliterator();
Spliterator<Integer> spl1 = spl.trySplit();
Spliterator<Integer> spl2 = spl1.trySplit();
spl2.forEachRemaining(value -> System.out.println("Second : " + value));
/* Output
Second : 1
Second : 2
*/
只迭代了一半的一半。