Java 如何对列表中的元素进行分组<;P>;到地图<;K、 列表<;V>&燃气轮机;在维持秩序的同时?

Java 如何对列表中的元素进行分组<;P>;到地图<;K、 列表<;V>&燃气轮机;在维持秩序的同时?,java,java-8,java-stream,linkedhashmap,collectors,Java,Java 8,Java Stream,Linkedhashmap,Collectors,我有一个GooglePlaceSummary对象列表,这些对象来自GooglePlacesAPI。我想收集和分组他们的谷歌地方ID,但也保留了元素的顺序。我认为有效的方法是: Map<String, List<PlaceSummary>> placesGroupedByPlaceId = places.stream() .collect(Collectors.groupingBy(

我有一个GooglePlaceSummary对象列表,这些对象来自GooglePlacesAPI。我想收集和分组他们的谷歌地方ID,但也保留了元素的顺序。我认为有效的方法是:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(PlaceSummary::getPlaceId, toList())
                  ));
Map placesGroupedByPlaceId=
places.stream()
.collect(收集器.groupingBy(
PlaceSummary::getPlaceId,
LinkedHashMap::新建,
Collectors.mapping(PlaceSummary::getPlaceId,toList())
));
但它甚至不会编译。看起来应该是这样的

以前我有这样的代码:

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId = places.stream()
            .collect(Collectors.groupingBy(PlaceSummary::getPlaceId));
Map placesGroupedByPlaceId=places.stream()
.collect(Collectors.groupingBy(PlaceSummary::getPlaceId));
但是,Streams API上的标准
.collect()
并没有保留后续
HashMap
中元素的顺序(显然,因为
HashMap
是无序的)。我希望输出是一个
LinkedHashMap
,这样映射就可以按照每个bucket的插入顺序进行排序

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

如何使用Java Stream API将列表转换为
LinkedHashMap
?有没有简洁的方法?如果它太难理解,我可能会求助于老式的Java8之前的方法


我注意到有,但这并没有我想要的解决方案,因为我需要收集“this”我特别迭代的对象。

你真的很接近你想要的:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(Function.identity(), Collectors.toList())
                  ));
然后像这样使用它:

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

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

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
          places.stream()
                .collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
                                               LinkedHashMap::new,
                                               Collectors.toList()));
Map placesGroupedByPlaceId=
places.stream()
.collect(收集器).groupingBy(PlaceSummary::getPlaceId、,
LinkedHashMap::新建,
收藏家;
/**
*我已经编写了更通用的代码,如果您愿意,可以基于任意*
*通过传递方法引用来传递实例变量、id、名称等。
**/
班级学生{
私有int-id;
私有字符串名称;
公立学生(int-id,字符串名){this.id=id;this.name=name;}
/**
*@返回id
*/
public int getId(){return id;}
/**
*@param-id
*要设置的id
*/
public void setId(int id){this.id=id;}
/**
*@返回名称
*/
公共字符串getName(){return name;}
/**
*@param name
*要设置的名称
*/
public void setName(字符串名){this.name=name;}
}
公立班学生{
公共静态void main(字符串[]args){
列表=新的ArrayList();
列表。添加(新学生(1,“Amit”);
增加(新学生(2名,“Sumit”);
增加(新学生(1名,“Ram”);
增加(新学生(2名,“Shyam”);
列表。添加(新学生(3,“Amit”);
列表。添加(新学生(4名,“Pankaj”);

Map如果您需要在保持顺序的同时进行分组并应用一个函数(减少),我可能会使用类似的方法

final Map<Integer,Long>map=stream.collect(Collectors.groupingBy(function
   ,LinkedHashMap::new
   ,Collectors.collectingAndThen(Collectors.counting(),Function.identity()))
 )
final Mapmap=stream.collect(收集器.groupingBy(函数
,LinkedHashMap::新建
,Collectors.collectingAndThen(Collectors.counting(),Function.identity())
)

从中给出的示例来看,它应该编译。可能是
Collectors.toList()
而不是
toList()
,除非您静态导入?@njzk2我不认为toList是问题所在-正如您所说,它是静态导入的。LinkedHashMap::新行在IntelliJ中有以下错误:方法引用中的返回类型错误:无法将java.util.LinkedHashMap转换为mt映射收集器应用于值,因此您需要p->p instead地点摘要::getPlaceId@AlexisC.那就是p->p.getPlaceId?那么…这是否意味着我可以用p->p来指代“this”元素?No p->p.getPlaceId意味着PlaceSummary分组的值将由它们的位置id映射。如果您想要实例本身,只需应用identity函数,因此p->p。是的,我认为这让我有点困惑。我认为最好使用参数orders键、值映射、toList()如果我是诚实的-但有点抱怨。我会记住这一点以备将来参考…谢谢。在生产代码中试用过-工作起来像做梦一样谢谢。肯定是对API的误解。此外,我更喜欢placeSummary->placeSummary而不是Function.identity()因为澄清起来似乎更清晰一些。如果您理解代码的目的,那么代码就不太复杂了。我试图做的是将重复ID分组,然后使用规则过滤掉重复的ID。其中一个规则是根据是否是扩展位置保留一个重复的ID(我们增加了位置,为他们提供谷歌API不知道的额外信息)或者不知道。非常感谢
/**
 * 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()));
    }
}
final Map<Integer,Long>map=stream.collect(Collectors.groupingBy(function
   ,LinkedHashMap::new
   ,Collectors.collectingAndThen(Collectors.counting(),Function.identity()))
 )