Home > front end >  Java-Stream - How to apply sorting while using Collector groupingBy
Java-Stream - How to apply sorting while using Collector groupingBy

Time:10-09

I'm using Collectors.groupingBy to group may objects into list. Examples are as follows:

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student(98,"man","CHEN",20);
        Student student2 = new Student(100,"man","PETER",20);
        Student student3 = new Student(100,"man","TMAC",21);
        ArrayList<Student> list = Lists.newArrayList(student1, student2, student3);

        Map<Integer, List<StudentDTO>> map = list.stream()
            .collect(Collectors.groupingBy(Student::getAge,
                Collectors.mapping(e -> new StudentDTO(e.getScore(), e.getName()),
                    Collectors.toList())
            ));

        System.out.println(map);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Student {
        private Integer score;
        private String sex;
        private String name;
        private Integer age;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class StudentDTO {
        private Integer score;
        private String name;
    }

}

Output:

{20=[Test.StudentDTO(score=98, name=CHEN),
     Test.StudentDTO(score=100, name=PETER)],
 21=[Test.StudentDTO(score=100, name=TMAC)]}

But my goal is to sort StudentDTO by score using Comparator.comparing with streams. How can I achieve this?

Desired output:

{20=[Test.StudentDTO(score=100, name=PETER),
     Test.StudentDTO(score=98, name=CHEN)],
 21=[Test.StudentDTO(score=100, name=TMAC)]}

CodePudding user response:

To sort each list of StudentDTO by score in descending order, you can use the following comparator:

Comparator.comparingInt(StudentDTO::getScore).reversed()

And to apply sorting while collecting the result, you can use collector collectingAndThen() as the downstream of groupingBy()

List<Student> students = List.of(student1, student2, student3);

Map<Integer, List<StudentDTO>> map = students.stream()
    .collect(Collectors.groupingBy(
        Student::getAge,
        Collectors.collectingAndThen(
            Collectors.mapping(e -> new StudentDTO(e.getScore(), e.getName()),
                Collectors.toList()),
            list -> { list.sort(Comparator.comparingInt(StudentDTO::getScore).reversed()); return list; }
        )
    ));

Note: writing the code against concrete implementation makes it inflexible, you might want to learn what are the benefits of using abstractions. See What does it mean to "program to an interface"?

CodePudding user response:

Try this. (I added import static java.util.stream.Collectors.*;)

Map<Integer, List<StudentDTO>> map = list.stream()
    .collect(groupingBy(Student::getAge,
        mapping(e -> new StudentDTO(e.getScore(), e.getName()),
            collectingAndThen(toList(),
                x -> x.stream()
                    .sorted(Comparator.comparing(StudentDTO::getScore).reversed())
                    .collect(toList())))));
  • Related