通过 java 中的两个属性键将(最小)对象保留在集合中

Keep (minim) object in a collection by two attribute keys in java

例如我有一个包含 3 个对象的列表:

List<Student> studentList= new ArrayList<Student>();
list.add(new Student("name1", 5);
list.add(new Student("name3", 6);
list.add(new Student("name1", 7);

class Student{ String name; Integer grade;}

我的过滤逻辑:如果名称相等,那么我需要过滤掉具有最高等级的对象 - 所以保留最小等级。

我也不知道集合中有重复的名字。

我坚持这个实现:

Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student st1, Student st2) {
                int compareName= st1.getName().compareTo(st2.getName());
                if (compareName== 0){
                    int compareGrade = st1.getGrade().compareTo(st2.getGrade());
                  // ?

                }
                return compareName;
            }
        });
        setStudents.addAll(studentList);

预期输出:

列表

Student("name1", 5)
Student("name3", 6);

谢谢

我会将成绩封装在一个单独的 class 中。另外,我不会使用列表,对于您的用例来说,这是一种低效的存储机制。我会这样做:

public class Grades {

    private Map<String, Integer> store = new HashMap<>();

    public void storeMinimumGrade(Student student) {
        Integer grade = store.get(student.getName());
        if(grade == null || student.getGrade() < grade) {
            grade.put(name, grade);
        }
    }
}

以上对你有用吗?

试试这个代码:

List<Student> studentList= new ArrayList<Student>();
studentList.add(new Student("name1", 5));
studentList.add(new Student("name3", 6));
studentList.add(new Student("name1", 7));

Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
    @Override
    public int compare(Student st1, Student st2) {
        int compareName= st1.name.compareTo(st2.name);
        if (compareName== 0){
            int compareGrade = (st1.grade).compareTo(st2.grade);
            if(compareGrade==-1){
             // compare two student current minimum grade is replaced with old minimum value 
                st2.grade=st1.grade;
            }
           // can not add new student to set, 
                return 0;

        }
        return compareName;
    }
});

setStudents.addAll(studentList);

检查

for (Student element : setStudents) {
        System.out.println(element.name);
    }

如果你使用Java 8,你可以编写自己的收集器。像这样:

public class MyCollector implements Collector<Student, Map<String, Student>, Set<Student>> {
    @Override
    public Supplier<Map<String, Student>> supplier() {
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<String, Student>, Student> accumulator() {
        return (nameStudentMap, student) -> nameStudentMap.merge(
                student.name, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
        );
    }

    @Override
    public BinaryOperator<Map<String, Student>> combiner() {
        return (nameStudentMap, nameStudentMap2) -> {
            nameStudentMap2.forEach(
                    (s, student) -> nameStudentMap.merge(
                            s, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
                    )
            );
            return nameStudentMap;
        };
    }

    @Override
    public Function<Map<String, Student>, Set<Student>> finisher() {
        return nameStudentMap -> new HashSet<>(nameStudentMap.values());
    }

    @Override
    public Set<Characteristics> characteristics() {
        return EnumSet.of(Collector.Characteristics.UNORDERED);
    }
}

然后,你可以把这个收集器送给一群学生:

List<Student> studentList = new ArrayList<>();
studentList.add(new Student("mike", 5));
studentList.add(new Student("mike", 1));
studentList.add(new Student("jenn", 8));
studentList.add(new Student("jenn", 2));
studentList.add(new Student("alex", 2));
studentList.add(new Student("mike", 3));
Set<Student> res = studentList.stream().collect(new MyCollector());

检查:

res.forEach((Student s) -> {
    System.out.println(s.name + " " + s.grade);
});

输出:

jenn 2
mike 1
alex 2

此解决方案比 TreeSet 更快,因为它使用 HashSet 而不是 TreeSet,它完全解决了您的问题。

这是你的学生 class :

public class Student {

    private String name;
    private int grade;

    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public int getGrade() {
        return grade;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        Student external = (Student) obj;
        if (external.getGrade() > this.getGrade()) {
            external.setGrade(this.getGrade());
            return true;
        } else {
            return true;
        }

    }
}

这是你的测试:

public class 启动器 {

    public static void main (String ... args){
        Set<Student> stSet= new HashSet<Student>();
        stSet.add(new Student("name1", 7));
        stSet.add(new Student("name3", 6));
        stSet.add(new Student("name1", 5));
        stSet.add(new Student("name1", 5));
        stSet.add(new Student("name1", 1));
        stSet.add(new Student("name1", 10));
        stSet.add(new Student("name1", 2));
        stSet.add(new Student("name1", 9));
        stSet.add(new Student("name1", 4));
    }
}

输出为1,因为在String"name1"中,最小值为1;

如果你想先创建列表然后获取那个集合,这也是可以的:

public class Launcher {

    public static void main (String ... args){

        List<Student> stList = new ArrayList<Student>();
        stList.add(new Student("name1", 7));
        stList.add(new Student("name3", 6));
        stList.add(new Student("name1", 5));
        stList.add(new Student("name1", 5));
        stList.add(new Student("name1", 1));
        stList.add(new Student("name1", 10));
        stList.add(new Student("name1", 2));
        stList.add(new Student("name1", 9));
        stList.add(new Student("name1", 4));
        Set<Student> stSet= new HashSet<Student>(stList);
    }
}

这会给你相同的结果!

如果您被允许使用 Java 8,您可以流式传输数据,按名称分组并使用下游 minBy 收集器。

public void test() {
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student("name1", 5));
    studentList.add(new Student("name3", 6));
    studentList.add(new Student("name1", 7));
    // Gather min grade of all students.
    Map<String, Optional<Student>> minGrade = studentList.stream()
            // Group by student name.
            .collect(Collectors.groupingBy(Student::getName,
                            // Get minimum grade.
                            Collectors.minBy((s1, s2) -> s1.getGrade().compareTo(s2.getGrade())))
            );
    System.out.println(minGrade);
}

static class Student {

    final String name;
    final Integer grade;

    public String getName() {
        return name;
    }

    public Integer getGrade() {
        return grade;
    }

    private Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{" + "name=" + name + ", grade=" + grade + '}';
    }

}