简体   繁体   English

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

[英]Java Collections.sort() not sorting as expected

I am trying to sort two different ArrayLists of objects by a specific atribute ('Student' objects by "program" and 'Professor' objects by "faculty"). 我试图通过特定的属性(“程序”的“学生”对象和“教师”的“教授”对象)对对象的两个不同的数组列表进行排序。 Both classes extend my abstract 'Person' class. 这两个类都扩展了我的抽象'Person'类。

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

Then, when I create an array of 1000000 random 'Person' objects than can be Students or Professors, I decide to sort it alphabetically by their name like this (which works properly). 然后,当我创建一个1000000个随机“人物”对象的数组而不是学生或教授时,我决定按照它们的名字(按字母顺序)按字母顺序排序(它可以正常工作)。

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

Then, I divide the original Person array into two ArrayLists, one for Student objects and another for Professor objects: 然后,我将原始Person数组分成两个ArrayLists,一个用于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]);
        }
    }

The problem comes when i try to sort each ArrayList alphabetically by the atribute I want, since it keeps sorting them by the name of the Person: 当我尝试按照我想要的属性按字母顺序对每个ArrayList进行排序时会出现问题,因为它会按Person的名称对它们进行排序:

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

Here I leave my Student and Professor 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());
    }
}

Professor 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());
    }
}

What am I doing wrong? 我究竟做错了什么? I thought if I call "Collections.sort()" on an ArrayList of Student objects it would use the "compareTo()" method from my Student class, which uses the "program" atribute. 我想如果我在Student对象的ArrayList上调用“Collections.sort()”,它将使用我的Student类中的“compareTo()”方法,该方法使用“program”属性。 I'm still learning to work with these methods so there is something I'm not getting. 我还在学习使用这些方法,所以有些东西我没有得到。

You have two distinct compareTo() methods. 您有两个不同的compareTo()方法。 The one you're expecting to be used does not get invoked by Collections.sort(). 您期望使用的那个不会被Collections.sort()调用。

If you want orderings on Students using Collections.sort() then you need a method with signature compareTo(Student student); 如果你想使用Collections.sort()对学生进行排序,那么你需要一个带签名compareTo(学生)的方法;

This method "overlaps" with compareTo(Person person) and that's a problem on two counts : 这个方法与compareTo(Person person)“重叠”,这是两个问题:

  • semantically, the compareTo() method at Person level establishes semantics and your compareTo() method at the Student level deviates from those semantics and that's never a good idea. 在语义上,Person级别的compareTo()方法建立了语义 ,而Student级别的compareTo()方法偏离了那些语义,这从来都不是一个好主意。

  • technically, you are relying on implementation details related to the method binding to make your system behave as desired. 从技术上讲,您依赖于与方法绑定相关的实现细节,以使您的系统按照需要运行。 That's dodgy at best. 这充其量是狡猾的。

I'd look out for a sorting method that uses an explicit user-provided comparator instead of a sorting method that relies on internal compareTo(). 我会寻找一种排序方法,它使用显式的用户提供的比较器,而不是依赖于内部compareTo()的排序方法。

The problems 问题

  1. You didn't define how Person objects should be compared. 您没有定义如何比较Person对象。
  2. You incorrectly defined how Student and Professor instances should be compared. 您错误地定义了应如何比较StudentProfessor实例。
  3. You wrote overloaded methods compareTo(String) that are misleading. 您编写了重载方法compareTo(String) ,这些方法具有误导性。

The solutions 解决方案

  1. Define Person#compareTo properly, remove its compareTo(String) : 正确定义Person#compareTo ,删除其compareTo(String)

     public int compareTo(Person p) { return getName().compareTo(p.getName()); } 
  2. Define Student#compareTo and Professor#compareTo correctly, remove their compareTo(String) . 正确定义Student#compareToProfessor#compareTo ,删除他们的compareTo(String) Here's an example of how Student#compareTo could be written: 以下是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; } 

    It says "compare them as Person s first; if they are equal (here, have the same name), compare them as Student s (here, by student's program)". 它说“将它们作为第一Person进行比较;如果它们是相同的(这里,具有相同的名称),将它们作为Student (在这里,通过学生的程序)进行比较”。

  3. I would remove these methods. 我会删除这些方法。 It isn't worth having a separate method for a simple line of code which doesn't fit the class domain. 对于不适合类域的简单代码行,不值得使用单独的方法。

If you want to sort objects using different orderings to the classes "natural" ordering, you should be using Arrays.sort(T[], Comparator<T>) , with a Comparator object that implements the specific sort ordering or orderings. 如果要使用不同的顺序将对象排序为“自然”排序类,则应使用Arrays.sort(T[], Comparator<T>) ,以及实现特定排序顺序或排序的Comparator对象。

The javadoc for Comparable explains the semantics that it should implement. Comparablejavadoc解释了它应该实现的语义。 (Read them carefully!) (仔细阅读!)

About natural ordering: 关于自然排序:

  • The "natural" ordering for a Person[] will given by the compareTo(Person) method. Person[]的“自然”排序将由compareTo(Person)方法给出。
  • The "natural" ordering for a Student[] (or ArrayList<Student> ) will given by the compareTo(Student) method. Student[] (或ArrayList<Student> )的“自然”排序将由compareTo(Student)方法给出。
  • And so on. 等等。
  • In none of these case will your compareTo(String) methods be used! 在这些情况下,都不会使用compareTo(String)方法!

Your compareTo(String) method in class Person doesn't make much sense, because it compares this (a Person ) with a String . Person中的compareTo(String)方法没有多大意义,因为它this (一个Person )与一个String进行了比较。 Especially it does not contribute to the interface Comparable<Person> implemented by class Person . 特别是它对由Person类实现的Comparable<Person>接口没有贡献。

You should rather compare this (a Person ) with another Person : 您应该this (一个Person )与另一个Person

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

Then, in your classes Professor and Student you can use the above method like this: 然后,在您的班级ProfessorStudent您可以使用上述方法,如下所示:

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

Actually, this method is not needed anymore, because its behavior is the same as the compareTo(Person) of Person . 其实,不需要这种方法了,因为它的行为是一样compareTo(Person)Person You can omit this method, and still have the same effect. 您可以省略此方法,但仍具有相同的效果。

This is a good time to remind ourselves with Effective Java book Item 40: Consistently use the Override. 这是用Effective Java书提醒自己的好时机第40项:始终如一地使用Override。

Your base class Person does not use the @Override notation on the compareTo method so you'll get no error if you're not actually overriding the compareTo method you think you are. 您的基类Person不会在compareTo方法上使用@Override表示法,因此如果您实际上没有覆盖您认为的compareTo方法,则不会出现错误。 In this case, the parameter type is wrong. 在这种情况下,参数类型是错误的。 It should be Person, not String. 它应该是Person,而不是String。 The method is not invoked and the default on is used instead. 不调用该方法,而是使用默认开启。

-me -我

I believe when you want to sort a Class over a specific attribute you need to use a Comparator. 我相信当您想要在特定属性上对类进行排序时,您需要使用比较器。

Try something like: 尝试类似的东西:

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

The comparator is a function that exists under the Collections so you just have to insert the condition that you are looking for. 比较器是一个存在于集合下的函数,因此您只需插入要查找的条件即可。

Let me know if you are not able to implement it. 如果您无法实现它,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM