Java 集合 - 按最大值分组、合并和去重

Java Collection - Group, Combine and Dedup by Maximum

我有以下 Classes:

public class Student {  
    String studentId;
    List<Subject> subjects;
    //+Getters, setters, constructors
}

public class Subject {  
    int subjectId;
    String grade;   
    int marks;
    //+Getters, setters, constructors
}

示例输入:

Student S1 subjects=[1, A, 90], [2, B, 80], [2, C, 70]
Student S1 subjects=[2, A+, 95], [3,C,70]
Student S2, subjects=[1, D, 50]
Student S2, subjects=[1, D, 50]

示例输出:

Student S1 subjects=[1, A, 90], [2, A+, 95], [3,C,70]  //Subject 2 is selected based on highest marks
Student S2, subjects=[1, D, 50] //Avoided duplicate for same mark

我想实现合并和重复数据删除功能。 该函数应该 return 一个 StudentId 只有一次。 每个 StudentID 应该包含每个 subjectID 在该学生中只出现一次。应根据最高分选择主题 ID。

public List<Student> consolidate (List<Student> students)
    {
        List<Student> consolidatedStudents = new ArrayList<Student>();
        //???
        return consolidatedStudents;
    }

我怎样才能以最有效的方式实现这一目标?

用于测试的主要示例 Class

    public class Main {
    
    public static void main(String args[])
    {
        List<Subject> s1Subjects_1 = new ArrayList<>();
        List<Subject> s1Subjects_2 = new ArrayList<>();
        Subject s1Physics = new Subject(1,"A",90);
        Subject s1Chemistry_1 = new Subject(2,"B",80);      
        Subject s1Chemistry_2 = new Subject(2,"C",70);
        Subject s1Chemistry_3 = new Subject(2,"A+",95);
        Subject s1Biology = new Subject(3,"C",70);
        
        s1Subjects_1.add(s1Physics);
        s1Subjects_1.add(s1Chemistry_1);
        s1Subjects_1.add(s1Chemistry_2);
        s1Subjects_2.add(s1Chemistry_3);
        s1Subjects_2.add(s1Biology);
        
        List<Subject> s2Subjects_1 = new ArrayList<>();
        List<Subject> s2Subjects_2 = new ArrayList<>();
        Subject s2Physics_1 = new Subject(1,"D",50);
        Subject s2Physics_2 = new Subject(1,"D",50);
        s2Subjects_1.add(s2Physics_1);
        s2Subjects_2.add(s2Physics_2);
        
        Student s1_1 = new Student("s1", s1Subjects_1);
        Student s1_2 = new Student("s1", s1Subjects_2);
        Student s2_1 = new Student("s2", s2Subjects_1);
        Student s2_2 = new Student("s2", s2Subjects_2);
        
        List<Student> input = new ArrayList<>();
        input.add(s1_1);
        input.add(s1_2);
        input.add(s2_1);
        input.add(s1_2);        
        
        List<Student> output = consolidate(input);
    }
    
    public static List<Student> consolidate (List<Student> students)
    {
        List<Student> consolidatedStudents = new ArrayList<Student>();
        //
        //???
        //
        return consolidatedStudents;
    }

}

这是一种使用流来实现的有趣方式..

这里我使用Collectors.toMap来建立一个临时的数据结构映射 学生 ID 添加到科目列表中。我正在使用辅助合并功能合并主题列表。

合并功能根据分数选择最佳主题。

public static List<Student> consolidate(List<Student> students) {
    Map<String, List<Subject>> map = students.stream()
            .collect(Collectors.toMap(Student::getStudentId,
                    Student::getSubjects,
                    (subjects1, subjects2) -> merge(subjects1, subjects2)));
    return map.entrySet()
            .stream()
            .map(e -> new Student(e.getKey(), e.getValue()))
            .collect(Collectors.toList());
}

merge 函数为两个主题构建一个临时映射(主题 id -> 主题的映射),并使用 Map#merge 将第二个合并到第一个中。对于相同 id 的受试者,它会选择得分最高的受试者。

private static List<Subject> merge(List<Subject> subjects1, List<Subject> subjects2) {
    Map<Integer, Subject> subjects1ById = new HashMap<>(subjectsMap(subjects1));
    Map<Integer, Subject> subjects2ById = subjectsMap(subjects2);
   
    subjects2ById.forEach((subId, sub) -> subjects1ById.merge(subId, sub,
            (sub1, sub2) -> pickBest(sub1, sub2)));
    return new ArrayList<>(subjects1ById.values());
}

private static Map<Integer, Subject> subjectsMap(List<Subject> subjects) {
    return subjects.stream()
            .collect(Collectors.toMap(Subject::getSubjectId, Function.identity(),
                    (sub1, sub2) -> pickBest(sub1, sub2)));
}

private static Subject pickBest(Subject s1, Subject s2) {
    return s1.getMarks() > s2.getMarks() ? s1 : s2;
}