Transform a List<Object> to a Map<String, Integer> such that the String is not a duplicate value using Java 8 Streams

坚强是说给别人听的谎言 提交于 2021-02-16 13:03:10

问题


We have a Student class as follows:

class Student {
    private int marks;
    private String studentName;

    public int getMarks() {
        return marks;
    }

    public void setMarks(int marks) {
        this.marks = marks;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public Student(String studentName, int marks) {
        this.marks = marks;
        this.studentName = studentName;
    }
}

We have a LIST of Students as follows :

List<Student> studentList = new ArrayList<>();
studentList.add(new Student("abc", 30));
studentList.add(new Student("Abc", 32));
studentList.add(new Student("ABC", 35));
studentList.add(new Student("DEF", 40));

This List needs to be converted into a HashMap<String, Integer> such that:

  1. the map does not contain any duplicate Student
  2. if a duplicate student name is found, his marks shall be added with the previous occurrence.

So the output should be:

{ABC = 97, DEF = 40}

I have tried to solve this issue as follows:

Map<String, Integer> studentMap = studentList.stream()
        .collect(Collectors.toMap(
                student -> student.getStudentName().toLowerCase(),
                student -> student.getMarks(),
                (s1, s2) -> s1,
                LinkedHashMap::new));

But the merge function does not allow me to concatenate the marks and this returns the output as:

{abc = 30, DEF = 40}

Can someone suggest an efficient solution for this?


回答1:


That's because of an incorrect merge function, you should instead use:

Map<String, Integer> map = studentList.stream()
        .collect(Collectors.toMap(
                student -> student.getStudentName().toLowerCase(),
                Student::getMarks,
                (s1, s2) -> s1 + s2, // add values when merging
                LinkedHashMap::new));



回答2:


An alternative solution is to use groupingBy with summingInt:

Map<String, Integer> studentMap = studentList.stream()
        .collect(Collectors.groupingBy(
                s -> s.getStudentName().toLowerCase(),
                Collectors.summingInt(Student::getMarks)));



回答3:


Your merge function is incorrect. It could be either (s1, s2) -> s1 + s2 or just Integer::sum if you'd rather use a method reference.

Another way to do it is without streams:

Map<String, Integer> studentMap = new LinkedHashMap<>();
studentList.forEach(s -> studentMap.merge(
                         s.getStudentName().toLowerCase(),
                         s.getMarks(),
                         Integer::sum));

This iterates the list of students and uses the Map.merge method to group them by name, summing their marks.




回答4:


You can use Collectors.groupingBy(classifier,mapFactory,downstream) method for this purpose:

List<Student> list = List.of(
        new Student("abc", 30),
        new Student("Abc", 32),
        new Student("ABC", 35),
        new Student("DEF", 40));

Map<String, Integer> map = list.stream()
        .collect(Collectors.groupingBy(
                Student::getStudentName,
                () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER),
                Collectors.summingInt(Student::getMarks)));

System.out.println(map); // {abc=97, DEF=40}


来源:https://stackoverflow.com/questions/54274392/transform-a-listobject-to-a-mapstring-integer-such-that-the-string-is-not-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!