Java 8 流与集合存储

Java 8 Stream vs Collection Storage

我一直在阅读 Java 8 Streams 以及从数据源流式传输数据的方式,而不是从整个集合中提取数据。

我在 an article 上特别阅读了关于 Java 8.

中的流的引述

No storage. Streams don't have storage for values; they carry values from a source (which could be a data structure, a generating function, an I/O channel, etc) through a pipeline of computational steps.

我理解从一个源逐个流式传输数据的概念。我不明白的是,如果您是从集合中流式传输的,怎么没有存储空间?该集合已存在于堆中,您只是从该集合流式传输数据,该集合已存在于 "storage" 中。

如果我只是使用标准的 for 循环遍历集合,那么在内存占用方面有什么不同?

将流想象成连接到作为数据结构的水箱的喷嘴。喷嘴没有自己的存储空间。当然,流提供的水(数据)来自具有存储空间的源,但流本身没有存储空间。将另一个喷嘴(流)连接到您的水箱(数据结构)不需要存储数据的全新副本。

流只是数据的视图,它没有自己的存储空间,您不能修改底层集合(假设它是一个建立在top a collection) 通过流。这就像 "read only" 访问。

如果您有任何 RDBMS 经验 - 这与 "view" 完全相同。

关于流和存储的声明意味着流没有任何存储它自己的。如果流的源是一个集合,那么显然该集合具有存储元素的存储空间。

让我们以那篇文章中的一个例子为例:

int sum = shapes.stream()
                .filter(s -> s.getColor() == BLUE)
                .mapToInt(s -> s.getWeight())
                .sum();

假设 shapes 是一个有数百万个元素的 Collection。人们可能会想象 filter 操作会迭代源中的元素并创建一个临时结果集合,该集合可能也有数百万个元素。 mapToInt 操作可能会遍历该临时集合并生成要求和的结果。

事情不是这样的。没有临时的中间集合。流操作是流水线操作,因此来自 filter 的元素通过 mapToInt 传递到 sum,而不存储到集合中或从集合中读取。

如果流源不是集合——比如说,元素是从网络集合中读取的——根本不需要任何存储。如下所示的管道:

int sum = streamShapesFromNetwork()
                .filter(s -> s.getColor() == BLUE)
                .mapToInt(s -> s.getWeight())
                .sum();

可能会处理数百万个元素,但它不需要在任何地方存储数百万个元素。

  1. Collection是一个数据结构。根据问题,您决定使用哪个集合,如 ArrayList、LinekedList(考虑时间和 space 复杂性)。而 Stream 只是一种处理工具,让您的生活变得轻松。

  2. 其他区别是,您可以将Collection视为in-memory数据结构,您可以在其中添加、删除元素。 在 Stream 中,您可以执行两种操作:

    一个。 中级操作:对结果集进行过滤、映射、排序、限制
    b. 终端操作:forEach,将结果集收集到一个集合中。

    但是如果您注意到,您无法使用流添加或删除元素。

  3. Stream是一种迭代器,可以通过流遍历集合。注意,你只能遍历一次stream,举个例子让你更好的理解:

示例 1:

List<String> employeeNameList = Arrays.asList("John","Peter","Sachin");
    Stream<String> s = employeeNameList.stream();

    // iterate through list
    s.forEach(System.out :: println);  // this work's perfectly fine
    s.forEach(System.out :: println);  // you will get IllegalStateException, stating stream already operated upon

因此,您可以推断,您可以根据需要迭代任意多次集合。但是对于流来说,一旦你迭代,它就不会记住它应该做什么。所以,你需要重新指导一下。

我希望,很清楚。

之前的答案大部分是正确的。然而,更直观的反应如下(对于 Google 降落在这里的乘客):

将流视为 UNIX 文本管道: 猫 input.file | … | grep ... > output.file

与处理的输入数据相比,这些 UNIX 文本实用程序通常会消耗少量 RAM。

情况并非总是如此。想想 "sort"。该算法需要将中间内容保存在内存中。 流也是如此。有时需要时态数据。大多数时候不会。

作为一个额外的明喻,在某种程度上 "cloud-serverless APIs" 遵循相同的 UNIX 管道 o Java 流设计。 在有一些输入数据要处理之前,它们不存在于内存中。云 OS 将启动它们并注入输入数据。输出逐渐发送到其他地方,因此 cloud-serverless-API 不会消耗很多资源(大部分时间)。

在这种情况下不是绝对的"trues"。