18

我有一个从 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())
                  ));

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

以前我有这个代码:

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

然而.collect(),Streams API 的标准不保留后续元素的顺序HashMap(显然因为HashMaps 是无序的)。我希望输出是 aLinkedHashMap以便 Map 按每个桶的插入顺序排序。

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

如何LinkedHashMap使用 Java Stream API 将 List 转换为 a?有没有简洁的方法来做到这一点?如果太难理解,我可能会求助于 Java 8 之前的老式方法。

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

4

4 回答 4

21

你真的很接近你想要的:

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

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

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

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


您可以将其编写为通用方法:

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);
于 2015-10-15T13:50:37.467 回答
4

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

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
          places.stream()
                .collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
                                               LinkedHashMap::new,
                                               Collectors.toList()));
于 2015-10-15T14:01:32.880 回答
0

如果您在维护订单时需要分组并应用功能(减少),也许我会使用这样的东西。

final Map<Integer,Long>map=stream.collect(Collectors.groupingBy(function
   ,LinkedHashMap::new
   ,Collectors.collectingAndThen(Collectors.counting(),Function.identity()))
 )
于 2019-02-26T19:44:20.280 回答
0
/**
 * I have written this code more generic, if you want then you can group based on any * 
 * instance variable , id, name etc via passing method reference.
**/

class Student {
    private int id;
    private String name;
    public Student(int id, String name) {this.id = id;this.name = name;}
    /**
     * @return the id
     */
    public int getId() {return id;}
    /**
     * @param id
     *            the id to set
     */
    public void setId(int id) {this.id = id;}
    /**
     * @return the name
     */
    public String getName() {return name;}
    /**
     * @param name
     *            the name to set
     */
    public void setName(String name) {this.name = name;}
}

public class StudentMain {

    public static void main(String[] args) {

        List<Student> list = new ArrayList<>();
        list.add(new Student(1, "Amit"));
        list.add(new Student(2, "Sumit"));
        list.add(new Student(1, "Ram"));
        list.add(new Student(2, "Shyam"));
        list.add(new Student(3, "Amit"));
        list.add(new Student(4, "Pankaj"));

        Map<?, List<Student>> studentById = groupByStudentId(list,
                Student::getId);
        System.out.println(studentById);

       Map<?, List<Student>> studentByName = groupByStudentId(list,
                Student::getName);
        System.out.println(studentByName);

    }

    private static <K, V> Map<?, List<V>> groupByStudentId(List<V> list,
            Function<V, K> keyFunction) {
        return list.stream().collect(
                Collectors.groupingBy(keyFunction, HashMap::new,
                        Collectors.toList()));
    }
}
于 2018-09-05T07:42:48.453 回答