CodeGym/Java Blog/Acak/Antarmuka Komparator Java
John Squirrels
Level 41
San Francisco

Antarmuka Komparator Java

Dipublikasikan di grup Acak
anggota
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. Antarmuka Komparator Javas - 1

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 oleh Messagekelas:
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 mainmetode 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 Objectkelas 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 hashCodemengembalikan beberapa numerik (int) representasi objek. Maksudnya itu apa? Ini berarti bahwa jika Anda membuat dua instance kelas yang berbeda, maka mereka harus memiliki hashCodes 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 instances yang berbeda, harus ada hashCodes yang berbeda. Artinya, metode ini tidak cocok untuk perbandingan kita. → equals. Metode tersebut equalsmenjawab 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 equalsmetodenya, 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.Comparatorantarmuka 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 mainmetode: Comparator<Message> comparator = new Comparator<Message>(); Tentu saja, ini tidak akan berfungsi, karena Comparatormerupakan 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.Comparableyang mengharuskan kita untuk mengimplementasikan compareTometode ini. Antarmuka ini mengatakan, "kelas yang mengimplementasikan saya memungkinkan untuk membandingkan contoh kelas." Sebagai contoh, Integerpenerapan To compareadalah sebagai berikut:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 memperkenalkan beberapa perubahan yang menyenangkan. Jika Anda melihat lebih dekat pada Comparatorantarmuka, Anda akan melihat @FunctionalInterfaceanotasi 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 Comparatormemiliki 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, Integerimplements 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 Comparatoryang membandingkan seperti ini: mengambil objek, menggunakan getId()metode untuk mendapatkan a Comparabledarinya, lalu menggunakan compareTountuk 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 Setdan Maptidak 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 Comparatorke 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 HashMapsini. 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

Comparatordan Comparablebaik. 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 SortedMapdan SortedSet.

Lebih banyak membaca:

Komentar
  • Populer
  • Baru
  • Lama
Anda harus login untuk memberikan komentar
Halaman ini belum memiliki komentar