如何在保持顺序的同时将 List<P> 中的元素分组到 Map<K、List<V>>?

我有一个从Google Places API获取的Google PlaceSummary对象列表。我想按 Google Place ID 收集和分组它们,同时还要保留元素的顺序。我认为可行的是:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(PlaceSummary::getPlaceId, toList())
                  ));

但它甚至不会编译。看起来它应该根据收集器上的Java API文档

以前我有这个代码:

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId = places.stream()
            .collect(Collectors.groupingBy(PlaceSummary::getPlaceId));

但是,Streams API 上的标准不会保留后续元素的顺序(显然,因为 s 是无序的)。我希望输出是 a,以便 Map 按每个存储桶的插入顺序排序。.collect()HashMapHashMapLinkedHashMap

但是,我建议的解决方案无法编译。首先,它不识别,因为它说它不是一个函数 - 即使我知道它是。其次,它说我不能转换为M.M应该是一个通用集合,所以它应该被接受。PlaceSummary::getPlaceIdLinkedHashMap<Object, Object>

如何将列表转换为使用 Java 流 API 的列表?有没有一种简洁的方法可以做到这一点?如果太难理解,我可能会求助于旧的Java 8之前的方法。LinkedHashMap

我注意到在将List转换为LinkedHashMap时还有另一个Stack Overflow答案,但是这没有我想要的解决方案,因为我需要收集“this”我专门迭代的对象。


答案 1

你真的接近你想要的:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(Function.identity(), Collectors.toList())
                  ));

在该方法中,您需要提供实例而不是地点 ID。在上面的代码中,我使用了:这个收集器用于构建值,因此我们需要累积位置本身(而不是它们的ID)。Collectors.mappingPlaceSummaryFunction.identity()

请注意,可以直接写入而不是 。Collectors.toList()Collectors.mapping(Function.identity(), Collectors.toList())

到目前为止,您拥有的代码尚未编译,因为它实际上正在创建一个:您正在为每个ID累积ID(这很奇怪)。Map<String, List<String>>


你可以把它写成一个通用的方法:

private static <K, V> Map<K, List<V>> groupByOrdered(List<V> list, Function<V, K> keyFunction) {
    return list.stream()
                .collect(Collectors.groupingBy(
                    keyFunction,
                    LinkedHashMap::new,
                    Collectors.toList()
                ));
}

并像这样使用它:

Map<String, List<PlaceSummary>> placesGroupedById = groupByOrdered(places, PlaceSummary::getPlaceId);

答案 2

我想你对最终的收藏家有点困惑。它仅表示每个地图值中需要包含的内容。不需要有辅助收集器,因为您只需要原始对象的列表。mapping

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
          places.stream()
                .collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
                                               LinkedHashMap::new,
                                               Collectors.toList()));

推荐