Java 在流中分组
Java grouping in stream
Java 8 个流允许我们在按任意约束分组的同时收集元素。例如:
Map<Type, List<MyThing>> grouped = stream
.collect(groupingBy(myThing -> myThing.type()));
然而,这有一个缺点,即必须完全读取流,因此没有机会对 grouped
上的未来操作进行惰性评估。
有没有办法进行分组操作得到类似Stream<Tuple<Type, Stream<MyThing>>>
的东西?甚至在概念上是否可以在不评估整个数据集的情况下以任何语言进行惰性分组?
我认为这没有意义,因为从惰性流 Stream<Tuple<Type, Stream<MyThing>>>
的一个分区流 (Tuple<Type, Stream<MyThing>>
) 读取可能会在其他分区中产生任意大量的消耗内存。
例如考虑按自然顺序排列的惰性正整数流,并按它们的最小质因数对它们进行分组。然后从分区流的最后一个接收到的元素读取将在之前接收到的流中产生越来越多的整数。
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream.
摘自文档:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
所以我认为没有办法在不消耗它和创建新流的情况下拆分它。
惰性分组的概念并没有真正意义。根据定义,分组意味着提前选择组,以避免为每个键搜索所有元素的开销。 "Lazy grouping" 看起来像这样:
List<MyThing> get(Type key) {
source.stream()
.filter(myThing -> myThing.type().equals(key))
.collect(toList());
}
如果您希望将迭代推迟到您知道需要它的时候,或者如果您想避免缓存分组映射的内存开销,这完全没问题。但是如果不提前迭代就无法优化选择过程。
Is it even conceptually possible to group lazily in any language without evaluating the whole data set?
不,如果不检查整个数据集或保证数据中存在可利用的模式,您就无法对整个数据集进行正确分组。例如,我可以将前 10,000 个整数懒惰地分组为奇偶数,但我不能懒惰地将 10,000 个整数的随机集合奇偶数分组。
至于以非终端方式分组...这似乎不是一个好主意。从概念上讲,流上的分组函数应该 return 多个流,就好像它在分支不同的流一样,Java 8 不支持。
如果你真的想使用原生 Stream 方法进行非终端分组,你可以滥用 sorted 方法。给它一个排序器,以不同方式对待组,但将组内的所有元素视为平等,你最终会得到 group1、group2、group3 等。这不会给你偷懒的评价,而是分组。
Java 8 个流允许我们在按任意约束分组的同时收集元素。例如:
Map<Type, List<MyThing>> grouped = stream
.collect(groupingBy(myThing -> myThing.type()));
然而,这有一个缺点,即必须完全读取流,因此没有机会对 grouped
上的未来操作进行惰性评估。
有没有办法进行分组操作得到类似Stream<Tuple<Type, Stream<MyThing>>>
的东西?甚至在概念上是否可以在不评估整个数据集的情况下以任何语言进行惰性分组?
我认为这没有意义,因为从惰性流 Stream<Tuple<Type, Stream<MyThing>>>
的一个分区流 (Tuple<Type, Stream<MyThing>>
) 读取可能会在其他分区中产生任意大量的消耗内存。
例如考虑按自然顺序排列的惰性正整数流,并按它们的最小质因数对它们进行分组。然后从分区流的最后一个接收到的元素读取将在之前接收到的流中产生越来越多的整数。
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream.
摘自文档:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
所以我认为没有办法在不消耗它和创建新流的情况下拆分它。
惰性分组的概念并没有真正意义。根据定义,分组意味着提前选择组,以避免为每个键搜索所有元素的开销。 "Lazy grouping" 看起来像这样:
List<MyThing> get(Type key) {
source.stream()
.filter(myThing -> myThing.type().equals(key))
.collect(toList());
}
如果您希望将迭代推迟到您知道需要它的时候,或者如果您想避免缓存分组映射的内存开销,这完全没问题。但是如果不提前迭代就无法优化选择过程。
Is it even conceptually possible to group lazily in any language without evaluating the whole data set?
不,如果不检查整个数据集或保证数据中存在可利用的模式,您就无法对整个数据集进行正确分组。例如,我可以将前 10,000 个整数懒惰地分组为奇偶数,但我不能懒惰地将 10,000 个整数的随机集合奇偶数分组。
至于以非终端方式分组...这似乎不是一个好主意。从概念上讲,流上的分组函数应该 return 多个流,就好像它在分支不同的流一样,Java 8 不支持。
如果你真的想使用原生 Stream 方法进行非终端分组,你可以滥用 sorted 方法。给它一个排序器,以不同方式对待组,但将组内的所有元素视为平等,你最终会得到 group1、group2、group3 等。这不会给你偷懒的评价,而是分组。