Java 中有关抽象的可遍历的对象有 Iterator, Iterable 和 Java 8 的 Stream, Iterable 可简单的用如下代码转换为 Stream
StreamSupport.stream(iterable.spliterator(), false)
再回过头来,为什么要把 Iterator 或 Iterable 转换为 Stream, 因为 Iterator 和 Iterable 只提供有限的遍历操作,如 Iterator 接口的全部四个方法
hasNext() next() forEachRemaining(consumer) remove()
同样 Iterable 也只有 iterator()
, forEach(consumer)
, 和 spliterator()
方法。而 Java 8 的 Stream 就大不一样的,带有大量的链式操作方法,如 filter, map, flatMap, collect 等。
因此如果我们已有一个 Iterator 类型,能够被转换为 Stream 类型的话将会大大简化后续的转换,处理操作。具体的从 Iterator 到 Stream 的转换方式有两种
由于 Iterator 的大小是不确定的,有多少个元素完全由 hasNext() 决定的, spliteratorUnknownSize()
方法正好应了这一情景。代码如下
Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator(); Stream<Integer> targetStream = StreamSupport.stream( Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.SORTED), false); System.out.println(Arrays.toString(targetStream.toArray()));
输出会是
[3, 1, 2, null, 2]
前面的 Spliterator.SORTED 参数值是 characteristics
, 预定义了七个常量值,但是对于 Spliterators.splieratorUnknownSize(...)
方法来说无论传什么都不会影响到最终的结果。比如我们可以做下面一个测试
public void test(int characteristics) { System.out.printf("characteristics %5d: ", characteristics); Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator(); Stream<Integer> targetStream = StreamSupport.stream( Spliterators.spliteratorUnknownSize(sourceIterator, characteristics), false); System.out.println(Arrays.toString(targetStream.toArray())); } Arrays.asList( Spliterator.CONCURRENT, Spliterator.DISTINCT, Spliterator.IMMUTABLE, Spliterator.NONNULL, Spliterator.SIZED, Spliterator.SORTED, Spliterator.SUBSIZED).forEach(this::test);
输出结果如下:
characteristics 4096: [3, 1, 2, null, 2] characteristics 1: [3, 1, 2, null, 2] characteristics 1024: [3, 1, 2, null, 2] characteristics 256: [3, 1, 2, null, 2] characteristics 64: [3, 1, 2, null, 2] characteristics 4: [3, 1, 2, null, 2] characteristics 16384: [3, 1, 2, null, 2]
这里的 characteristics 传什么都行。
根据下面的分析,characteristics 用不着从常量定义中挑选,直接给 0
就行,写成下面那样
Spliterators.spliteratorUnknownSize(sourceIterator, 0)
像最前面那样 Iterable 可以轻松转换为 Stream, 所以先把 Iterator 变为 Iterable 再转化为 Stream。
Iterator<Integer> sourceIterator = Arrays.asList(3, 1, 2, null, 2).iterator(); Iterable<Integer> iterable = () -> sourceIterator; Stream<Integer> targetStream = StreamSupport.stream(iterable.spliterator(), false); System.out.println(Arrays.toString(targetStream.toArray()));
注意到上面由一个 Lambda 变 Iterator 为 Iterable 了,看 Iterable 接口的源代码
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); } }
只有一个抽象方法(其他两个为默认方法),所以可用
Iterable<Integer> iterable = () -> sourceIterator
声明一个 iterator() 返回 sourceIterator 的 Iterable 类型。
再看 Iterable 的默认方法 spliterator() 的实现,同样是调用的
Spliterators.spliteratorUnknownsSize(iterator(), 0)
这里的第二个参数 0
实际上不是 Spliterator 中的 CONCURRENT
, DISTINCT
, IMMUTABLE
, NONNULL
, SIZED
, SORTED
, SUBSIZED
中的任何一个值。
写到这里,通过参源代码阅读,前面所述的两种方式实质上没有一点区别。
下面例子创建一个无限大小的 Iterator (hasNext() 永远返回 true),然后由它转换成 Stream, 再调用 Stream 的 filter 和 limit 来检验它是一个真正的 Stream
public Stream<Integer> convert(Iterator<Integer> sourceIterator) { Iterable<Integer> iterable = () -> sourceIterator; return StreamSupport.stream(iterable.spliterator(), false); } @Test public void test() { Iterator<Integer> sourceIterator = new Iterator<Integer>() { private AtomicInteger count = new AtomicInteger(0); private Random random = new Random(); @Override public boolean hasNext() { return true; } @Override public Integer next() { System.out.println("next: " + count.incrementAndGet()); //每一次遍历将会打印计数 return random.nextInt(99999); } }; //无条件的获得 3 个元素即可 System.out.println(Arrays.toString(convert(sourceIterator).limit(3).toArray())); System.out.println(); //从流中过虑出小于 30000 的 3 个元素 System.out.println(Arrays.toString(convert(sourceIterator).filter(a -> a < 30000).limit(3).toArray())); }
下面是某一次的执行输出
next: 1 next: 2 next: 3 [11430, 20177, 64297] next: 4 next: 5 next: 6 next: 7 next: 8 next: 9 next: 10 next: 11 next: 12 next: 13 next: 14 next: 15 next: 16 next: 17 [19378, 16142, 9354]
该行为与 Stream 是相吻合的,因为 Stream 是一个 Lazy 的,它确实是一个流,无需事选知道流中将会有多少元素。