我正在努力详细了解java-8流。
来自streams上的oracle文档页面:
Streams在几个方面与集合不同:
无存储空间。流不是存储元素的数据结构;相反,它通过计算操作管道传递来自数据结构,数组,生成器函数或I / O通道等源的元素。
流操作和管道
流操作分为中间操作和终端操作,并组合在一起形成流管道。
流管道由源(例如Collection,数组,生成器函数或I / O通道)组成;然后是零个或多个中间操作,例如Stream.filter或Stream.map;和一个终端操作,如Stream.forEach或Stream.reduce。
中级操作返回新流
除了文档,我还经历了相关的SE问题:
How does streams in Java affect memory consumption?
引用的所有地方都说由于流操作的管道衬里而没有消耗额外的内存。原始流将通过管道传递。
来自Benjamin博客的一个实例:
List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
但是当像filter, map and sorted
这样的中间操作返回新流时,为什么它不会增加内存消耗?我在这里错过了什么吗?
答案 0 :(得分:7)
我认为你在字面上解释了文档的“无存储”部分,因为“没有内存增加”。这种解释是错误的:“无存储”意味着“没有存储流元素”。 Stream对象本身代表一个固定的开销,就像空集合有一些开销一样,因此流本身的大小不计算。
但是当filter,map和sorted等中间操作返回新流时,为什么不增加内存消耗呢?
确实如此。但是,尺寸的增加是固定的,即O(1)增加。这与集合形成对比,集合中n
元素集合的副本增加为O(n)。
答案 1 :(得分:1)
试着在这里阅读http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html和/或http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/,我认为你想要解释的概念相当好。
基本上你可以看到的是,对于大多数中间操作,它们并不是每次操作都会同时发生。一次1个元素,它们通过所有中间操作处理,并根据终端操作丢弃或放入集合/添加到总和/打印等。如果它是一个收集类型的终端操作,那么在创建这个新集合时当然会有一些内存开销,但是在流中单独保存没有任何东西。这也是为什么你不能两次(部分)迭代流的原因。
但是有些操作,例如stream.sorted(func)在处理过程中可能需要一些状态。