问题
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:
- the map does not contain any duplicate Student
- 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