Java Collections.sort() 未按预期排序

Java Collections.sort() not sorting as expected

我正在尝试按特定属性对两个不同的对象 ArrayList 进行排序('Student' 个对象按 "program" 排序,'Professor' 个对象按 "faculty" 排序)。两个 classes 都扩展了我的摘要 'Person' class.

public abstract class Person implements Comparable<Person>{
    private String name;
    private String adress;

    //getters, setters, etc., all works properly

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); 
    }

    public int compareTo(String string) {
        return name.compareTo(string);
    }
}

然后,当我创建一个包含 1000000 个随机 'Person' 对象而不是学生或教授的数组时,我决定按他们的姓名字母顺序对其进行排序(这样工作正常)。

Person personByName[] = arrayPersonas.clone();
Arrays.sort(personByName);

然后,我将原来的 Person 数组分成两个 ArrayList,一个用于 Student 对象,另一个用于 Professor 对象:

    ArrayList<Student> studentsByProgram = new ArrayList();
    ArrayList<Professor> professorsByFaculty = new ArrayList();
    for (int i = 0; i < 1000000; i++) { 
        if (arrayPersonas[i] instanceof Student) {
            studentsByProgram.add((Student)arrayPersonas[i]);
        } else {
            professorsByFaculty.add((Professor)arrayPersonas[i]);
        }
    }

当我尝试按我想要的属性按字母顺序对每个 ArrayList 进行排序时,问题就来了,因为它一直按人名对它们进行排序:

Collections.sort(studentsByProgram);
Collections.sort(professorsByFaculty);

在这里我离开我的学生和教授 classes:

public class Student extends Person {
    private String program;
    private int year;
    private double fee;

    //constructor, setters, getters, toString, equals

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); 
    }



    public int compareTo(String string) {
        return program.compareTo(string); 
    }

    @Override
    public int compareTo(Person t) {
        return super.compareTo(t.getName());
    }
}

教授class:

public class Professor extends Person {
    private String faculty;
    private double salary;

    //constructor, setters, getters, toString, equals

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); 
    }


    public int compareTo(String string) {
        return faculty.compareTo(string); 
    }

    @Override
    public int compareTo(Person t) {
        return super.compareTo(t.getName());
    }
}

我做错了什么?我想如果我在 Student 对象的 ArrayList 上调用 "Collections.sort()",它将使用我的 Student class 中的 "compareTo()" 方法,该方法使用 "program" 属性。我仍在学习使用这些方法,所以有些东西我没有得到。

问题

  1. 您没有定义应该如何比较 Person 个对象。
  2. 您错误地定义了 StudentProfessor 实例的比较方式。
  3. 您编写的重载方法 compareTo(String) 具有误导性。

解决方案

  1. 正确定义Person#compareTo,移除其compareTo(String):

    public int compareTo(Person p) {
        return getName().compareTo(p.getName());
    }
    
  2. 正确定义 Student#compareToProfessor#compareTo,删除它们的 compareTo(String)。下面是如何编写 Student#compareTo 的示例:

    @Override
    public int compareTo(Person t) {
        final int personComparisonResult = super.compareTo(t);
    
        if (personComparisonResult == 0) {
            return program.compareTo(((Student) t).program);
        }
    
        return personComparisonResult;
    }
    

    它说 "compare them as Persons first; if they are equal (here, have the same name), compare them as Students (here, by student's program)".

  3. 我会删除这些方法。对于不适合 class 域的简单代码行,不值得使用单独的方法。

如果您想使用与 类 "natural" 排序不同的顺序对对象进行排序,您应该使用 Arrays.sort(T[], Comparator<T>),以及实现特定的 Comparator 对象排序排序或排序。

Comparablejavadoc 解释了它应该实现的语义。 (仔细阅读!)

关于自然排序:

  • Person[] 的 "natural" 排序将由 compareTo(Person) 方法给出。
  • Student[](或ArrayList<Student>)的"natural"排序将由compareTo(Student)方法给出。
  • 等等。
  • 在 none 这些情况下,将使用您的 compareTo(String) 方法!

您有两个不同的 compareTo() 方法。 Collections.sort().

未调用您期望使用的那个

如果您想使用 Collections.sort() 对 Students 进行排序,那么您需要一个带有签名 compareTo(Student student);

的方法

此方法 "overlaps" 与 compareTo(Person person) 有两个问题:

  • 在语义上,Person 级别的 compareTo() 方法建立了 语义 和您的 compareTo() 方法在学生水平偏离了那些语义,这绝不是一个好主意。

  • 从技术上讲,您依赖与方法绑定相关的实现细节来使您的系统按预期运行。这充其量是狡猾的。

我会寻找一种使用显式用户提供的比较器的排序方法,而不是依赖于内部 compareTo() 的排序方法。

你在classPerson中的compareTo(String)方法没有多大意义, 因为它将 thisPerson)与 String 进行比较。 特别是它 对由 class Person.

实现的接口 Comparable<Person> 有贡献

您应该将 this(一个 Person)与另一个 Person:

进行比较
@Override
public int compareTo(Person otherPerson) {
    return name.compareTo(otherPerson.name);
}

然后,在你的 classes ProfessorStudent 你可以像这样使用上面的方法:

@Override
public int compareTo(Person otherPerson) {
    return super.compareTo(otherPerson);
}

实际上,不再需要此方法,因为它的行为与PersoncompareTo(Person) 相同。 这个方法可以省略,效果一样。

现在是提醒自己 有效 Java 书第 40 条:始终如一地使用覆盖的好时机。

你的基础 class Person 没有在 compareTo 方法上使用 @Override 符号,所以你不会得到如果您实际上没有覆盖您认为的 compareTo 方法,则会出错。在这种情况下,参数类型是错误的。它应该是 Person,而不是 String。该方法未被调用,而是使用默认值。

-我

我相信当您想对 Class 对特定属性进行排序时,您需要使用比较器。

试试这样的东西:

static final Comparator<Student> compareProgram = new Comparator<Student>() {
        public int compare(Student e1, Student e2) {
            //condition ( you need to return the condition)
            return e2.program().compareTo(e1.program());

        }
};

// Employee database
static final Collection<Student> students = ... ;

public static void main(String[] args) {
    List<Student> e = new ArrayList<Student>(students);
    Collections.sort(e, compareProgram);
    System.out.println(e);
}

比较器是一个存在于集合下的函数,因此您只需插入要查找的条件即可。

如果您无法实施,请告诉我。