JavaRush /Blog Java /Random-MS /Pembanding di Jawa

Pembanding di Jawa

Diterbitkan dalam kumpulan
Hanya orang malas yang belum menulis tentang Comparator dan perbandingan di Jawa. Saya tidak malas - jadi saya meminta anda untuk menyukai dan memilih satu lagi variasi. Saya harap ia tidak akan berlebihan. Dan ya, artikel ini adalah jawapan kepada soalan: "Bolehkah anda menulis pembanding dari ingatan?" Saya berharap selepas membaca artikel ini semua orang akan dapat menulis pembanding dari ingatan.
Pembanding dalam Java - 1
Pengenalan Java dikenali sebagai bahasa berorientasikan objek. Akibatnya, di Jawa adalah perkara biasa untuk beroperasi dengan objek. Tetapi lambat laun tugas membandingkan objek mengikut beberapa prinsip timbul. Jadi, diberikan: Kami mempunyai beberapa mesej, yang diterangkan oleh kelas Mesej:
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 pada pengkompil java Tutorialspoint . Mari kita juga ingat untuk menambah import:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
Dalam kaedah utama kami akan mencipta beberapa mesej:
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 fikirkan apa yang patut kita lakukan jika kita ingin membandingkannya? Sebagai contoh, kami ingin mengisih mengikut id. Dan untuk membuat susunan, anda perlu membandingkan objek untuk memahami objek yang sebelumnya (iaitu, lebih kecil) dan yang seterusnya (iaitu, lebih besar). Mari kita mulakan dengan kelas seperti java.lang.Object . Seperti yang kita tahu, semua kelas mewarisi secara tersirat daripada kelas Objek ini. Dan ini adalah logik, kerana Ini pada asasnya menyatakan konsep: "Semuanya adalah objek" dan menyediakan tingkah laku biasa untuk semua kelas. Dan kelas ini mentakrifkan bahawa setiap kelas mempunyai dua kaedah: → hashCode Kaedah hashCode mengembalikan beberapa perwakilan berangka (int) objek sebagai contoh kelas. Apakah maksudnya? Ini bermakna jika anda mencipta dua kejadian kelas yang berbeza, maka memandangkan kejadian itu berbeza, Kod hash mereka harus berbeza. Inilah yang dikatakan dalam perihalan kaedah: "Sebanyak yang munasabah praktikal, kaedah hashCode yang ditakrifkan oleh Objek kelas mengembalikan integer yang berbeza untuk objek yang berbeza" Iaitu, jika ini adalah dua kejadian yang berbeza, maka ia sepatutnya mempunyai yang berbeza. Kod cincang. Maksudnya, kaedah ini tidak sesuai untuk perbandingan kita. → equals Kaedah equals menjawab soalan "adalah objek sama" dan mengembalikan boolean. Kaedah ini mempunyai kod lalai:
public boolean equals(Object obj) {
    return (this == obj);
}
Iaitu, tanpa mengatasi kaedah ini pada objek, kaedah ini pada dasarnya mengatakan sama ada rujukan kepada objek sepadan atau tidak. Ini tidak sesuai untuk mesej kami, kerana kami tidak berminat dengan pautan ke objek, kami berminat dengan id mesej. Dan walaupun kita mengatasi kaedah sama, maksimum yang akan kita perolehi ialah: "Mereka sama" atau "Mereka tidak sama." Tetapi ini tidak mencukupi untuk kita menentukan pesanan.

Pembanding dan Sebanding di Jawa

Apa yang sesuai dengan kita? Jika kita menterjemah perkataan “compare” ke dalam bahasa Inggeris dalam penterjemah, kita akan mendapat terjemahan “compare”. Hebat, maka kita memerlukan seseorang yang akan membandingkan. Jika anda membandingkan perbandingan ini, maka yang membandingkan adalah Pembanding. Mari buka Java Api dan cari Comparator di sana . Dan sememangnya, terdapat antara muka sedemikian - java.util.Comparator java.util.Comparator dan java.lang.Comparable Seperti yang anda lihat, terdapat antara muka sedemikian. Kelas yang melaksanakannya mengatakan bahawa "Saya sedang melaksanakan fungsi untuk membandingkan objek." Satu-satunya perkara yang perlu diingati ialah kontrak pembanding, yang dinyatakan seperti berikut:

Comparator возвращает int по следующей схеме: 
  • отрицательный int (первый an object отрицательный, то есть меньше)
  • положительный int (первый an object положительный, хороший, то есть больший)
  • ноль = an objectы равны
Sekarang mari kita tulis pembanding. Kami perlu mengimport java.util.Comparator . Selepas import, tambahkan kaedah ke utama: Comparator<Message> comparator = new Comparator<Message>(); Sememangnya, ini tidak akan berfungsi, kerana Pembanding ialah antara muka. Oleh itu, selepas kurungan kami akan menambah yang kerinting { }. Dalam kurungan ini kami akan menulis kaedah:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Anda tidak perlu ingat untuk menulis ini. Pembanding ialah orang yang melakukan perbandingan, iaitu membuat perbandingan. Untuk menjawab soalan tentang susunan objek yang dibandingkan, kami kembalikan int. Itu sahaja, sebenarnya. Mudah dan mudah. Seperti yang dapat kita lihat daripada contoh, sebagai tambahan kepada Comparator, terdapat satu lagi antara muka - java.lang.Comparable , melaksanakan yang kita mesti menentukan kaedah compareTo . Antara muka ini mengatakan bahawa "Kelas yang melaksanakan antara muka membolehkan kejadian kelas dibandingkan." Sebagai contoh, pelaksanaan Integer compareTo kelihatan seperti ini:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Bagaimana untuk mengingati semua antara muka ini? Untuk apa? Semuanya datang dari bahasa Inggeris. Bandingkan - untuk membandingkan, orang yang membandingkan ialah Pembanding (sebagai pendaftar, contohnya. Iaitu, orang yang mendaftar), dan kata sifat "dibandingkan" ialah Sebanding. Nah, "Bandingkan dengan" diterjemahkan bukan sahaja sebagai bandingkan dengan, tetapi juga sebagai dibandingkan dengan. Mudah sahaja. Bahasa Jawa ditulis oleh orang yang berbahasa Inggeris, dan dalam menamakan segala-galanya di Jawa, mereka hanya dibimbing oleh bahasa Inggeris dan terdapat beberapa jenis logik dalam penamaan. Dan kaedah compareTo menerangkan bagaimana suatu contoh kelas harus dibandingkan dengan kejadian lain. Sebagai contoh, rentetan dibandingkan secara leksigrafi dan nombor dibandingkan dengan nilai.
Pembanding dalam Java - 2
Java 8 membawa beberapa perubahan yang bagus. Jika kita melihat dengan teliti antara muka Comparator, kita akan melihat bahawa terdapat anotasi di atasnya @FunctionalInterface. Sebenarnya, anotasi ini adalah untuk maklumat dan bermakna antara muka ini berfungsi. Ini bermakna antara muka ini hanya mempunyai 1 kaedah abstrak tanpa pelaksanaan. Apa yang diberikan ini kepada kita? Kita boleh menulis kod pembanding sekarang seperti ini:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Dalam kurungan ialah cara kita menamakan pembolehubah. Java sendiri akan melihatnya kerana... Sekiranya terdapat hanya satu kaedah, maka jelas parameter input yang diperlukan, berapa banyak, dan jenis apa. Seterusnya, kami katakan dengan anak panah bahawa kami ingin memindahkannya ke bahagian kod ini. Di samping itu, terima kasih kepada Java 8, kaedah lalai muncul dalam antara muka - ini adalah kaedah yang muncul secara lalai (secara lalai) apabila kami melaksanakan antara muka. Terdapat beberapa daripada ini dalam antara muka Pembanding. Contohnya:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Terdapat kaedah lain yang akan menjadikan kod anda lebih bersih. Mari lihat contoh di atas, di mana kami menerangkan pembanding kami. Apa yang dia buat? Ia agak primitif. Ia hanya mengambil objek dan mengekstrak beberapa nilai daripadanya yang setanding. Sebagai contoh, Integer melaksanakan setanding, jadi kami dapat melakukan compareTo pada nilai id mesej. Fungsi pembanding mudah ini juga boleh ditulis seperti ini:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Iaitu, secara literal, "Kami mempunyai Pembanding yang membandingkan seperti ini: ia mengambil objek, mendapat Sebanding daripadanya menggunakan kaedah getId(), membandingkan menggunakan compareTo." Dan tiada lagi reka bentuk yang dahsyat. Dan akhirnya, saya ingin ambil perhatian satu lagi ciri. Pembanding boleh dirantai bersama. Sebagai contoh:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Permohonan

Pengisytiharan pembanding ternyata agak logik, bukan? Sekarang kita perlu melihat cara menggunakannya dan di tempat apa. → Collections.sort (java.util.Collections) Sudah tentu, kita boleh mengisih koleksi dengan cara ini. Tetapi bukan semuanya, hanya senarai. Dan tidak ada yang luar biasa di sini, kerana... Ia adalah senarai yang memerlukan akses kepada elemen mengikut indeks. Dan ini membolehkan elemen nombor dua ditukar dengan elemen nombor tiga. Oleh itu, pengisihan dengan cara ini hanya boleh dilakukan untuk senarai:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort (java.util.Arrays) Tatasusunan juga mudah untuk diisih. Sekali lagi, atas sebab yang sama untuk mengakses elemen mengikut indeks. → Keturunan java.util.SortedSet dan java.util.SortedMap Seperti yang kita ingat, Set dan Map tidak menjamin susunan penyimpanan rekod. TETAPI kami mempunyai pelaksanaan khas yang menjamin ketertiban. Dan jika elemen koleksi tidak melaksanakan java.lang.Comparable, maka kita boleh menyerahkan Comparator kepada pembina koleksi tersebut:
Set<Message> msgSet = new TreeSet(comparator);
Stream API Dalam Stream Api, yang muncul dalam Java 8, pembanding membolehkan anda memudahkan kerja pada elemen aliran. Sebagai contoh, kita memerlukan urutan nombor rawak 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 boleh berhenti, tetapi ada masalah yang lebih menarik. Sebagai contoh, anda perlu menyediakan Peta, di mana kuncinya ialah id mesej. Pada masa yang sama, kami ingin mengisih kekunci ini supaya kunci itu tersusun, daripada terkecil kepada terbesar. Mari mulakan dengan kod ini:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
Apa yang kami akan dapatkan kembali di sini sebenarnya ialah HashMap. Dan seperti yang kita tahu, ia tidak menjamin sebarang pesanan. Oleh itu, rekod kami yang diisih mengikut ID hanya tidak terurus. Tidak baik. Kami perlu menukar sedikit pengumpul 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));
Kod itu kelihatan lebih menyeramkan, tetapi masalahnya kini telah diselesaikan dengan betul berkat pelaksanaan TreeMap yang jelas. Anda boleh membaca lebih lanjut mengenai pelbagai kumpulan di sini: Anda boleh membuat pengumpul itu sendiri. Anda boleh membaca lebih lanjut di sini: "Mencipta pengumpul tersuai dalam Java 8" . Dan adalah berguna untuk membaca perbincangan di sini: "Senarai Java 8 untuk dipetakan dengan aliran" .
Pembanding dalam Java - 3
Pemanggang dan Pemakai Sebanding adalah bagus. Tetapi ada satu nuansa yang berkaitan dengan mereka yang patut diingat. Apabila kelas melakukan pengisihan, ia mengira bahawa ia boleh menghantar kelas anda ke Comparable. Jika ini tidak berlaku, anda akan menerima ralat pada masa pelaksanaan. Mari lihat contoh:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Nampaknya tiada yang salah di sini. Tetapi sebenarnya, dalam contoh kami, ia akan ranap dengan ralat: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Dan semua kerana ia cuba mengisih elemen (Ia adalah SortedSet, selepas semua). Dan saya tidak boleh. Anda harus ingat ini apabila bekerja dengan SortedMap dan SortedSet. Selain itu Disyorkan untuk tontonan: Yuri Tkach: HashSet dan TreeSet - Collections #1 - Advanced Java
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION