Comparator dan Comparable di Java

1. Perkenalan

Perbandingan di Java cukup mudah - sampai sebenarnya tidak.

Saat bekerja dengan jenis khusus, atau mencoba membandingkan objek yang tidak dapat dibandingkan secara langsung, kita perlu menggunakan strategi perbandingan. Kita dapat membangunnya dengan sederhana, tetapi menggunakan antarmuka Comparator atau Comparable .

2. Menyiapkan Contoh

Mari kita ambil contoh tim sepak bola - di mana kami ingin menyusun pemain berdasarkan peringkat mereka.

Kami akan mulai dengan membuat kelas Pemain sederhana :

public class Player { private int ranking; private String name; private int age; // constructor, getters, setters }

Selanjutnya, mari buat kelas PlayerSorter untuk membuat koleksi kita dan mencoba mengurutkannya menggunakan Collections.sort :

public static void main(String[] args) { List footballTeam = new ArrayList(); Player player1 = new Player(59, "John", 20); Player player2 = new Player(67, "Roger", 22); Player player3 = new Player(45, "Steven", 24); footballTeam.add(player1); footballTeam.add(player2); footballTeam.add(player3); System.out.println("Before Sorting : " + footballTeam); Collections.sort(footballTeam); System.out.println("After Sorting : " + footballTeam); } 

Di sini, seperti yang diharapkan, ini menghasilkan kesalahan waktu kompilasi:

The method sort(List) in the type Collections is not applicable for the arguments (ArrayList)

Mari kita pahami kesalahan yang kita lakukan di sini.

3. Sebanding

Seperti namanya, Comparable adalah antarmuka yang mendefinisikan strategi untuk membandingkan suatu objek dengan objek lain yang berjenis sama. Ini disebut “tatanan alami” kelas.

Oleh karena itu, untuk dapat mengurutkan - kita harus mendefinisikan objek Player sebagai pembanding dengan mengimplementasikan antarmuka Comparable :

public class Player implements Comparable { // same as before @Override public int compareTo(Player otherPlayer) { return Integer.compare(getRanking(), otherPlayer.getRanking()); } } 

Urutan pengurutan ditentukan oleh nilai kembalian dari metode bandingkanTo () . The Integer.compare (x, y) kembali -1 jika x kurang dari y , kembali 0 jika mereka sama, dan kembali 1 sebaliknya.

Metode mengembalikan angka yang menunjukkan apakah objek yang dibandingkan lebih kecil dari, sama dengan, atau lebih besar dari objek yang diteruskan sebagai argumen.

Terakhir, saat kami menjalankan PlayerSorter kami sekarang, kami dapat melihat Pemain kami diurutkan berdasarkan peringkat mereka:

Before Sorting : [John, Roger, Steven] After Sorting : [Steven, John, Roger]

Sekarang setelah kita memiliki pemahaman yang jelas tentang pengurutan natural dengan Comparable , mari kita lihat bagaimana kita dapat menggunakan jenis pengurutan lain, dengan cara yang lebih fleksibel daripada mengimplementasikan antarmuka secara langsung.

4. Pembanding

The Comparator antarmuka mendefinisikan membandingkan (arg1, arg2) metode dengan dua argumen yang mewakili dibandingkan benda dan bekerja mirip dengan Comparable.compareTo () metode.

4.1. Membuat Komparator

Untuk membuat Comparator, kita harus mengimplementasikan antarmuka Comparator .

Dalam contoh pertama kami, kami akan membuat Pembanding untuk menggunakan atribut peringkat Pemain untuk mengurutkan pemain:

public class PlayerRankingComparator implements Comparator { @Override public int compare(Player firstPlayer, Player secondPlayer) { return Integer.compare(firstPlayer.getRanking(), secondPlayer.getRanking()); } }

Demikian pula, kita dapat membuat Pembanding untuk menggunakan atribut usia Pemain untuk mengurutkan pemain:

public class PlayerAgeComparator implements Comparator { @Override public int compare(Player firstPlayer, Player secondPlayer) { return Integer.compare(firstPlayer.getAge(), secondPlayer.getAge()); } }

4.2. Pembanding Beraksi

Untuk mendemonstrasikan konsepnya, mari kita ubah PlayerSorter kita dengan memasukkan argumen kedua ke metode Collections.sort yang sebenarnya merupakan instance dari Comparator yang ingin kita gunakan.

Dengan menggunakan pendekatan ini, kita dapat mengganti urutan natural :

PlayerRankingComparator playerComparator = new PlayerRankingComparator(); Collections.sort(footballTeam, playerComparator); 

Sekarang, mari jalankan PlayerRankingSorter kita untuk melihat hasilnya:

Before Sorting : [John, Roger, Steven] After Sorting by ranking : [Steven, John, Roger]

Jika kita menginginkan urutan sortir yang berbeda, kita hanya perlu mengubah Pembanding yang kita gunakan:

PlayerAgeComparator playerComparator = new PlayerAgeComparator(); Collections.sort(footballTeam, playerComparator);

Sekarang, ketika kita menjalankan PlayerAgeSorter , kita dapat melihat urutan berbeda berdasarkan umur:

Before Sorting : [John, Roger, Steven] After Sorting by age : [Roger, John, Steven]

4.3. Pembanding Java 8

Java 8 menyediakan cara baru untuk mendefinisikan Pembanding dengan menggunakan ekspresi lambda dan metode pabrik statis membandingkan () .

Mari kita lihat contoh singkat tentang cara menggunakan ekspresi lambda untuk membuat Pembanding :

Comparator byRanking = (Player player1, Player player2) -> Integer.compare(player1.getRanking(), player2.getRanking());

The Comparator.comparing Metode mengambil metode menghitung properti yang akan digunakan untuk membandingkan item, dan mengembalikan cocok komparator misalnya:

Comparator byRanking = Comparator .comparing(Player::getRanking); Comparator byAge = Comparator .comparing(Player::getAge);

Anda dapat menjelajahi fungsionalitas Java 8 secara mendalam di panduan Membandingkan Java 8 kami.

5. Pembanding vs Sebanding

The Sebanding antarmuka adalah pilihan yang baik ketika digunakan untuk mendefinisikan pemesanan bawaan atau, dengan kata lain, apakah itu cara utama membandingkan benda.

Lalu, kita harus bertanya pada diri sendiri mengapa menggunakan Comparator jika kita sudah memiliki Comparable ?

Ada beberapa alasan mengapa:

  • Sometimes, we can't modify the source code of the class whose objects we want to sort, thus making the use of Comparable impossible
  • Using Comparators allows us to avoid adding additional code to our domain classes
  • We can define multiple different comparison strategies which isn't possible when using Comparable

6. Avoiding the Subtraction Trick

Over the course of this tutorial, we used the Integer.compare() method to compare two integers. One might argue that we should use this clever one-liner instead:

Comparator comparator = (p1, p2) -> p1.getRanking() - p2.getRanking();

Although it's much more concise compared to other solutions, it can be a victim of integer overflows in Java:

Player player1 = new Player(59, "John", Integer.MAX_VALUE); Player player2 = new Player(67, "Roger", -1); List players = Arrays.asList(player1, player2); players.sort(comparator);

Since -1 is much less than the Integer.MAX_VALUE, “Roger” should come before the “John” in the sorted collection. However, due to integer overflow, the “Integer.MAX_VALUE – (-1)” will be less than zero. So, based on the Comparator/Comparable contract, the Integer.MAX_VALUE is less than -1, which is obviously incorrect.

Hence, despite what we expected, “John” comes before the “Roger” in the sorted collection:

assertEquals("John", players.get(0).getName()); assertEquals("Roger", players.get(1).getName());

7. Conclusion

In this tutorial, we explored the Comparable and Comparator interfaces and discussed the differences between them.

To understand more advanced topics of sorting, check out our other articles such as Java 8 Comparator, Java 8 Comparison with Lambdas.

Dan, seperti biasa, kode sumber dapat ditemukan di GitHub.