מתכנתים צריכים לעתים קרובות למיין אלמנטים ממסד נתונים לאוסף, מערך או מפה. בג'אווה אנו יכולים ליישם את כל אלגוריתם המיון שנרצה בכל סוג שהוא. בעזרת Comparable
הממשק compareTo()
והשיטה נוכל למיין לפי סדר אלפביתי, String
אורך, סדר אלפביתי הפוך או מספרים. Comparator
הממשק מאפשר לנו לעשות את אותו הדבר אבל בצורה גמישה יותר.
מה שלא נרצה לעשות, עלינו רק לדעת ליישם את לוגיקת המיון הנכונה עבור הממשק והסוג הנתון.
קבל את קוד המקור
קבל את הקוד עבור צ'לנג'ר Java זה. אתה יכול לבצע בדיקות משלך בזמן שאתה עוקב אחר הדוגמאות.
מיון רשימת Java עם אובייקט מותאם אישית
לדוגמא שלנו נשתמש באותו POJO בו השתמשנו עבור אתגרי Java אחרים עד כה. בדוגמה ראשונה זו, אנו מיישמים את הממשק השווה 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); } }
שימו לב שדרסנו את שיטת השוואת () והעברנו Simpson
באובייקט אחר . ביטלנו את toString()
השיטה, רק כדי להקל על הקריאה בדוגמה.
toString
שיטת המופעים כל מידע מהאובייקט. כאשר אנו מדפיסים את האובייקט, הפלט יהיה כל מה שיושם בו toString()
.
שיטת comparTo ()
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
מיון מערך ג'אווה
בג'אווה נוכל למיין מערך בכל סוג שנרצה כל עוד הוא מיישם את 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
כדי להיות ממוינים, אפילו כמערך.
האם אוכל למיין אובייקטים ללא השוואה?
אם האובייקט סימפסון לא היה מיושם 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
ממשק ה- API של Java כולל שיעורים רבים שיסייעו במיון, כולל TreeMap. בדוגמה שלהלן אנו משתמשים TreeMap
במיון מקשים ל- a 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
רצון ייזרק.
מיון עם Comparator
מה אם לא נרצה להשתמש באותה 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