使用 Streams
一个 Stream
是在其上顺序和并行聚合操作可以执行元素的序列。任何给定的 Stream
都可能有无限量的数据流过它。结果,从 Stream
接收的数据在到达时被单独处理,而不是完全对数据进行批处理。当与 lambda 表达式结合使用时,它们提供了一种使用函数方法对数据序列执行操作的简明方法。
例如: ( 看到它在 Ideone 工作 )
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
fruitStream.filter(s -> s.contains("a"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
输出:
苹果
香蕉
ORANGE
PEAR
上述代码执行的操作可归纳如下:
-
使用静态工厂方法
Stream.of(values)
创建一个包含有序Stream
水果String
元素的Stream<String>
。 -
所述
filter()
操作只保留匹配给定的谓词(元件,当由所述谓词返回真测试),该元件。在这种情况下,它保留了包含a
的元素。谓词以 lambda 表达式给出。 -
所述
map()
操作变换使用给定的函数,称为映射器的每个元素。在这种情况下,每个水果String
使用方法参考String::toUppercase
映射到其大写的String
版本。请注意,如果映射函数返回与其输入参数不同的类型,则
map()
操作将返回具有不同泛型类型的流。例如,在Stream<String>
上调用.map(String::isEmpty)
会返回一个Stream<Boolean>
-
所述
sorted()
操作根据其自然顺序排序Stream
的元素(按字典顺序,在String
的情况下)。 -
最后,
forEach(action)
操作执行一个动作,该动作作用于Stream
的每个元素,并将其传递给 Consumer 。在该示例中,每个元素都被简单地打印到控制台。该操作是终端操作,因此不可能再次对其进行操作。注意,由于终端操作,执行
Stream
上定义的操作。如果没有终端操作,则不处理流。流不能重用。一旦调用终端操作,Stream
对象就变得不可用。
操作(如上所示)被链接在一起以形成可以被视为对数据的查询。
关闭流
**请注意,通常不必关闭
Stream
。**只需要关闭在 IO 通道上运行的流。大多数Stream
类型不依赖于资源,因此不需要关闭。
Stream
界面扩展了 AutoCloseable
。可以通过调用 close
方法或使用 try-with-resource 语句来关闭流。
应该关闭 Stream
的示例用例是当你从文件创建线条时:
try (Stream<String> lines = Files.lines(Paths.get("somePath"))) {
lines.forEach(System.out::println);
}
Stream
接口还声明了 Stream.onClose()
方法,该方法允许你注册 Runnable
处理程序,这些处理程序将在流关闭时调用。一个示例用例是生成流的代码需要知道何时使用它来执行某些清理。
public Stream<String>streamAndDelete(Path path) throws IOException {
return Files.lines(path).onClose(() -> someClass.deletePath(path));
}
只有在 try-with-resources 语句显式或隐式调用 close()
方法时,才会执行运行处理程序。
处理订单
Stream
对象的处理可以是顺序的或并行的 。
在顺序模式中,元素按照 Stream
的来源顺序处理。如果订购了 Stream
(例如 SortedMap
实现或 List
),则保证处理与源的顺序相匹配。但是,在其他情况下,应注意不要依赖于排序(参见: Java HashMap
keySet()
迭代顺序是否一致? )。
例:
List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42);
// sequential
long howManyOddNumbers = integerList.stream()
.filter(e -> (e % 2) == 1)
.count();
System.out.println(howManyOddNumbers); // Output: 2
并行模式允许在多个内核上使用多个线程,但不保证元素的处理顺序。
如果在顺序 Stream
上调用多个方法,则不必调用每个方法。例如,如果过滤了 Stream
并且元素的数量减少为 1,则不会发生对诸如 sort
之类的方法的后续调用。这可以提高顺序 Stream
的性能 - 这是并行 Stream
无法实现的优化。
例:
// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
.filter(e -> (e % 2) == 1)
.count();
System.out.println(howManyOddNumbersParallel); // Output: 2
与容器(或集合 )的差异
虽然可以在 Container 和 Streams 上执行某些操作,但它们最终用于不同的目的并支持不同的操作。容器更关注元素的存储方式以及如何有效地访问这些元素。另一方面,Stream
不能直接访问和操作其元素; 它更专注于作为集体实体的对象组,并作为整体对该实体执行操作。Stream
和 Collection
是针对这些不同目的的单独的高级抽象。