Java 8中,流(Streams)和集合(Collections)有什么区别?

38

我正在学习Java 8中的Streams。有一个概念让我很困惑:

集合是一种内存中的数据结构,它保存着当前数据结构中所有的值——在将元素添加到集合之前,所有元素都必须先计算出来。相比之下,流是一种概念上的固定数据结构,其中的元素可以按需计算。

我不理解,如何才能使集合仅包含那些必须在添加到集合之前计算的值?此外,“流”与“固定数据结构”的比较是什么意思?


2
不清楚你想问什么。你不明白“在添加之前进行计算”或者“概念上固定的数据结构”这些术语的哪个部分吗?此外,你是否询问了这些术语的含义,正如你的问题所述,还是询问集合和流之间的区别,正如你标题所述,后者更为微不足道。 - user207421
两者的目的不同。从根本上讲,集合的目标是管理其元素,如添加、删除、在特定索引处添加等。而流的目标是处理这些元素,如查找与条件匹配的任何元素,基于条件过滤元素等。 - lupchiazoem
该引文来自*Java 8实战* "4.3. 流与集合"一章。 - Jason Law
2个回答

69
您没有提供您引用的来源,所以让我向您引用一下javadoc中的内容:

流与集合在多个方面有所不同:

  • 无存储。流不是存储元素的数据结构;相反,它通过计算操作管道从诸如数据结构、数组、生成器函数或 I/O 通道等源头传递元素。
  • 功能性质。对流的操作会产生结果,但不会修改其源。例如,从集合获得的 Stream 进行过滤将产生一个新的没有过滤元素的 Stream,而不是从源集合中删除元素。
  • 延迟执行。许多流操作(例如过滤、映射或重复项删除)可以实现延迟执行,从而暴露出优化机会。例如,“查找具有三个连续元音字母的第一个字符串”无需检查所有输入字符串。流操作分为中间操作(生成流)和终端操作(产生值或副作用)。中间操作始终是延迟执行的。
  • 可能无限。虽然集合具有有限大小,但流不必如此。诸如 limit(n)findFirst() 的短路操作可以使对无限流的计算在有限时间内完成。
  • 可消耗。流的元素只在流的生命周期中访问一次。与 Iterator 类似,必须生成新流才能重新访问源的相同元素。
相比之下,Collection 是对象(元素)的容器。除非将对象预先添加到集合中,否则无法从集合中获取(检索)对象。

除非对象先前添加到集合中,否则无法从集合中获取(检索)对象。这种说法是不正确的,没有这样的约定。一个集合可以在初始时就是非空的;它也可以是惰性的,在你请求时才实现一个元素(例如,Hibernate的惰性集合)。甚至有限大小也是一个值得怀疑的属性,因为迭代器可以返回无限多个元素。 - Marko Topolnik
5
@MarkoTopolnik,“Iterator”和“Collection”不是同一个概念,请不要混淆。集合有“size()”方法。---我认为在构造期间给出一个元素列表与在那个时候添加它们是相同的。这些元素在成为集合的成员之前就已经存在了,这才是关键。---但你说得对,实现“Collection”接口而没有实际存储支持也是可能的,Hibernate就是一个例子。但即使在那里,这仍然是从实际存储(数据库)中进行惰性加载的技术。这些元素已经存在。 - Andreas
集合的契约规定了迭代器,所以我不会混淆任何东西。集合确实有size()方法,这就是为什么我说它是“有问题的”——例如,Map也有size()方法,但你可以在HashMap中拥有超过2^31个元素。将元素描述为“实际存在”的特征是哲学性的。它们也可以在实现时计算,例如通过掷骰子来确定。 - Marko Topolnik
3
集合 API 已经进行了改进,可以容纳超过 2³¹ 个元素。在这种情况下,集合必须从 size() 返回 Integer.MAX_VALUE。然而,当前的 HashMap 实现具有一个 int 类型大小字段,它会静默地溢出,因此它不遵循这个惯例。从这样的映射创建的流将无法报告正确的 long 类型大小,所以我认为这不是受支持的用例,而只是一个缺失的错误检测。 - Holger
@holger 是的,CollectionMap 都指定了这个 Integer.MAX_VALUE 逻辑。随着 Java 堆的增长,HashMap 的不兼容性将成为一个更紧迫的问题。 - Marko Topolnik

4

一些基本的区别如下:

集合和流之间的区别

但这些只是有限的区别。要了解更多,请深入探索。集合类似于CD/DVD,而流则像电影。流可以是无状态或有状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接