我有以下代码,它对列表进行分组,然后对每个分组的列表进行操作,依次将其转换为单个项目:
Map<Integer, List<Record>> recordsGroupedById = myList.stream()
.collect(Collectors.groupingBy(r -> r.get("complex_id")));
List<Complex> whatIwant = recordsGroupedById.values().stream().map(this::toComplex)
.collect(Collectors.toList());
toComplex
函数如下所示:
Complex toComplex(List<Record> records);
我觉得我可以在不创建中间地图的情况下做到这一点,也许使用 reduce。 有什么想法吗?
输入流按我想要在流中按顺序分组的元素进行排序。 在正常的循环结构中,我将能够确定下一组何时开始并在那时创建一个"复杂"。
创建一个将 groupingBy 和后处理函数与 collectingAndThen
相结合的收集器。
Map<Integer, Complex> map = myList.stream()
.collect(collectingAndThen(groupingBy(r -> r.get("complex_id"),
Xxx::toComplex));
如果您只想在这里Collection<Complex>
,则可以向地图询问其values()
。
好吧,你可以避免Map
(老实说!)并使用我的StreamEx库在单个管道中完成所有操作:
List<Complex> result = StreamEx.of(myList)
.sortedBy(r -> r.get("complex_id"))
.groupRuns((r1, r2) -> r1.get("complex_id").equals(r2.get("complex_id")))
.map(this::toComplex)
.toList();
在这里,我们首先按complex_id
对输入进行排序,然后使用自定义中间操作groupRuns
如果应用于两个相邻元素的给定BiPredicate
返回 true,则将相邻的流元素分组到List
。然后,您有一个列表流,该列表流映射到Complex
对象流,最后收集到列表中。
实际上没有中间映射,groupRuns
实际上是懒惰的(在顺序模式下,它一次只保留一个中间List
),它也很好地并行化。另一方面,我的测试表明,对于未排序的输入,这种解决方案比基于groupingBy
的解决方案慢,因为它涉及对整个输入进行排序。当然,sortedBy
(这只是sorted(Comparator.comparing(...))
的快捷方式)需要中间内存来存储输入。如果您的输入已经排序(或至少部分排序,因此 TimSort 可以快速执行),那么这样的解决方案通常比 groupingBy
更快。
不,你不能。在向前移动之前,必须收集所有数据以确保所有组的内容都是已知的。但是,显然,如果您可以在分配给组时对组中的每个元素执行流程,则可以做到这一点。
这样想 - 想象一下列表中的第一项和列表中的最后一项包含相同的complex_id
。然后,您必须等待列表的末尾才能完全收集该组(以及所有其他组),因此您必须在处理之前将所有组聚集在一起。
另外 - 你显然应该能够做到:
List<Complex> whatIwant = myList.stream()
.collect(Collectors.groupingBy(r -> r.get("complex_id")))
.values()
.stream()
.map(this::toComplex)
.collect(Collectors.toList());