การเรียงลำดับด้วย Comparable และ Comparator ใน Java
โปรแกรมเมอร์มักจะต้องเรียงองค์ประกอบจากฐานข้อมูลลงในคอลเลกชันอาร์เรย์หรือแผนที่ ใน Java เราสามารถใช้อัลกอริทึมการเรียงลำดับที่เราต้องการกับประเภทใดก็ได้ เมื่อใช้Comparable
อินเทอร์เฟซและcompareTo()
วิธีการเราสามารถจัดเรียงโดยใช้ลำดับตัวอักษรString
ความยาวลำดับตัวอักษรย้อนกลับหรือตัวเลข Comparator
อินเตอร์เฟซที่ช่วยให้เราสามารถทำเช่นเดียวกัน แต่ในทางที่มีความยืดหยุ่นมากขึ้น
ไม่ว่าเราต้องการทำอะไรเราเพียงแค่ต้องรู้วิธีใช้ตรรกะการเรียงลำดับที่ถูกต้องสำหรับอินเทอร์เฟซและประเภทที่กำหนด
รับซอร์สโค้ด
รับรหัสสำหรับ Java Challenger นี้ คุณสามารถเรียกใช้การทดสอบของคุณเองในขณะที่ทำตามตัวอย่าง
การเรียงลำดับรายการ Java ด้วยวัตถุแบบกำหนดเอง
สำหรับตัวอย่างของเราเราจะใช้ POJO เดียวกันกับที่เราเคยใช้กับ Java Challengers อื่น ๆ จนถึงตอนนี้ ในตัวอย่างแรกนี้เราใช้อินเทอร์เฟซที่เปรียบเทียบได้ในSimpson
คลาสโดยใช้Simpson
ในประเภททั่วไป:
class Simpson implements Comparable { 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 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()
เมธอดเท่านั้น หากเราพยายามส่งผ่านSimpson
ที่ไม่ได้ใช้งานComparable
เราจะได้รับข้อผิดพลาดในการคอมไพล์
sort()
วิธีการใช้แตกต่างโดยการส่งผ่านวัตถุใด ๆ Comparable
ที่เป็น จากนั้นวัตถุจะถูกจัดเรียงตามที่คาดไว้
ผลลัพธ์จากรหัสก่อนหน้าจะเป็น:
Bart Homer Lisa Marge
หากเราต้องการกลับคำสั่งเราสามารถแลกเปลี่ยนsort()
a 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) method java.util.Collections.sort(java.util.List) is not applicable (inference variable T has incompatible bounds equality constraints: com.javaworld.javachallengers.sortingcomparable.Simpson lower bounds: java.lang.Comparable) method java.util.Collections.sort(java.util.List,java.util.Comparator) is not applicable (cannot infer type-variable(s) T (actual and formal argument lists differ in length))
บันทึกนี้อาจทำให้สับสน แต่ไม่ต้องกังวล โปรดทราบว่าClassCastException
จะมีการโยนวัตถุใด ๆ ที่ไม่ได้ใช้Comparable
อินเทอร์เฟซ
การจัดเรียงแผนที่ด้วย TreeMap
Java API มีคลาสมากมายเพื่อช่วยในการเรียงลำดับรวมถึง TreeMap ในตัวอย่างด้านล่างเราใช้TreeMap
เพื่อจัดเรียงคีย์เป็นไฟล์Map
.
public class TreeMapExample { public static void main(String... barney) { Map 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
ใช้compareTo()
วิธีการที่ใช้งานโดยComparable
อินเทอร์เฟซ แต่ละองค์ประกอบในผลลัพธ์Map
จะจัดเรียงตามคีย์ ในกรณีนี้ผลลัพธ์จะเป็น:
Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun
จำไว้ว่าถ้าวัตถุไม่ดำเนินComparable
การClassCastException
จะถูกโยน
การจัดเรียงชุดด้วย TreeSet
Set
อินเตอร์เฟซที่เป็นผู้รับผิดชอบสำหรับการจัดเก็บค่าไม่ซ้ำกัน แต่เมื่อเราใช้การดำเนินการ TreeSet องค์ประกอบแทรกจะถูกจัดเรียงโดยอัตโนมัติขณะที่เราเพิ่มพวกเขา
public class TreeSetExample { public static void main(String... barney) { Set 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
จะถูกโยน
การเรียงลำดับด้วยตัวเปรียบเทียบ
จะเกิดอะไรขึ้นถ้าเราไม่ต้องการใช้compareTo()
วิธีการเดียวกันจากคลาส POJO? เราสามารถแทนที่Comparable
วิธีการเพื่อใช้ตรรกะอื่นได้หรือไม่? ด้านล่างนี้คือตัวอย่าง:
public class BadExampleOfComparable { public static void main(String... args) { List 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); } }
As you can see, this code is complicated and includes a lot of repetition. We had to override the compareTo()
method twice for the same logic. If there were more elements we would have to replicate the logic for each object.
Fortunately, we have the Comparator interface, which lets us detach the compareTo()
logic from Java classes. Consider the same example above rewritten using Comparator
:
public class GoodExampleOfComparator { public static void main(String... args) { List characters = new ArrayList(); SimpsonCharacter homer = new SimpsonCharacter("Homer"); SimpsonCharacter moe = new SimpsonCharacter("Moe"); characters.add(homer); characters.add(moe); Collections.sort(characters, (Comparator. comparingInt(character1 -> character1.name.length()) .thenComparingInt(character2 -> character2.name.length()))); System.out.println(characters); } }
These examples demonstrate the main difference between Comparable
and Comparator
.
Use Comparable
when there is a single, default comparison for your object. Use Comparator
when you need to work around an existing compareTo()
, or when you need to use specific logic in a more flexible way. Comparator
detaches the sorting logic from your object and contains the compareTo()
logic within your sort()
method.
Using Comparator with an anonymous inner class
In this next example, we use an anonymous inner class to compare the value of objects. An anonymous inner class, in this case, is any class that implements Comparator
. Using it means we are not bound to instantiating a named class implementing an interface; instead, we implement the compareTo()
method inside the anonymous inner class.
public class MarvelComparator { public static void main(String... comparator) { List marvelHeroes = new ArrayList(); marvelHeroes.add("SpiderMan "); marvelHeroes.add("Wolverine "); marvelHeroes.add("Xavier "); marvelHeroes.add("Cyclops "); Collections.sort(marvelHeroes, new Comparator() { @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); } }
More about inner classes
An anonymous inner class is simply any class whose name doesn’t matter, and which implements the interface we are declaring. So in the example, the new Comparator
is actually the instantiation of a class that doesn’t have a name, which implements the method with the logic we want.
Using Comparator with lambda expressions
Anonymous inner classes are verbose, which can cause problems in our code. In the Comparator
interface, we can use lambda expressions to simplify and make the code easier to read. For example, we could change this:
Collections.sort(marvel, new Comparator() { @Override public int compare(String hero1, String hero2) { return hero1.compareTo(hero2); } });
to this:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
Less code and the same result!
The output of this code would be:
Cyclops SpiderMan Wolverine Xavier
We could make the code even simpler by changing this:
Collections.sort(marvel, (m1, m2) -> m1.compareTo(m2));
to this:
Collections.sort(marvel, Comparator.naturalOrder());
Lambda expressions in Java
Learn more about lambda expressions and other functional programming techniques in Java.
Are the core Java classes Comparable?
Many core Java classes and objects implement the Comparable
interface, which means we don’t have to implement the compareTo()
logic for those classes. Here are a few familiar examples:
String
public final class String implements java.io.Serializable, Comparable, CharSequence { ...
Integer
public final class Integer extends Number implements Comparable { …
Double
public final class Double extends Number implements Comparable {...
There are many others. I encourage you to explore the Java core classes to learn their important patterns and concepts.
ท้าทายอินเทอร์เฟซที่เทียบเคียงได้!
ทดสอบสิ่งที่คุณได้เรียนรู้โดยการหาผลลัพธ์ของรหัสต่อไปนี้ จำไว้ว่าคุณจะเรียนรู้ได้ดีที่สุดหากคุณแก้ปัญหานี้ด้วยตัวคุณเองเพียงแค่ศึกษามัน เมื่อคุณได้รับคำตอบแล้วคุณสามารถตรวจสอบคำตอบด้านล่าง คุณยังสามารถเรียกใช้การทดสอบของคุณเองเพื่อซึมซับแนวคิดอย่างเต็มที่
public class SortComparableChallenge { public static void main(String... doYourBest) { Set 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 list = new ArrayList(); list.addAll(set); Collections.reverse(list); list.forEach(System.out::println); } static class Simpson implements Comparable { 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