JavaRush /Java Blog /Random-ID /Komparator di Jawa
Viacheslav
Level 3

Komparator di Jawa

Dipublikasikan di grup Random-ID
Hanya orang malas yang belum menulis tentang Komparator dan perbandingan di Java. Saya tidak malas - jadi saya meminta Anda untuk menyukai dan menyukai satu variasi lagi. Saya harap itu tidak berlebihan. Dan ya, artikel ini adalah jawaban atas pertanyaan: “Bisakah Anda menulis pembanding dari ingatan?” Saya berharap setelah membaca artikel ini semua orang dapat menulis pembanding dari ingatan.
Pembanding di Java - 1
Pendahuluan Java dikenal sebagai bahasa berorientasi objek. Oleh karena itu, di Java, pengoperasian objek adalah hal yang umum. Namun cepat atau lambat muncul tugas membandingkan objek menurut prinsip tertentu. Jadi, diketahui: Kita mempunyai beberapa pesan, yang dijelaskan oleh kelas Message:
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;
    }
}
Mari tambahkan kelas ini ke kompiler java Tutorialspoint . Ingatlah juga untuk menambahkan impor:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Dalam metode utama kita akan membuat 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 harus kita lakukan jika ingin membandingkannya? Misalnya kita ingin mengurutkan berdasarkan id. Dan untuk menciptakan keteraturan, Anda perlu membandingkan objek untuk memahami objek mana yang sebelumnya (yaitu lebih kecil) dan mana yang berikutnya (yaitu lebih besar). Mari kita mulai dengan kelas seperti java.lang.Object . Seperti yang kita ketahui, semua kelas mewarisi secara implisit dari kelas Object ini. Dan ini logis, karena Ini pada dasarnya mengungkapkan konsep: "Segala sesuatu adalah sebuah objek" dan memberikan perilaku umum untuk semua kelas. Dan kelas ini mendefinisikan bahwa setiap kelas memiliki dua metode: → kode hash Metode kode hash mengembalikan beberapa representasi numerik (int) dari objek sebagai turunan dari kelas. Apa artinya? Artinya jika Anda membuat dua instance kelas yang berbeda, maka karena instance tersebut berbeda, kode hashnya juga harus berbeda. Inilah yang dikatakan dalam deskripsi metodenya: “Sebisa mungkin, metode hashCode yang didefinisikan oleh kelas Object akan mengembalikan bilangan bulat yang berbeda untuk objek yang berbeda” Artinya, jika ini adalah dua contoh yang berbeda, maka keduanya harus mempunyai nilai yang berbeda. kode hash. Artinya, metode ini tidak cocok untuk perbandingan kami. → sama dengan Metode sama menjawab pertanyaan “apakah objek sama” dan mengembalikan boolean. Metode ini memiliki kode default:
public boolean equals(Object obj) {
    return (this == obj);
}
Artinya, tanpa mengesampingkan metode ini pada suatu objek, metode ini pada dasarnya menyatakan apakah referensi ke objek tersebut cocok atau tidak. Ini tidak cocok untuk pesan kami, karena kami tidak tertarik pada tautan ke objek, kami tertarik pada id pesan. Dan bahkan jika kita mengganti metode sama dengan, hasil maksimal yang kita peroleh adalah: “Mereka sama” atau “Mereka tidak sama.” Namun ini tidak cukup bagi kami untuk menentukan urutannya.

Pembanding dan Pembanding di Java

Apa yang cocok untuk kita? Jika kita menerjemahkan kata “bandingkan” ke dalam bahasa Inggris di penerjemah, kita akan mendapatkan terjemahan “bandingkan”. Bagus, kalau begitu kita butuh seseorang yang bisa membandingkan. Jika membandingkan perbandingan ini, maka yang membandingkan adalah Pembanding. Mari kita buka Java Api dan temukan Comparator di sana . Dan memang, ada antarmuka seperti itu - java.util.Comparator java.util.Comparator dan java.lang.Comparable Seperti yang Anda lihat, ada antarmuka seperti itu. Kelas yang mengimplementasikannya mengatakan bahwa “Saya mengimplementasikan fungsi untuk membandingkan objek.” Satu-satunya hal yang perlu diingat adalah kontrak pembanding, yang dinyatakan sebagai berikut:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
Sekarang mari kita menulis pembanding. Kita perlu mengimpor java.util.Comparator . Setelah mengimpor, tambahkan metode ke main: Comparator<Message> comparator = new Comparator<Message>(); Tentu saja, ini tidak akan berhasil, karena Komparator adalah sebuah antarmuka. Oleh karena itu, setelah tanda kurung kita akan menambahkan yang keriting { }. Dalam tanda kurung ini kita akan menulis metodenya:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Anda bahkan tidak perlu ingat untuk menulis ini. Pembanding adalah orang yang melakukan perbandingan, yaitu membuat perbandingan. Untuk menjawab pertanyaan tentang urutan objek yang dibandingkan, kita kembalikan int. Sebenarnya itu saja. Sederhana dan mudah. Seperti yang dapat kita lihat dari contoh, selain Comparator, ada antarmuka lain - java.lang.Comparable , yang implementasinya harus kita definisikan metode bandingkanTo . Antarmuka ini menyatakan bahwa "Kelas yang mengimplementasikan antarmuka memungkinkan instance kelas tersebut dibandingkan." Misalnya, implementasi CompareTo pada Integer terlihat seperti ini:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Bagaimana cara mengingat semua antarmuka ini? Untuk apa? Semuanya berasal dari bahasa Inggris. Bandingkan - membandingkan, yang membandingkan adalah Comparator (sebagai pencatat, misalnya, yaitu yang mendaftar), dan kata sifat “dibandingkan” adalah Comparable. Nah, “Bandingkan dengan” diterjemahkan tidak hanya sebagai membandingkan dengan, tetapi juga sebagai membandingkan dengan. Itu mudah. Bahasa Jawa ditulis oleh orang yang berbahasa Inggris, dan dalam penamaan segala sesuatu yang ada di Jawa mereka hanya berpedoman pada bahasa Inggris dan ada semacam logika dalam penamaannya. Dan metode bandingkanTo menjelaskan bagaimana sebuah instance dari suatu kelas harus dibandingkan dengan instance lainnya. Misalnya, string dibandingkan secara leksigrafik , dan angka dibandingkan berdasarkan nilai.
Pembanding di Java - 2
Java 8 membawa beberapa perubahan bagus. Jika kita perhatikan lebih dekat pada antarmuka Comparator, kita akan melihat ada anotasi di atasnya @FunctionalInterface. Faktanya, anotasi ini hanya untuk informasi dan berarti antarmuka ini berfungsi. Artinya antarmuka ini hanya memiliki 1 metode abstrak tanpa implementasi. Apa manfaatnya bagi kita? Kita dapat menulis kode pembanding sekarang seperti ini:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Dalam tanda kurung adalah cara kita memberi nama variabel. Java sendiri akan melihatnya karena... Jika metodenya hanya satu, maka sudah jelas parameter input apa yang dibutuhkan, berapa banyak, dan jenisnya apa. Selanjutnya, kita katakan dengan panah bahwa kita ingin memindahkannya ke bagian kode ini. Selain itu, berkat Java 8, metode default muncul di antarmuka - ini adalah metode yang muncul secara default (secara default) saat kita mengimplementasikan antarmuka. Ada beberapa di antaranya di antarmuka Comparator. Misalnya:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Ada metode lain yang akan membuat kode Anda lebih bersih. Mari kita lihat contoh di atas, dimana kami mendeskripsikan komparator kami. Apa yang dia lakukan? Itu cukup primitif. Ini hanya mengambil sebuah objek dan mengekstrak beberapa nilai yang sebanding darinya. Misalnya, Integer mengimplementasikan perbandingan, sehingga kami dapat melakukan perbandinganTo pada nilai id pesan. Fungsi komparator sederhana ini juga dapat ditulis seperti ini:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Artinya, secara harafiah, “Kami memiliki Komparator yang membandingkan seperti ini: mengambil objek, mendapatkan Comparable dari objek tersebut menggunakan metode getId(), membandingkan menggunakan CompareTo.” Dan tidak ada lagi desain yang buruk. Dan terakhir, saya ingin mencatat satu fitur lagi. Pembanding dapat dirangkai bersama. Misalnya:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Aplikasi

Deklarasi pembanding ternyata cukup logis bukan? Sekarang kita perlu melihat bagaimana menggunakannya dan di tempat apa saja. → Collections.sort (java.util.Collections) Tentu saja kita bisa mengurutkan koleksi dengan cara ini. Tapi tidak semuanya, hanya daftar. Dan tidak ada yang aneh di sini, karena... Ini adalah daftar yang memerlukan akses ke elemen berdasarkan indeks. Dan ini memungkinkan elemen nomor dua ditukar dengan elemen nomor tiga. Oleh karena itu, pengurutan dengan cara ini hanya dapat dilakukan untuk daftar:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Array juga mudah untuk diurutkan. Sekali lagi, untuk alasan yang sama dalam mengakses elemen berdasarkan indeks. → Keturunan dari java.util.SortedSet dan java.util.SortedMap Seperti yang kita ingat, Set dan Map tidak menjamin urutan penyimpanan record. TAPI kami memiliki implementasi khusus yang menjamin ketertiban. Dan jika elemen koleksi tidak mengimplementasikan java.lang.Comparable, maka kita dapat meneruskan Comparator ke konstruktor koleksi tersebut:
Set<Message> msgSet = new TreeSet(comparator);
Stream API Di Stream Api, yang muncul di Java 8, komparator memungkinkan Anda menyederhanakan pekerjaan pada elemen stream. Misalnya, kita memerlukan rangkaian angka acak dari 0 hingga 999 inklusif:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Kita bisa berhenti, tapi ada masalah yang lebih menarik. Misalnya, Anda perlu menyiapkan Peta, yang kuncinya adalah id pesan. Pada saat yang sama, kami ingin mengurutkan kunci-kunci ini sehingga kunci-kunci tersebut berurutan, dari yang terkecil hingga yang terbesar. Mari kita mulai dengan kode ini:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Apa yang akan kita dapatkan kembali di sini sebenarnya adalah HashMap. Dan seperti yang kita ketahui, itu tidak menjamin pesanan apa pun. Oleh karena itu, catatan kami yang diurutkan berdasarkan ID menjadi tidak berurutan. Tidak baik. Kita harus mengubah sedikit kolektor kita:
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));
Kodenya tampak sedikit lebih menyeramkan, tetapi masalahnya sekarang telah diselesaikan dengan benar berkat implementasi TreeMap yang eksplisit. Anda dapat membaca lebih lanjut tentang berbagai grup di sini: Anda dapat membuat kolektor sendiri. Anda dapat membaca lebih lanjut di sini: "Membuat kolektor khusus di Java 8" . Dan bermanfaat untuk membaca diskusi di sini: "Daftar Java 8 untuk dipetakan dengan aliran" .
Pembanding di Java - 3
Penggaruk Komparator dan Penggaruk Sebanding bagus. Namun ada satu nuansa terkait dengannya yang patut diingat. Saat sebuah kelas melakukan pengurutan, ia menghitung bahwa kelas tersebut dapat memasukkan kelas Anda ke Sebanding. Jika tidak, Anda akan menerima kesalahan pada waktu eksekusi. Mari kita lihat sebuah contoh:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Sepertinya tidak ada yang salah disini. Namun pada kenyataannya, dalam contoh kita, ia akan crash dengan kesalahan: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Dan semua itu karena ia mencoba mengurutkan elemen (bagaimanapun juga, ini adalah SortedSet). Dan saya tidak bisa. Anda harus mengingat ini ketika bekerja dengan SortedMap dan SortedSet. Selain itu Direkomendasikan untuk dilihat: Yuri Tkach: HashSet dan TreeSet - Koleksi #1 - Java Tingkat Lanjut
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION