2017-08-07 45 views
1

后不保留顺序我有一个列表名称availableSeats我整理并通过blockIndex属性分组如下图所示:流分组

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

的问题是,分组由结果未按blockIndex排序。

回答

4

请记住,Collectors#groupingBy(Function)将返回一个HashMap,这并不能保证秩序。如果你想订购是在为了总的身份(您i % 2 == 0结果)显示出来,那么你可以使用LinkedHashMap

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

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

+0

如果密钥类型'Boolean',您可以用'partitioningBy',并得到一个地图有固有的顺序但是,在问题中哪里出现了关键功能?由于顺序是以前的排序顺序(通过相同的值),删除排序操作并收集到一个'TreeMap'会简单得多... – Holger

+0

Thaks节省我的时间。 – Sviatlana

1

不幸的是流API的实现是不知道的事实,你通过流已经按您需要什么,所以“分组”其实是微不足道的排序。因此,它使用与此SO answer基本类似的默认方式,即为组创建一个映射并将其填充到流的元素。默认情况下,使用的Map实现是HashMap(请参阅code here),这对性能有好处,但对目标不利,因为HashMap不保留键的顺序而不是第一次排序。

它可能似乎有点不走运,集团通过在流API实现只为“收集”,所以你不能第一组,然后排序在一个班轮。但这似乎是有意为之:如果没有完全实现结果,没有办法实现Group By,因此它不能懒惰,因​​此必须成为收集者。 @Rogue为LinkedHashMap提供了一个很好的技巧,但对我来说,它是绑定到实现细节的。仍然我会写更多行代码和第一组,然后通过键对列表中的条目进行排序(即实际分组的HashMap)。最有可能它会更快。

+0

为什么在分组后进行排序? 'Collectors.groupingBy(SeatedTicketAssignment :: getBlockIndex,TreeMap的::新,Collectors.toList())',如果这不是一个一行,我不知道... – Holger

+0

@Holger,这真的取决于数据。问题是''TreeMap'与其他任何排序一样''O(N * log(N))'对'HashMap'的'O(N)'具有复杂性。如果分组仅仅减少3个元素中的2个元素的数量,那么'TreeMap'可能会赢,但如果我们像'sqrt(N)'分组那样分组,那么首先分组然后排序会更快。另一点是,恕我直言,这是'Rogue'答案依赖于实施细节太多,这是不好的(例如:你保证,如果稍后有人将代码更改为并行流,顺序将始终保留)。因此我更喜欢明确的代码。 – SergGr

+0

盗贼的答案不依赖于实现细节。无论您是否使用并行流,都可以保证遇到顺序。但是他的回答使用'LinkedHashMap',仍然依赖于前面的排序操作,它不仅在最坏情况下有'O(n log n)',而且在收集之前还需要一个临时的'O(n)'存储操作。加上实际的“收集”操作。因此直接在没有先前的排序操作的情况下直接收集到“TreeMap”中可能会更快。 – Holger

1

由于groupingBy收集不需要排序输入,可以收集后的组进行排序。这将是比第一分拣物品,反正快,假设有组比较少的项目:

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

您也可以使用['Map.Entry.comparingByKey()'](https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByKey--)... – Holger