Menyusun dengan Perbandingan dan Pembanding di Java

Pengaturcara sering perlu menyusun elemen dari pangkalan data ke dalam koleksi, susunan, atau peta. Di Java, kita dapat menerapkan algoritma penyortiran apa pun yang kita inginkan dengan jenis apa pun. Dengan menggunakan Comparableantara muka dan compareTo()kaedah, kita dapat mengisih menggunakan susunan abjad, Stringpanjang, susunan abjad terbalik, atau angka. Antara Comparatormuka membolehkan kita melakukan perkara yang sama tetapi dengan cara yang lebih fleksibel.

Apa sahaja yang ingin kita lakukan, kita hanya perlu tahu bagaimana menerapkan logik urutan yang betul untuk antara muka dan jenis yang diberikan.

Dapatkan kod sumber

Dapatkan kod untuk Java Challenger ini. Anda boleh menjalankan ujian anda sendiri semasa anda mengikuti contohnya.

Menyusun Daftar Java dengan objek tersuai

Sebagai contoh, kita akan menggunakan POJO yang sama seperti yang kita gunakan untuk Java Challenger lain setakat ini. Dalam contoh pertama ini, kami menerapkan antara muka yang boleh dibandingkan di Simpsonkelas, menggunakan Simpsonjenis 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 bahawa kami telah mengatasi kaedah membandingkanTo () dan meneruskan Simpsonobjek lain . Kami juga telah menolak toString()kaedahnya, untuk menjadikan contohnya lebih mudah dibaca.

Yang toStringmenunjukkan kaedah semua maklumat dari objek. Ketika kita mencetak objek, output akan menjadi apa pun yang dilaksanakan toString().

Kaedah membandingkanTo ()

The compareTo()kaedah membandingkan objek yang diberikan atau contoh semasa dengan objek yang ditetapkan untuk menentukan susunan objek. Berikut adalah gambaran ringkas bagaimana ia compareTo()berfungsi

  Sekiranya perbandingan kembali

  Kemudian ...

  >= 1

  this.name > simpson.name

  0

  this.name == simpson.name

  <= -1

  this.name < simpson.name

Kita hanya boleh menggunakan kelas yang setanding dengan sort()kaedahnya. Sekiranya kita cuba melepasi a Simpsonyang tidak dilaksanakan Comparable, kita akan menerima ralat penyusunan.

The sort()Cara menggunakan polymorphism dengan melepaskan apa-apa objek yang Comparable. Objek kemudian akan disusun seperti yang diharapkan.

Keluaran dari kod sebelumnya adalah:

 Bart Homer Lisa Marge 

Jika kita mahu untuk mengubah perintah itu, kita boleh bertukar-tukar sort()untuk reverse(); dari:

 Collections.sort(simpsons); 

untuk:

 Collections.reverse(simpsons); 

Menggunakan reverse()kaedah akan mengubah output sebelumnya menjadi:

 Marge Lisa Homer Bart 

Menyusun susunan Java

Di Jawa, kita dapat menyusun susunan dengan jenis apa pun yang kita inginkan selagi menerapkan Comparableantara muka. Inilah 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()permintaan pertama , susunan disusun mengikut:

 1 6 7 8 9 

Dalam sort()doa kedua , disusun ke:

 Homer Lisa 

Perlu diingat bahawa objek khusus mesti dilaksanakan Comparableagar dapat disusun, bahkan sebagai array.

Bolehkah saya menyusun objek tanpa Banding?

Sekiranya objek Simpson tidak dilaksanakan Comparable, ClassCastException akan dilemparkan. Sekiranya anda menjalankan ini sebagai ujian, anda akan melihat seperti output 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 risau. Perlu diingat bahawa ClassCastExceptionkehendak akan dilemparkan untuk objek yang disusun yang tidak melaksanakan Comparableantara muka.

Menyusun Peta dengan TreeMap

Java API merangkumi banyak kelas untuk membantu menyusun, termasuk TreeMap. Dalam contoh di bawah, kita gunakan TreeMapuntuk mengisih kunci menjadi 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); } } 

TreeMapmenggunakan compareTo()kaedah yang dilaksanakan oleh Comparableantara muka. Setiap elemen yang dihasilkan Mapdisusun mengikut kuncinya. Dalam kes ini, outputnya adalah:

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

Ingatlah, walaupun: jika objek tersebut tidak dilaksanakan Comparable, ClassCastExceptionakan dilemparkan.

Menyusun Set dengan TreeSet

The Setmuka adalah bertanggungjawab untuk menyimpan nilai unik, tetapi apabila kita menggunakan pelaksanaan TreeSet, unsur-unsur dimasukkan akan secara automatik disusun seperti yang kita menambah mereka:

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

Keluaran dari kod ini adalah:

 Barney, Homer, Lenny, Moe 

Sekali lagi, jika kita menggunakan objek yang bukan Comparable, ClassCastExceptionkemahuan akan dilemparkan.

Menyusun dengan Pembanding

Bagaimana jika kita tidak mahu menggunakan compareTo()kaedah yang sama dari kelas POJO? Bolehkah kita mengganti Comparablekaedah menggunakan logik yang berbeza? Berikut adalah contoh:

 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.

Ikuti cabaran antara muka yang setanding!

Uji apa yang telah anda pelajari dengan mengetahui keluaran kod berikut. Ingat, anda akan belajar dengan lebih baik sekiranya anda menyelesaikan cabaran ini untuk diri sendiri hanya dengan mempelajarinya. Setelah mencapai jawapan, anda boleh menyemak jawapan di bawah. Anda juga boleh menjalankan ujian 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; } } } 

Yang manakah output kod ini?

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