Pemalas bukan satu-satunya yang menulis tentang Pembanding dan perbandingan di Jawa. Saya tidak malas, jadi tolong cintai dan keluhkan tentang penjelasan lainnya. Saya harap itu tidak akan berlebihan. Dan ya, artikel ini adalah jawaban untuk pertanyaan: " Bisakah Anda menulis pembanding dari ingatan? " Saya harap semua orang dapat menulis pembanding dari ingatan setelah membaca artikel ini.

Perkenalan
Seperti yang Anda ketahui, Java adalah bahasa berorientasi objek. Akibatnya, sudah menjadi kebiasaan untuk memanipulasi objek di Java. Namun cepat atau lambat, Anda dihadapkan pada tugas membandingkan objek berdasarkan beberapa karakteristik. Sebagai contoh : Misalkan kita memiliki beberapa pesan yang dijelaskan olehMessage
kelas:
public static class Message {
private String message;
private int id;
public Message(String message) {
this.message = message;
this.id = new Random().nextInt(1000);
}
public String getMessage() {
return message;
}
public Integer getId() {
return id;
}
public String toString() {
return "[" + id + "] " + message;
}
}
Letakkan kelas ini di compiler Java Tutorialspoint . Jangan lupa untuk menambahkan pernyataan impor juga:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Dalam main
metode ini, buat beberapa pesan:
public static void main(String[] args){
List<Message> messages = new ArrayList();
messages.add(new Message("Hello, World!"));
messages.add(new Message("Hello, Sun!"));
System.out.println(messages);
}
Mari kita pikirkan apa yang akan kita lakukan jika kita ingin membandingkannya? Misalnya, kami ingin mengurutkan berdasarkan id. Dan untuk membuat urutan, kita perlu membandingkan objek untuk memahami objek mana yang harus didahulukan (yaitu yang lebih kecil) dan mana yang harus diikuti (yaitu yang lebih besar). Mari kita mulai dengan kelas seperti java.lang.Object . Kita tahu bahwa semua kelas secara implisit mewarisi Object
kelas tersebut. Dan ini masuk akal karena mencerminkan konsep bahwa "semuanya adalah objek" dan menyediakan perilaku umum untuk semua kelas. Kelas ini menentukan bahwa setiap kelas memiliki dua metode: → hashCode
Metode hashCode
mengembalikan beberapa numerik (int
) representasi objek. Maksudnya itu apa? Ini berarti bahwa jika Anda membuat dua instance kelas yang berbeda, maka mereka harus memiliki hashCode
s yang berbeda. Deskripsi metode mengatakan sebanyak: "Sebanyak yang cukup praktis, metode kode hash yang didefinisikan oleh objek kelas tidak mengembalikan bilangan bulat yang berbeda untuk objek yang berbeda". Dengan kata lain, untuk dua instance
s yang berbeda, harus ada hashCode
s yang berbeda. Artinya, metode ini tidak cocok untuk perbandingan kita. → equals
. Metode tersebut equals
menjawab pertanyaan "apakah benda-benda ini sama?" dan mengembalikan boolean
." Secara default, metode ini memiliki kode berikut:
public boolean equals(Object obj) {
return (this == obj);
}
Yaitu, jika metode ini tidak ditimpa, pada dasarnya dikatakan apakah referensi objek cocok atau tidak. Ini bukan yang kami inginkan untuk pesan kami, karena kami tertarik pada id pesan, bukan referensi objek. Dan bahkan jika kita mengganti equals
metodenya, yang paling bisa kita harapkan adalah mempelajari apakah mereka setara. Dan ini tidak cukup bagi kami untuk menentukan urutannya. Jadi apa yang kita butuhkan? Kami membutuhkan sesuatu yang sebanding. Yang membandingkan adalah Comparator
. Buka Java API dan temukan Comparator . Memang, ada java.util.Comparator
antarmuka java.util.Comparator and java.util.Comparable
Seperti yang Anda lihat, antarmuka seperti itu ada. Kelas yang mengimplementasikannya mengatakan, "Saya mengimplementasikan metode yang membandingkan objek." Satu-satunya hal yang perlu Anda ingat adalah kontrak pembanding, yang dinyatakan sebagai berikut:
Comparator returns an int according to the following rules:
- It returns a negative int if the first object is smaller
- It returns a positive int if the first object is larger
- It returns zero if the objects are equal
Sekarang mari kita menulis pembanding. Kita perlu mengimpor java.util.Comparator
. Setelah pernyataan impor, tambahkan yang berikut ke main
metode: Comparator<Message> comparator = new Comparator<Message>();
Tentu saja, ini tidak akan berfungsi, karena Comparator
merupakan antarmuka. Jadi kami menambahkan kurung kurawal {}
setelah tanda kurung. Tulis metode berikut di dalam kurung:
public int compare(Message o1, Message o2) {
return o1.getId().compareTo(o2.getId());
}
Anda bahkan tidak perlu mengingat ejaannya. Komparator adalah orang yang melakukan perbandingan, yaitu membandingkan. Untuk menunjukkan urutan relatif objek, kami mengembalikan int
. Itu pada dasarnya. Baik dan mudah. Seperti yang Anda lihat dari contoh, selain Comparator, ada antarmuka lain — , java.lang.Comparable
yang mengharuskan kita untuk mengimplementasikan compareTo
metode ini. Antarmuka ini mengatakan, "kelas yang mengimplementasikan saya memungkinkan untuk membandingkan contoh kelas." Sebagai contoh, Integer
penerapan To compare
adalah sebagai berikut:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 memperkenalkan beberapa perubahan yang menyenangkan. Jika Anda melihat lebih dekat pada Comparator
antarmuka, Anda akan melihat @FunctionalInterface
anotasi di atasnya. Anotasi ini untuk tujuan informasi dan memberi tahu kami bahwa antarmuka ini berfungsi. Artinya interface ini hanya memiliki 1 abstract method, yaitu method tanpa implementasi. Apa yang ini berikan kepada kita? Sekarang kita dapat menulis kode pembanding seperti ini:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Kami memberi nama variabel dalam tanda kurung. Java akan melihat bahwa karena hanya ada satu metode, maka jumlah dan jenis parameter input yang diperlukan jelas. Kemudian kami menggunakan operator panah untuk meneruskannya ke bagian kode ini. Terlebih lagi, berkat Java 8, kami sekarang memiliki metode default di antarmuka. Metode ini muncul secara default saat kami mengimplementasikan sebuah antarmuka. Antarmuka Comparator
memiliki beberapa. Misalnya:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Ada metode lain yang akan membuat kode Anda lebih bersih. Lihatlah contoh di atas, di mana kami mendefinisikan pembanding kami. Apa fungsinya? Ini cukup primitif. Ini hanya mengambil objek dan mengekstraksi beberapa nilai yang "sebanding". Misalnya, Integer
implements comparable
, sehingga kita dapat melakukan operasi compareTo pada nilai bidang id pesan. Fungsi pembanding sederhana ini dapat ditulis seperti ini:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Dengan kata lain, kita memiliki a Comparator
yang membandingkan seperti ini: mengambil objek, menggunakan getId()
metode untuk mendapatkan a Comparable
darinya, lalu menggunakan compareTo
untuk membandingkan. Dan tidak ada konstruksi yang lebih mengerikan. Dan terakhir, saya ingin mencatat satu fitur lagi. Pembanding dapat dirantai. Misalnya:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());
Aplikasi
Mendeklarasikan pembanding ternyata cukup logis, bukan begitu? Sekarang kita perlu melihat bagaimana dan di mana menggunakannya. →Collections.sort(java.util.Collections)
Kami tentu saja dapat mengurutkan koleksi dengan cara ini. Tapi tidak semua koleksi, hanya daftar. Tidak ada yang aneh di sini, karena daftar adalah jenis koleksi tempat Anda mengakses elemen berdasarkan indeksnya. Ini memungkinkan elemen kedua ditukar dengan elemen ketiga. Itu sebabnya metode pengurutan berikut hanya untuk daftar:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
→ Arrays.sort(java.util.Arrays)
Array juga mudah diurutkan. Sekali lagi, untuk alasan yang sama — elemen mereka diakses oleh index. → Descendants of java.util.SortedSet and java.util.SortedMap
Anda akan mengingatnya Set
dan Map
tidak menjamin urutan penyimpanan elemen. NAMUN, kami memiliki implementasi khusus yang menjamin pesanan. Dan jika elemen koleksi tidak mengimplementasikan java.util.Comparable
, maka kita dapat meneruskan a Comparator
ke konstruktornya:
Set<Message> msgSet = new TreeSet(comparator);
→ Stream API
Di Stream API, yang muncul di Java 8, pembanding memungkinkan Anda menyederhanakan pekerjaan dengan elemen aliran. Misalnya, kita membutuhkan urutan angka acak dari 0 hingga 999, termasuk:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
.limit(10)
.sorted(Comparator.naturalOrder())
.forEach(e -> System.out.println(e));
Kita bisa berhenti di sini, tetapi ada masalah yang lebih menarik. Misalnya, Anda perlu menyiapkan Map
, di mana kuncinya adalah id pesan. Selain itu, kami ingin mengurutkan kunci ini, jadi kami akan mulai dengan kode berikut:
Map<Integer, Message> collected = Arrays.stream(messages)
.sorted(Comparator.comparing(msg -> msg.getId()))
.collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Kami benar-benar mendapatkan di HashMap
sini. Dan seperti yang kita tahu, itu tidak menjamin pesanan apa pun. Akibatnya, elemen kami, yang diurutkan berdasarkan id, kehilangan urutannya. Tidak baik. Kami harus sedikit mengubah kolektor kami:
Map<Integer, Message> collected = Arrays.stream(messages)
.sorted(Comparator.comparing(msg -> msg.getId()))
.collect(Collectors.toMap(msg -> msg.getId(), msg -> msg, (oldValue, newValue) -> oldValue, TreeMap::new));
Kode mulai terlihat sedikit lebih menakutkan, tetapi sekarang masalahnya telah diselesaikan dengan benar. Baca lebih lanjut tentang berbagai pengelompokan di sini:
Anda dapat membuat kolektor sendiri. Baca selengkapnya di sini: "Membuat kolektor khusus di Java 8" . Dan Anda akan mendapat manfaat dari membaca diskusi di sini: "Java 8 list to map with stream" .
Jebakan jatuh
Comparator
dan Comparable
baik. Namun ada satu nuansa yang harus Anda ingat. Saat sebuah kelas melakukan penyortiran, kelas Anda diharapkan dapat dikonversi menjadi file Comparable
. Jika tidak demikian, maka Anda akan menerima kesalahan saat dijalankan. Mari kita lihat sebuah contoh:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Sepertinya tidak ada yang salah di sini. Namun pada kenyataannya, dalam contoh kita, itu akan gagal dengan kesalahan: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable
Dan semua karena mencoba untuk mengurutkan elemen (itu adalah SortedSet
, bagaimanapun juga)... tetapi tidak bisa. Jangan lupakan ini saat bekerja dengan SortedMap
dan SortedSet
.
Lebih banyak membaca: |
---|
GO TO FULL VERSION