Java 物件排序詳解
本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!
很難想象有Java開發人員不曾使用過Collection框架。在Collection框架中,主要使用的類是來自List介面中的ArrayList,以及來自Set介面的HashSet、TreeSet,我們經常處理這些Collections的排序。
在本文中,我將主要關注排序Collection的ArrayList、HashSet、TreeSet,以及最後但並非最不重要的陣列。
讓我們看看如何對給定的整數集合(5,10,0,-1)進行排序:
資料(整數)儲存在ArrayList中
private void sortNumbersInArrayList() { List<Integer> integers = new ArrayList<>(); integers.add(5); integers.add(10); integers.add(0); integers.add(-1); System.out.println("Original list: " +integers); Collections.sort(integers); System.out.println("Sorted list: "+integers); Collections.sort(integers, Collections.reverseOrder()); System.out.println("Reversed List: " +integers); }
輸出:
Original list: [5, 10, 0, -1] Sorted list: [-1, 0, 5, 10] Reversed List: [10, 5, 0, -1]
資料(整數)儲存在HashSet中
private void sortNumbersInHashSet() { Set<Integer> integers = new HashSet<>(); integers.add(5); integers.add(10); integers.add(0); integers.add(-1); System.out.println("Original set: " +integers); // Collections.sort(integers); This throws error since sort method accepts list not collection List list = new ArrayList(integers); Collections.sort(list); System.out.println("Sorted set: "+list); Collections.sort(list, Collections.reverseOrder()); System.out.println("Reversed set: " +list); }
輸出:
Original set: [0, -1, 5, 10] Sorted set: [-1, 0, 5, 10] Reversed set: [10, 5, 0, -1]
在這個例子中(資料(整數)儲存在HashSet中),我們看到HashSet被轉換為ArrayList進行排序。在不轉換為ArrayList的情況下,可以通過使用TreeSet來實現排序。TreeSet是Set的另一個實現,並且在使用預設建構函式建立Set時,使用自然排序進行排序。
資料(整數)儲存在TreeSet中
private void sortNumbersInTreeSet() { Set<Integer> integers = new TreeSet<>(); integers.add(5); integers.add(10); integers.add(0); integers.add(-1); System.out.println("Original set: " + integers); System.out.println("Sorted set: "+ integers); Set<Integer> reversedIntegers = new TreeSet(Collections.reverseOrder()); reversedIntegers.add(5); reversedIntegers.add(10); reversedIntegers.add(0); reversedIntegers.add(-1); System.out.println("Reversed set: " + reversedIntegers); }
輸出:
Original set: [-1, 0, 5, 10] Sorted set: [-1, 0, 5, 10] Reversed set: [10, 5, 0, -1]
在這種情況下,“Original set:”和“Sorted set:”兩者相同,因為我們已經使用了按排序順序儲存資料的TreeSet,所以在插入後不用排序。
到目前為止,一切都如期工作,排序似乎是一件輕而易舉的事。現在讓我們嘗試在各個Collection中儲存自定義物件(比如Student),並檢視排序是如何工作的。
資料(Student物件)儲存在ArrayList中
private void sortStudentInArrayList() { List<Student> students = new ArrayList<>(); Student student1 = createStudent("Biplab", 3); students.add(student1); Student student2 = createStudent("John", 1); students.add(student2); Student student3 = createStudent("Pal", 5); students.add(student3); Student student4 = createStudent("Biplab", 2); students.add(student4); System.out.println("Original students list: " + students); Collections.sort(students);// Error here System.out.println("Sorted students list: " + students); Collections.sort(students, Collections.reverseOrder()); System.out.println("Reversed students list: " + students); } private Student createStudent(String name, int no) { Student student = new Student(); student.setName(name); student.setNo(no); return student; } public class Student { String name; int no; public String getName() { return name; } public int getNo() { return no; } public void setName(String name) { this.name = name; } public void setNo(int no) { this.no = no; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", no=" + no + '}'; } }
這會丟擲編譯時錯誤,並顯示以下錯誤訊息:
sort(java.util.List<T>) in Collections cannot be applied to (java.util.List<com.example.demo.dto.Student>) reason: no instance(s) of type variable(s) T exist so that Student conforms to Comparable<? Super T>
為了解決這個問題,要麼Student類需要實現Comparable,要麼需要在呼叫Collections.sort時傳遞Comparator物件。在整型情況下,排序方法沒有錯誤,因為Integer類實現了Comparable。讓我們看看,實現Comparable或傳遞Comparator如何解決這個問題,以及排序方法如何實現Collection排序。
使用Comparable排序
package com.example.demo.dto; public class Student implements Comparable{ String name; int no; public String getName() { return name; } public int getNo() { return no; } public void setName(String name) { this.name = name; } public void setNo(int no) { this.no = no; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", no=" + no + '}'; } @Override public int compareTo(Object o) { return this.getName().compareTo(((Student) o).getName()); } }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}] Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]
在所有示例中,為了顛倒順序,我們使用“Collections.sort(students, Collections.reverseOrder()”,相反的,它可以通過改變compareTo(..)方法的實現而達成目標,且compareTo(…) 的實現看起來像這樣 :
@Override public int compareTo(Object o) { return (((Student) o).getName()).compareTo(this.getName()); }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}] Reversed students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]
如果我們觀察輸出結果,我們可以看到“Sorted students list:”以顛倒的順序(按學生name)輸出學生資訊。
到目前為止,對學生的排序是根據學生的“name”而非“no”來完成的。如果我們想按“no”排序,我們只需要更改Student類的compareTo(Object o)實現,如下所示:
@Override public int compareTo(Object o) { return (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1)); }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='John', no=1}, Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='Pal', no=5}] Reversed students list: [Student{name='Pal', no=5}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}]
在上面的輸出中,我們可以看到“no”2和3的兩名學生具有相同的名字“Biplab”。
現在假設我們首先需要按“name”對這些學生進行排序,如果超過1名學生具有相同姓名的話,則這些學生需要按“no”排序。為了實現這一點,我們需要改變compareTo(…)方法的實現,如下所示:
@Override public int compareTo(Object o) { int result = this.getName().compareTo(((Student) o).getName()); if(result == 0) { result = (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1)); } return result; }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}] Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]
使用Comparator排序
為了按照“name”對Students進行排序,我們將新增一個Comparator並將其傳遞給排序方法:
public class Sorting { private void sortStudentInArrayList() { List<Student> students = new ArrayList<>(); Student student1 = createStudent("Biplab", 3); students.add(student1); Student student2 = createStudent("John", 1); students.add(student2); Student student3 = createStudent("Pal", 5); students.add(student3); Student student4 = createStudent("Biplab", 2); students.add(student4); System.out.println("Original students list: " + students); Collections.sort(integers, new NameComparator()); System.out.println("Sorted students list: " + students); } } public class Student { String name; int no; public String getName() { return name; } public int getNo() { return no; } public void setName(String name) { this.name = name; } public void setNo(int no) { this.no = no; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", no=" + no + '}'; } } class NameComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]
同樣,如果我們想按照“no”對Students排序,那麼可以再新增一個Comparator(NoComparator.java),並將其傳遞給排序方法,然後資料將按“no”排序。
現在,如果我們想通過“name”然後“no”對學生進行排序,那麼可以在compare(…)內結合兩種邏輯來實現。
class NameNoComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { int result = o1.getName().compareTo(o2.getName()); if(result == 0) { result = o1.getNo() < o2.getNo() ? -1 : o1.getNo() == o2.getNo() ? 0 : 1; } return result; } }
輸出:
Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}] Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}]
資料(Students物件)儲存在Set中
在Set的這個情況下,我們需要將HashSet轉換為ArrayList,或使用TreeSet對資料進行排序。此外,我們知道要使Set工作,equals(…)和hashCode()方法需要被覆蓋。下面是基於“no”欄位覆蓋equals和hashcode的例子,且這些是IDE自動生成的程式碼。與Comparable或Comparator相關的其他程式碼與ArrayList相同。
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return no == student.no; } @Override public int hashCode() { return Objects.hash(no); }
資料(Students物件)儲存在陣列中
為了對陣列排序,我們需要做與排序ArrayList相同的事情(要麼執行Comparable要麼傳遞Comparable給sort方法)。在這種情況下,sort方法是“Arrays.sort(Object[] a )”而非“Collections.sort(..)”。
private void sortStudentInArray() { Student [] students = new Student[4]; Student student1 = createStudent("Biplab", 3); students[0] = student1; Student student2 = createStudent("John", 1); students[1] = student2; Student student3 = createStudent("Pal", 5); students[2] = student3; Student student4 = createStudent("Biplab", 2); students[3] = student4; System.out.print("Original students list: "); for (Student student: students) { System.out.print( student + " ,"); } Arrays.sort(students); System.out.print("\nSorted students list: "); for (Student student: students) { System.out.print( student +" ,"); } Arrays.sort(students, Collections.reverseOrder()); System.out.print("\nReversed students list: " ); for (Student student: students) { System.out.print( student +" ,"); } } //Student class // All properties goes here @Override public int compareTo(Object o) { int result =this.getName().compareTo(((Student)o).getName()); if(result ==0) { result = (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1)); } return result; }
輸出:
Original students list: Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} ,Student{name='Biplab', no=2} , Sorted students list: Student{name='Biplab', no=2} ,Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} , Reversed students list: Student{name='Pal', no=5} ,Student{name='John', no=1} ,Student{name='Biplab', no=3} ,Student{name='Biplab', no=2} ,
結論
我們經常對Comparable/Comparator的使用以及何時使用哪個感到困惑。下面是我總結的Comparable/Comparator的使用場景。
Comparator:
- 當我們想排序一個無法修改的類的例項時。例如來自jar的類的例項。
- 根據用例需要排序不同的欄位時,例如,一個用例需要通過“name”排序,還有個想要根據“no”排序,或者有的用例需要通過“name和no”來排序。
Comparable:
應該在定義類時知道排序的順序時使用,並且不會有其他任何需要使用Collection /陣列來根據其他欄位排序的情況。
注意:我沒有介紹Set / List的細節。我假設讀者已經瞭解了這些內容。此外,沒有提供使用Set / array進行排序的詳細示例,因為實現與ArrayList非常相似,而ArrayList我已經詳細給出了示例。
最後,感謝閱讀。
譯文連結:http://www.codeceo.com/article/java-objects-sort.html
英文原文:How To Sort Objects In Java
翻譯作者:碼農網 – 小峰
[ 轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]
相關文章
- 詳解Java的物件建立Java物件
- java中Collections.sort排序詳解Java排序
- Java物件導向詳解-上Java物件
- 詳解 Java 中的物件克隆Java物件
- Java中的類與物件詳解Java物件
- java集合物件排序總結Java物件排序
- 堆排序詳解排序
- Java map 詳解 - 用法、遍歷、排序、常用API等Java排序API
- JavaScript表格排序詳解JavaScript排序
- CSSStyleSheet 物件詳解CSS物件
- File 物件詳解物件
- 圖文詳解Java物件記憶體佈局Java物件記憶體
- 氣泡排序和選擇排序詳解排序
- 【JS系列】物件詳解JS物件
- JavaScript arguments物件詳解JavaScript物件
- Git物件概念詳解Git物件
- O(nlogn)快速排序-雙路排序+詳細註解排序
- 演算法 | 快速排序詳解演算法排序
- JavaScript快速排序功能詳解JavaScript排序
- js--history 物件詳解JS物件
- JavaScript中 Map 物件詳解JavaScript物件
- JavaScript 檔案物件詳解JavaScript物件
- 氣泡排序與選擇排序超詳細講解排序
- 氣泡排序及優化詳解排序優化
- 歸併排序詳解及應用排序
- JAVA物件導向詳細總結Java物件
- Java 註解詳解Java
- Java註解詳解Java
- 標準庫~JSON物件詳解JSON物件
- JavaScript物件導向詳解(原理)JavaScript物件
- Js錯誤Error物件詳解JSError物件
- 物件陣列排序物件陣列排序
- 詳解Java 虛擬機器(第②篇)——HotSpot 虛擬機器物件Java虛擬機HotSpot物件
- 『現學現忘』Git物件 — 16、Tree物件詳解Git物件
- 『現學現忘』Git物件 — 15、blob物件詳解Git物件
- java方法詳解Java
- Java Stream 詳解Java
- java反射詳解Java反射