簡體   English   中英

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

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

我試圖通過特定的屬性(“程序”的“學生”對象和“教師”的“教授”對象)對對象的兩個不同的數組列表進行排序。 這兩個類都擴展了我的抽象'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);
    }
}

然后,當我創建一個1000000個隨機“人物”對象的數組而不是學生或教授時,我決定按照它們的名字(按字母順序)按字母順序排序(它可以正常工作)。

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

然后,我將原始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]);
        }
    }

當我嘗試按照我想要的屬性按字母順序對每個ArrayList進行排序時會出現問題,因為它會按Person的名稱對它們進行排序:

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

在這里,我留下我的學生和教授課程:

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

教授班:

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類中的“compareTo()”方法,該方法使用“program”屬性。 我還在學習使用這些方法,所以有些東西我沒有得到。

您有兩個不同的compareTo()方法。 您期望使用的那個不會被Collections.sort()調用。

如果你想使用Collections.sort()對學生進行排序,那么你需要一個帶簽名compareTo(學生)的方法;

這個方法與compareTo(Person person)“重疊”,這是兩個問題:

  • 在語義上,Person級別的compareTo()方法建立了語義 ,而Student級別的compareTo()方法偏離了那些語義,這從來都不是一個好主意。

  • 從技術上講,您依賴於與方法綁定相關的實現細節,以使您的系統按照需要運行。 這充其量是狡猾的。

我會尋找一種排序方法,它使用顯式的用戶提供的比較器,而不是依賴於內部compareTo()的排序方法。

問題

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

    它說“將它們作為第一Person進行比較;如果它們是相同的(這里,具有相同的名稱),將它們作為Student (在這里,通過學生的程序)進行比較”。

  3. 我會刪除這些方法。 對於不適合類域的簡單代碼行,不值得使用單獨的方法。

如果要使用不同的順序將對象排序為“自然”排序類,則應使用Arrays.sort(T[], Comparator<T>) ,以及實現特定排序順序或排序的Comparator對象。

Comparablejavadoc解釋了它應該實現的語義。 (仔細閱讀!)

關於自然排序:

  • Person[]的“自然”排序將由compareTo(Person)方法給出。
  • Student[] (或ArrayList<Student> )的“自然”排序將由compareTo(Student)方法給出。
  • 等等。
  • 在這些情況下,都不會使用compareTo(String)方法!

Person中的compareTo(String)方法沒有多大意義,因為它this (一個Person )與一個String進行了比較。 特別是它對由Person類實現的Comparable<Person>接口沒有貢獻。

您應該this (一個Person )與另一個Person

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

然后,在您的班級ProfessorStudent您可以使用上述方法,如下所示:

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

其實,不需要這種方法了,因為它的行為是一樣compareTo(Person)Person 您可以省略此方法,但仍具有相同的效果。

這是用Effective Java書提醒自己的好時機第40項:始終如一地使用Override。

您的基類Person不會在compareTo方法上使用@Override表示法,因此如果您實際上沒有覆蓋您認為的compareTo方法,則不會出現錯誤。 在這種情況下,參數類型是錯誤的。 它應該是Person,而不是String。 不調用該方法,而是使用默認開啟。

-我

我相信當您想要在特定屬性上對類進行排序時,您需要使用比較器。

嘗試類似的東西:

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

比較器是一個存在於集合下的函數,因此您只需插入要查找的條件即可。

如果您無法實現它,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM