Mengurutkan dengan Comparable dan Comparator di Java

Pemrogram sering kali perlu mengurutkan elemen dari database ke dalam kumpulan, larik, atau peta. Di Java, kita dapat mengimplementasikan algoritma pengurutan apa pun yang kita inginkan dengan tipe apa pun. Dengan menggunakan Comparableantarmuka dan compareTo()metode, kita dapat mengurutkan menggunakan urutan abjad, Stringpanjang, urutan abjad terbalik, atau angka. The Comparatorantarmuka memungkinkan kita untuk melakukan hal yang sama tetapi dengan cara yang lebih fleksibel.

Apa pun yang ingin kita lakukan, kita hanya perlu tahu bagaimana mengimplementasikan logika pengurutan yang benar untuk antarmuka dan tipe yang diberikan.

Dapatkan kode sumbernya

Dapatkan kode untuk Java Challenger ini. Anda dapat menjalankan pengujian Anda sendiri sambil mengikuti contoh.

Mengurutkan Daftar Java dengan objek kustom

Untuk contoh kami, kami akan menggunakan POJO yang sama yang telah kami gunakan untuk Java Challenger lainnya sejauh ini. Dalam contoh pertama ini, kami mengimplementasikan antarmuka Comparable di Simpsonkelas, menggunakan Simpsontipe generik:

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

Perhatikan bahwa kita telah mengganti metode bandingkanTo () dan meneruskan Simpsonobjek lain . Kami juga telah mengganti toString()metode ini, hanya untuk membuat contoh lebih mudah dibaca.

The toStringMetode menunjukkan semua informasi dari objek. Saat kita mencetak objek, outputnya akan menjadi apa pun yang diimplementasikan toString().

Metode bandingkanTo ()

The compareTo()Metode membandingkan objek tertentu atau contoh saat dengan benda tertentu untuk menentukan urutan objek. Berikut sekilas cara compareTo()kerjanya:

  Jika perbandingan kembali

  Kemudian ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Kami hanya dapat menggunakan kelas yang sebanding dengan sort()metode tersebut. Jika kami mencoba untuk melewati Simpsonyang tidak diimplementasikan Comparable, kami akan menerima kesalahan kompilasi.

The sort()Metode menggunakan polimorfisme dengan melewati setiap objek yang Comparable. Objek kemudian akan diurutkan seperti yang diharapkan.

Output dari kode sebelumnya adalah:

 Bart Homer Lisa Marge 

Jika kita ingin membalik urutan, kita bisa bertukar sort()untuk reverse(); dari:

 Collections.sort(simpsons); 

untuk:

 Collections.reverse(simpsons); 

Menerapkan reverse()metode akan mengubah keluaran sebelumnya menjadi:

 Marge Lisa Homer Bart 

Mengurutkan array Java

Di Java, kita bisa mengurutkan array dengan tipe apa pun yang kita inginkan selama itu mengimplementasikan Comparableantarmuka. Berikut contohnya:

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

Dalam sort()pemanggilan pertama , larik diurutkan ke:

 1 6 7 8 9 

Dalam sort()pemanggilan kedua , ini diurutkan ke:

 Homer Lisa 

Perlu diingat bahwa objek khusus harus diimplementasikan Comparableuntuk diurutkan, bahkan sebagai larik.

Bisakah saya mengurutkan objek tanpa Comparable?

Jika objek Simpson tidak diimplementasikan Comparable, ClassCastException akan dilempar. Jika Anda menjalankan ini sebagai pengujian, Anda akan melihat keluaran seperti berikut:

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

Log ini mungkin membingungkan, tetapi jangan khawatir. Perlu diingat bahwa a ClassCastExceptionakan dilemparkan untuk objek yang diurutkan yang tidak mengimplementasikan Comparableantarmuka.

Menyortir Peta dengan TreeMap

Java API menyertakan banyak kelas untuk membantu pengurutan, termasuk TreeMap. Pada contoh di bawah ini, kami menggunakan TreeMapuntuk mengurutkan kunci menjadi 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); } } 

TreeMapmenggunakan compareTo()metode yang diterapkan oleh Comparableantarmuka. Setiap elemen dalam hasil Mapdiurutkan berdasarkan kuncinya. Dalam hal ini, hasilnya adalah:

 Barney=beer, Homer=television, Lenny=Carl, Moe=shotgun 

Ingat, meskipun: jika objek tidak diimplementasikan Comparable, a ClassCastExceptionakan dilempar.

Menyortir Set dengan TreeSet

The Setantarmuka bertanggung jawab untuk menyimpan nilai-nilai yang unik, tetapi ketika kita menggunakan implementasi TreeSet, elemen dimasukkan akan secara otomatis diurutkan seperti yang kita menambahkannya:

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

Output dari kode ini adalah:

 Barney, Homer, Lenny, Moe 

Sekali lagi, jika kita menggunakan objek yang bukan Comparable, a ClassCastExceptionakan dilempar.

Menyortir dengan Pembanding

Bagaimana jika kita tidak ingin menggunakan compareTo()metode yang sama dari kelas POJO? Bisakah kita mengganti Comparablemetode untuk menggunakan logika yang berbeda? Berikut ini contohnya:

 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 Comparatorwhen 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.

Ambil tantangan antarmuka yang sebanding!

Uji apa yang telah Anda pelajari dengan mencari tahu keluaran dari kode berikut. Ingat, Anda akan belajar paling baik jika Anda memecahkan tantangan ini untuk diri Anda sendiri hanya dengan mempelajarinya. Setelah Anda mencapai sebuah jawaban, Anda dapat memeriksa jawabannya di bawah ini. Anda juga dapat menjalankan pengujian Anda sendiri untuk menyerap konsep sepenuhnya.

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

Manakah keluaran dari kode ini?

 A) Bart Homer Lisa Maggie Marge B) Maggie Bart Lisa Marge Homer C) Marge Maggie Lisa Homer Bart D) Indeterminate