7

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?

0

4 Answers 4

7

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));
Sign up to request clarification or add additional context in comments.

Comments

5

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)));

Comments

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.

Comments

0

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}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.