1. 概述
Java 8 引入了流(Stream) API,它提供了一种处理元素序列的功能。流API支持在对象集合(如列表、集合)上进行操作链式处理,以达到期望的结果。
本教程将探讨如何将流用作可迭代的序列。
2. 可迭代与迭代器
Iterable<T>
接口自Java 1.5起可用。实现此接口的类允许其对象成为for-each
循环的目标。实现类不存储任何关于迭代状态的信息,并应生成有效的迭代器。
Collection
接口扩展了Iterable
接口,所有Collection
接口的具体实现,如ArrayList
或HashSet
,通过实现Iterable
的iterator()
方法来产生迭代器。
Iterator<T>
接口也是Java集合框架的一部分,自Java 1.2起可用。实现Iterator<T>
的类必须提供遍历集合的方法,例如移动到下一个元素、检查是否有更多元素或从集合中删除当前元素:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
3. 问题陈述
现在我们了解了迭代器和可迭代接口的基础知识以及它们的作用,接下来理解问题的核心。
实现了Collection
接口的类本质上也实现了Iterable<T>
接口。然而,流有一些不同。值得注意的是,流Stream<T>
扩展的基流接口BaseStream<T>
有iterator()
方法,但并未实现Iterable
接口。
这个限制带来了一个挑战:无法直接在流上使用增强的for-each
循环。
接下来的章节我们将探讨一些解决这个问题的方法,并最终讨论为什么流不同于集合,不扩展Iterable
接口的原因。
4. 使用iterator()
方法将流转换为可迭代
流接口的iterator()
方法返回流中元素的迭代器。这是一个终端流操作:
Iterator<T> iterator();
然而,我们仍然不能直接使用这个迭代器在增强的for-each
循环中:
private void streamIterator(List<String> listOfStrings) {
Stream<String> stringStream = listOfStrings.stream();
// this does not compile
for (String eachString : stringStream.iterator()) {
doSomethingOnString(eachString);
}
}
for-each
循环适用于可迭代对象,而不是迭代器。为了解决这个问题,我们需要将迭代器转换为Iterable
实例,然后应用所需的for-each
循环。由于Iterable<T>
是一个函数式接口,我们可以使用lambda表达式编写代码:
for (String eachString : (Iterable<String>) () -> stringStream.iterator()) {
doSomethingOnString(eachString);
}
利用方法引用的方式,我们可以进一步重构代码:
for (String eachString : (Iterable<String>) stringStream::iterator) {
doSomethingOnString(eachString.toLowerCase());
}
也可以使用临时变量iterableStream
先持有Iterable
,然后再在for-each
循环中使用:
Iterable<String> iterableStream = () -> stringStream.iterator();
for (String eachString : iterableStream) {
doSomethingOnString(eachString, sentence);
}
5. 将流转换为for-each
循环中的可迭代
我们之前讨论过Collection
接口继承自Iterable
接口。因此,我们可以将给定的流转换为集合,并将其作为可迭代对象使用:
for(String eachString : stringStream.collect(Collectors.toList())) {
doSomethingOnString(eachString);
}
6. 为何流不实现Iterable
我们已经看到如何使用流作为可迭代对象。列表和集合等数据结构通常会存储数据并在其生命周期中多次使用。这些对象会被传递给不同的方法,多次改变,并且最重要的是,它们会被反复遍历。
相反,流是一种一次性使用的数据结构,因此并不设计用于使用for-each
循环进行迭代。流通常不期望被反复遍历,当流已关闭并进行操作时,会抛出IllegalStateException
。因此,尽管流提供了iterator()
方法,但它并不扩展Iterable
接口。
7. 总结
在这篇文章中,我们探讨了流作为可迭代序列的不同使用方式。
我们简要讨论了可迭代和迭代器的区别,以及为什么Stream<T>
不实现Iterable<T>
接口。
如往常一样,所有的代码示例可以在GitHub上找到。