分组后流不保留订单

时间:2017-08-07 23:56:47

标签: java java-stream

我有一个列表名称 availableSeats 我正在按 blockIndex 属性进行排序和分组,如下所示:

availableSeats.stream()
                .sorted(Comparator.comparing(SeatedTicketAssignment::getBlockIndex))
                .collect(Collectors.groupingBy(SeatedTicketAssignment::getBlockIndex))
                .forEach((block, blockAssignments) -> {
                     //Rest of the code
                } 

问题是分组的结果没有按blockIndex排序。

3 个答案:

答案 0 :(得分:8)

请注意,Collectors#groupingBy(Function)将返回HashMap,但不保证订单。如果您希望订购符合聚合身份(您的i % 2 == 0结果)显示的顺序,那么您可以使用LinkedHashMap

.collect(Collectors.groupingBy(i -> i % 2 == 0, LinkedHashMap::new, Collectors.toList()))

将返回LinkedHashMap<Boolean, List<SeatedTicketAssignment>>(因为您的收集器按布尔值分组)。此外,由于收集器使用的列表是ArrayList,因此它应该保留流相对于列表的迭代顺序。

答案 1 :(得分:1)

不幸的是,Stream API实现并没有意识到这样一个事实,即您传递的流已按您所需的顺序排序,因此&#34;分组&#34;实际上是微不足道的。因此,它使用与此SO answer基本类似的默认方式,即为组创建Map并使用流的元素填充它。默认情况下,使用的Map实现是HashMap(请参阅code here),这有助于提高性能,但不利于您的目标,因为HashMap不会保留键的顺序,而不是第一次排序。

Group By在Stream API中实现仅作为&#34;收集器&#34;似乎有点不吉利。因此,您不能先进行分组,然后进行单行分类。但这似乎是故意的:如果没有完全实现结果,就没有办法实现Group By,因此它不会是懒惰的,因此必须是收集器。 @Rogue为LinkedHashMap提供了一个很好的技巧,但对我来说它是绑定到实现细节。我仍然会写几行代码和第一组,然后按键对列表的条目(即实际分组的HashMap)进行排序。最有可能的是它会更快。

答案 2 :(得分:1)

由于groupingBy收集器不需要排序输入,因此您可以在收集后对组进行排序。这将比首先对项目排序更快,无论如何,假设组中的项目少于项目:

availableSeats.stream()
        .collect(Collectors.groupingBy(SeatedTicketAssignment::getBlockIndex))
        .entrySet().stream()
        .sorted(Comparator.comparing(Map.Entry::getKey))
        .forEach(mapEntry -> {
             //Rest of the code
        }