程式員經常需要将資料庫中的元素排序到集合,數組或映射中。 在Java中,我們可以實作任何類型的排序算法。 使用
Comparable
接口和
compareTo()
方法,我們可以使用字母順序,
String
長度,反向字母順序或數字進行排序。
Comparator
界面允許我們以更靈活的方式執行相同操作。
無論我們想做什麼,我們隻需要知道如何為給定的接口和類型實作正确的排序邏輯即可。
擷取源代碼
擷取此Java Challenger 的代碼 。 在遵循示例的同時,您可以運作自己的測試。
用自定義對象對Java清單進行排序
在我們的示例中,我們将使用到目前為止與其他Java Challenger相同的POJO。 在第一個示例中,我們使用通用類型的
Simpson
在
Simpson
類中實作Comparable接口:
class Simpson implements Comparable<Simpson> {
String name;
Simpson(String name) {
this.name = name;
}
@Override
public int compareTo(Simpson simpson) {
return this.name.compareTo(simpson.name);
}
}
public class SimpsonSorting {
public static void main(String... sortingWithList) {
List<SimpsonCharacter> simpsons = new ArrayList<>();
simpsons.add(new SimpsonCharacter("Homer "));
simpsons.add(new SimpsonCharacter("Marge "));
simpsons.add(new SimpsonCharacter("Bart "));
simpsons.add(new SimpsonCharacter("Lisa "));
Collections.sort(simpsons);
simpsons.stream().map(s -> s.name).forEach(System.out::print);
Collections.reverse(simpsons);
simpsons.stream().forEach(System.out::print);
}
}
請注意,我們已經重寫了compareTo()方法并傳遞了另一個
Simpson
對象。 我們還重寫了
toString()
方法,隻是為了使示例易于閱讀。
toString
方法顯示該對象的所有資訊。 當我們列印對象時,輸出将是在
toString()
。
compareTo()方法
compareTo()
方法将給定對象或目前執行個體與指定對象進行比較,以确定對象的順序。 快速浏覽
compareTo()
工作原理:
如果比較傳回 | 然後 ... |
| |
| |
| |
我們隻能使用與
sort()
方法相當的
sort()
。 如果我們嘗試傳遞未實作
Comparable
的
Simpson
,則會收到編譯錯誤。
sort()
方法通過傳遞
Comparable
任何對象來使用多态 。 然後将按預期對對象進行排序。
先前代碼的輸出為:
Bart Homer Lisa Marge
如果我們想颠倒順序,我們可以将
sort()
換成
reverse()
; 從:
Collections.sort(simpsons);
至:
Collections.reverse(simpsons);
部署
reverse()
方法會将先前的輸出更改為:
Marge Lisa Homer Bart
排序Java數組
在Java中,我們可以對數組進行任意排序,隻要它實作
Comparable
接口即可。 這是一個例子:
public class ArraySorting {
public static void main(String... moeTavern) {
int[] moesPints = new int[] {9, 8, 7, 6, 1};
Arrays.sort(moesPints);
Arrays.stream(moesPints).forEach(System.out::print);
Simpson[] simpsons = new Simpson[]{new Simpson("Lisa"), new Simpson("Homer")};
Arrays.sort(simpsons);
Arrays.stream(simpsons).forEach(System.out::println);
}
}
在第一個
sort()
調用中,将數組排序為:
1 6 7 8 9
在第二次
sort()
調用中,将其排序為:
Homer Lisa
請記住,自定義對象必須實作
Comparable
才能進行排序,即使是數組也是如此。
我可以對沒有可比對象的對象進行排序嗎?
如果Simpson對象未實作
Comparable
,則将抛出ClassCastException 。 如果将其作為測試運作,您将看到類似以下輸出的内容:
Error:(16, 20) java: no suitable method found for sort(java.util.List<com.javaworld.javachallengers.sortingcomparable.Simpson>)
method java.util.Collections.<T>sort(java.util.List<T>) is not applicable
(inference variable T has incompatible bounds
equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson
lower bounds: java.lang.Comparable<? super T>)
method java.util.Collections.<T>sort(java.util.List<T>,java.util.Comparator<? super T>) is not applicable
(cannot infer type-variable(s) T
(actual and formal argument lists differ in length))
該日志可能令人困惑,但請不要擔心。 請記住,任何未實作
Comparable
接口的已排序對象都将引發
ClassCastException
。
使用TreeMap對地圖排序
Java API包括許多有助于排序的類,包括TreeMap 。 在下面的示例中,我們使用
TreeMap
将鍵排序到
Map
。
public class TreeMapExample {
public static void main(String... barney) {
Map<SimpsonCharacter, String> simpsonsCharacters = new TreeMap<>();
simpsonsCharacters.put(new SimpsonCharacter("Moe"), "shotgun");
simpsonsCharacters.put(new SimpsonCharacter("Lenny"), "Carl");
simpsonsCharacters.put(new SimpsonCharacter("Homer"), "television");
simpsonsCharacters.put(new SimpsonCharacter("Barney"), "beer");
System.out.println(simpsonsCharacters);
}
}
TreeMap
使用
Comparable
接口實作的
compareTo()
方法。 生成的
Map
中的每個元素均按其鍵排序。 在這種情況下,輸出為:
Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun
但是請記住:如果對象未實作
Comparable
,則将抛出
ClassCastException
。
使用TreeSet對集合進行排序
Set
接口負責存儲唯一值,但是當我們使用TreeSet實作時,插入的元素将在添加它們時自動排序:
public class TreeSetExample {
public static void main(String... barney) {
Set<SimpsonCharacter> simpsonsCharacters = new TreeSet<>();
simpsonsCharacters.add(new SimpsonCharacter("Moe"));
simpsonsCharacters.add(new SimpsonCharacter("Lenny"));
simpsonsCharacters.add(new SimpsonCharacter("Homer"));
simpsonsCharacters.add(new SimpsonCharacter("Barney"));
System.out.println(simpsonsCharacters);
}
}
此代碼的輸出是:
Barney, Homer, Lenny, Moe
同樣,如果我們使用的對象不是
Comparable
,則将抛出
ClassCastException
。
用比較器排序
如果我們不想使用POJO類中的相同
compareTo()
方法怎麼辦? 我們可以重寫
Comparable
方法以使用其他邏輯嗎? 下面是一個示例:
public class BadExampleOfComparable {
public static void main(String... args) {
List<SimpsonCharacter> characters = new ArrayList<>();
SimpsonCharacter homer = new SimpsonCharacter("Homer") {
@Override
public int compareTo(SimpsonCharacter simpson) {
return this.name.length() - (simpson.name.length());
}
};
SimpsonCharacter moe = new SimpsonCharacter("Moe") {
@Override
public int compareTo(SimpsonCharacter simpson) {
return this.name.length() - (simpson.name.length());
}
};
characters.add(homer);
characters.add(moe);
Collections.sort(characters);
System.out.println(characters);
}
}
如您所見,此代碼很複雜,并且包含很多重複。 對于相同的邏輯,我們必須兩次重寫
compareTo()
方法。 如果還有更多元素,我們将不得不為每個對象複制邏輯。
幸運的是,我們具有Comparator接口,該接口使我們可以将
compareTo()
邏輯與Java類分離。 考慮上面使用
Comparator
重寫的同一示例:
public class GoodExampleOfComparator {
public static void main(String... args) {
List<SimpsonCharacter> characters = new ArrayList<>();
SimpsonCharacter homer = new SimpsonCharacter("Homer");
SimpsonCharacter moe = new SimpsonCharacter("Moe");
characters.add(homer);
characters.add(moe);
Collections.sort(characters, (Comparator.<SimpsonCharacter>
comparingInt(character1 -> character1.name.length())
.thenComparingInt(character2 -> character2.name.length())));
System.out.println(characters);
}
}
這些示例說明了
Comparable
和
Comparator
之間的主要差別。
當對象有一個預設的預設比較時,請使用
Comparable
。 使用
Comparator
時,你需要解決現有
compareTo()
或者當你需要使用特定的邏輯更靈活的方式。
Comparator
從您的對象分離排序邏輯,并在
sort()
方法内包含
compareTo()
邏輯。
将Comparator與匿名内部類一起使用
在下一個示例中,我們使用匿名内部類比較對象的值。 在這種情況下, 匿名内部類是實作
Comparator
任何類。 使用它意味着我們不必執行個體化實作接口的命名類。 相反,我們在匿名内部類中實作了
compareTo()
方法。
public class MarvelComparator {
public static void main(String... comparator) {
List<String> marvelHeroes = new ArrayList<>();
marvelHeroes.add("SpiderMan ");
marvelHeroes.add("Wolverine ");
marvelHeroes.add("Xavier ");
marvelHeroes.add("Cyclops ");
Collections.sort(marvelHeroes, new Comparator<String>() {
@Override
public int compare(String hero1, String hero2) {
return hero1.compareTo(hero2);
}
});
Collections.sort(marvelHeroes, (m1, m2) -> m1.compareTo(m2));
Collections.sort(marvelHeroes, Comparator.naturalOrder());
marvelHeroes.forEach(System.out::print);
}
}
有關内部類的更多資訊
匿名内部類就是名稱無關緊要且實作了我們聲明的接口的任何類。 是以,在該示例中,新的
Comparator
實際上是一個沒有名稱的類的執行個體化,該類使用所需的邏輯來實作該方法。
将Comparator與lambda表達式一起使用
匿名内部類非常冗長,這可能會導緻我們的代碼出現問題。 在
Comparator
界面中,我們可以使用lambda表達式來簡化代碼并使代碼更易于閱讀。 例如,我們可以更改此:
Collections.sort(marvel, new Comparator<String>() {
@Override
public int compare(String hero1, String hero2) {
return hero1.compareTo(hero2);
}
});
對此:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
更少的代碼和相同的結果!
該代碼的輸出為:
Cyclops SpiderMan Wolverine Xavier
通過更改此代碼,我們可以使代碼更簡單:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
對此:
Collections.sort(marvel, Comparator.naturalOrder());
Java中的Lambda表達式
了解有關Java中的lambda表達式和其他功能程式設計技術的更多資訊。
核心Java類是否可比?
許多核心Java類和對象都實作
Comparable
接口,這意味着我們不必為這些類實作
compareTo()
邏輯。 以下是一些熟悉的示例:
串
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence { ...
整數
public final class Integer extends Number implements Comparable<Integer> { …
雙
public final class Double extends Number implements Comparable<Double> {...
還有很多。 我鼓勵您探索Java核心類,以學習它們的重要模式和概念。
接受可比接口挑戰!
通過弄清楚以下代碼的輸出來測試您學到了什麼。 請記住,如果僅通過學習自己解決挑戰,您将學得最好。 找到答案後,您可以檢查以下答案。 您也可以運作自己的測試以完全吸收這些概念。
public class SortComparableChallenge {
public static void main(String... doYourBest) {
Set<Simpson> set = new TreeSet<>();
set.add(new Simpson("Homer"));
set.add(new Simpson("Marge"));
set.add(new Simpson("Lisa"));
set.add(new Simpson("Bart"));
set.add(new Simpson("Maggie"));
List<Simpson> list = new ArrayList<>();
list.addAll(set);
Collections.reverse(list);
list.forEach(System.out::println);
}
static class Simpson implements Comparable<Simpson> {
String name;
public Simpson(String name) {
this.name = name;
}
public int compareTo(Simpson simpson) {
return simpson.name.compareTo(this.name);
}
public String toString() {
return this.name;
}
}
}
該代碼的輸出是什麼?
A) Bart
Homer
Lisa
Maggie
Marge
B) Maggie
Bart
Lisa
Marge
Homer
C) Marge
Maggie
Lisa
Homer
Bart
D) Indeterminate
翻譯自: https://www.infoworld.com/article/3323403/java-challengers-5-sorting-with-comparable-and-comparator-in-java.html