Menerapkan antarmuka Komparator
Antarmuka Comparator dapat dilihat di mana saja di perpustakaan JDK, mulai dari pencarian, pengurutan, hingga operasi pembalikan, dan seterusnya. Di Java 8, ini menjadi antarmuka fungsional. Keuntungannya adalah kita dapat menggunakan sintaks streaming untuk mengimplementasikan komparator.
Mari terapkan Comparator dalam beberapa cara berbeda untuk melihat nilai sintaksis baru. Jari Anda akan berterima kasih. Tidak harus menerapkan kelas dalam anonim akan menghemat banyak penekanan tombol.
Menyortir menggunakan Komparator
Contoh berikut akan menggunakan metode perbandingan yang berbeda untuk mengurutkan sekelompok orang. Pertama mari kita buat Person JavaBean.
Copy kode kodenya sebagai berikut:
kelas publik Orang {
nama String akhir pribadi;
usia int akhir pribadi;
Orang publik(String terakhir Nama, int akhir Usia) {
nama = Nama;
umur = Umur;
}
String publik getName() { nama kembali; }
public int getAge() { usia kembali; }
public int ageDifference(Orang terakhir lainnya) {
usia kembali - usia lainnya;
}
String publik keString() {
return String.format("%s - %d", nama, umur);
}
}
Kita bisa mengimplementasikan antarmuka Comparator melalui kelas Person, namun dengan cara ini kita hanya bisa menggunakan satu metode perbandingan. Kami ingin dapat membandingkan atribut yang berbeda - seperti nama, usia, atau kombinasi dari semuanya. Untuk melakukan perbandingan secara fleksibel, kita dapat menggunakan Comparator untuk menghasilkan kode yang relevan ketika kita perlu membandingkan.
Pertama mari kita buat daftar Orang, masing-masing dengan nama dan usia berbeda.
Copy kode kodenya sebagai berikut:
Daftar terakhir<Orang> orang = Array.asList(
Orang baru("John", 20),
Orang baru("Sara", 21),
Orang baru("Jane", 21),
Orang baru("Greg", 35));
Kita dapat mengurutkan orang dalam urutan menaik atau menurun berdasarkan nama atau usianya. Metode umumnya adalah dengan menggunakan kelas dalam anonim untuk mengimplementasikan antarmuka Komparator. Jika ditulis seperti ini, hanya kode yang lebih relevan yang bermakna, dan selebihnya hanyalah formalitas. Menggunakan ekspresi lambda dapat fokus pada esensi perbandingan.
Mari kita urutkan terlebih dahulu berdasarkan usia dari yang termuda hingga yang tertua.
Sekarang kita memiliki objek Daftar, kita dapat menggunakan metode sort() untuk mengurutkan. Namun, metode ini juga mempunyai masalah. Ini adalah metode void, artinya ketika kita memanggil metode ini, daftarnya akan berubah. Untuk mempertahankan daftar aslinya, pertama-tama kita harus membuat salinannya lalu memanggil metode sort(). Itu terlalu banyak usaha. Saat ini kita harus beralih ke kelas Stream untuk mendapatkan bantuan.
Kita bisa mendapatkan objek Stream dari Daftar dan kemudian memanggil metode sortir()nya. Ini mengembalikan koleksi yang diurutkan daripada mengubah koleksi asli. Dengan menggunakan metode ini, Anda dapat dengan mudah mengonfigurasi parameter Komparator.
Copy kode kodenya sebagai berikut:
Daftar<Orang> menaikUmur =
orang.aliran()
.sorted((orang1, orang2) -> orang1.ageDifference(orang2))
.collect(toList());
printPeople("Diurutkan berdasarkan umur: ", ascendingAge);
Pertama-tama kita ubah daftarnya menjadi objek Stream melalui metode stream(). Kemudian panggil metode sortir()-nya. Metode ini menerima parameter Komparator. Karena Comparator adalah antarmuka fungsional, kita dapat meneruskan ekspresi lambda. Terakhir, kita memanggil metode pengumpulan dan menyimpannya hasilnya dalam sebuah daftar. Metode pengumpulan adalah peredam yang dapat mengeluarkan objek selama proses iterasi ke dalam format atau tipe tertentu. Metode toList() adalah metode statis dari kelas Collectors.
Metode abstrak bandingkanTo() dari Comparator menerima dua parameter, yaitu objek yang akan dibandingkan, dan mengembalikan hasil bertipe int. Agar kompatibel dengan ini, ekspresi lambda kami juga menerima dua parameter, dua objek Person, yang tipenya secara otomatis disimpulkan oleh kompiler. Kami mengembalikan tipe int yang menunjukkan apakah objek yang dibandingkan sama.
Karena kita ingin mengurutkan berdasarkan umur, kita akan membandingkan umur kedua benda tersebut dan kemudian mengembalikan hasil perbandingannya. Jika ukurannya sama, kembalikan 0. Jika tidak, angka negatif akan dikembalikan jika orang pertama lebih muda, dan angka positif akan dikembalikan jika orang pertama lebih tua.
Metode sortir() akan melintasi setiap elemen koleksi target dan memanggil Pembanding yang ditentukan untuk menentukan urutan elemen. Metode eksekusi metode sortir() agak mirip dengan metode pengurangan() yang disebutkan sebelumnya. Metode pengurangan() secara bertahap mengurangi daftar menjadi sebuah hasil. Metode sortir() mengurutkan berdasarkan hasil perbandingan.
Setelah kita mengurutkannya, kita ingin mencetak hasilnya, jadi kita memanggil metode printPeople(), mari kita terapkan metode ini.
Copy kode kodenya sebagai berikut:
public static void printOrang(
pesan String terakhir, Daftar<Orang> orang terakhir) {
System.out.println(pesan);
people.forEach(System.out::println);
}
Dalam metode ini, pertama-tama kita mencetak pesan, lalu melintasi daftar dan mencetak setiap elemen di dalamnya.
Mari kita panggil metode sortir() untuk melihat bagaimana metode ini akan mengurutkan orang-orang dalam daftar dari yang termuda ke yang tertua.
Copy kode kodenya sebagai berikut:
Diurutkan dalam urutan menaik berdasarkan usia:
Yohanes - 20
Sara - 21
Jane - 21
Greg - 35
Mari kita lihat lagi metode sortir() untuk melakukan perbaikan.
Copy kode kodenya sebagai berikut:
.sorted((orang1, orang2) -> orang1.ageDifference(orang2))
Dalam ekspresi lambda yang diteruskan, kita cukup mengarahkan kedua parameter ini - parameter pertama digunakan sebagai target panggilan metode ageDifference(), dan parameter kedua digunakan sebagai parameternya. Tapi kita tidak bisa menulisnya seperti ini, tapi menggunakan mode ruang kantor-yaitu, menggunakan referensi metode dan membiarkan compiler Java melakukan routing.
Perutean parameter yang digunakan di sini sedikit berbeda dari yang kita lihat sebelumnya. Kita telah melihat sebelumnya bahwa argumen diteruskan sebagai target panggilan atau sebagai parameter panggilan. Sekarang, kita mempunyai dua parameter, dan kita ingin membaginya menjadi dua bagian, satu sebagai target pemanggilan metode, dan yang kedua sebagai parameter. Jangan khawatir, kompiler Java akan memberi tahu Anda, "Saya akan mengurus ini."
Kita dapat mengganti ekspresi lambda pada metode sortir() sebelumnya dengan metode ageDifference yang singkat dan ringkas.
Copy kode kodenya sebagai berikut:
orang.aliran()
.sorted(Orang::ageDifference)
Kode ini sangat ringkas, berkat referensi metode yang disediakan oleh kompiler Java. Kompiler menerima parameter instance dua orang dan menggunakan yang pertama sebagai target metode ageDifference() dan yang kedua sebagai parameter metode. Kami membiarkan kompiler melakukan pekerjaan ini daripada menulis kode secara langsung. Saat menggunakan metode ini, kita harus memastikan bahwa parameter pertama adalah target pemanggilan metode yang direferensikan, dan parameter sisanya adalah parameter masukan metode tersebut.
Gunakan kembali Komparator
Sangat mudah untuk mengurutkan orang-orang dalam daftar dari yang termuda ke yang tertua, dan juga mudah untuk mengurutkan dari yang tertua ke yang termuda. Mari kita mencobanya.
Copy kode kodenya sebagai berikut:
printPeople("Diurutkan berdasarkan umur: ",
orang.aliran()
.sorted((orang1, orang2) -> orang2.ageDifference(orang1))
.collect(toList()));
Kita memanggil metode sortir() dan meneruskan ekspresi lambda, yang cocok dengan antarmuka Comparator, seperti pada contoh sebelumnya. Satu-satunya perbedaan adalah penerapan ekspresi lambda ini - kami telah mengubah urutan orang yang akan dibandingkan. Hasilnya harus diurutkan dari yang tertua hingga termuda berdasarkan usianya. Mari kita lihat.
Copy kode kodenya sebagai berikut:
Diurutkan dalam urutan menurun berdasarkan usia:
Greg - 35
Sara - 21
Jane - 21
Yohanes - 20
Tidak perlu banyak usaha untuk sekadar mengubah logika perbandingan. Namun kami tidak dapat merekonstruksi versi ini menjadi referensi metode karena urutan parameter tidak sesuai dengan aturan perutean parameter untuk referensi metode; parameter pertama tidak digunakan sebagai target pemanggilan metode, tetapi sebagai parameter metode. Ada cara untuk mengatasi masalah ini yang juga mengurangi duplikasi upaya. Mari kita lihat cara melakukannya.
Kami telah membuat dua ekspresi lambda sebelumnya: satu untuk mengurutkan berdasarkan usia dari kecil ke besar, dan yang lainnya adalah mengurutkan dari besar ke kecil. Melakukan hal ini akan menyebabkan redundansi dan duplikasi kode, serta melanggar prinsip KERING. Jika kita hanya ingin mengatur urutan pengurutan, JDK menyediakan metode sebaliknya, yang memiliki pengubah metode khusus, default. Kita akan membahasnya pada metode default di halaman 77. Disini pertama-tama kita menggunakan metode reversed() untuk menghilangkan redundansi.
Copy kode kodenya sebagai berikut:
Pembanding<Orang> bandingkanAscending =
(orang1, orang2) -> orang1.ageDifference(orang2);
Pembanding<Orang> bandingkanDescending = bandingkanAscending.reversed();
Kami pertama kali membuat Komparator, bandingkanAscending, untuk mengurutkan orang berdasarkan usia dari yang termuda hingga yang tertua. Untuk membalikkan urutan perbandingan, daripada menulis kode ini lagi, kita hanya perlu memanggil metode reversed() dari Comparator pertama untuk mendapatkan objek Comparator kedua. Di bawah tenda metode reversed(), ini menciptakan komparator untuk membalikkan urutan parameter yang dibandingkan. Hal ini menunjukkan bahwa reversed juga merupakan metode tingkat tinggi - metode ini membuat dan mengembalikan fungsi tanpa efek samping. Kami menggunakan dua pembanding ini dalam kode.
Copy kode kodenya sebagai berikut:
printPeople("Diurutkan berdasarkan umur: ",
orang.aliran()
.sorted(bandingkanAscending)
.collect(keDaftar())
);
printPeople("Diurutkan berdasarkan umur: ",
orang.aliran()
.sorted(bandingkanMenurun)
.collect(keDaftar())
);
Terlihat jelas dari kodenya bahwa fitur-fitur baru Java8 ini telah sangat mengurangi redundansi dan kompleksitas kode, namun manfaatnya jauh lebih dari ini. Ada kemungkinan tak terbatas yang menunggu untuk Anda jelajahi di JDK.
Kita sudah bisa mengurutkannya berdasarkan umur, dan juga mudah untuk mengurutkan berdasarkan nama. Mari kita urutkan secara leksikografis berdasarkan nama. Demikian pula, kita hanya perlu mengubah logika dalam ekspresi lambda.
Copy kode kodenya sebagai berikut:
printPeople("Urutan menaik berdasarkan nama: ",
orang.aliran()
.sorted((orang1, orang2) ->
person1.getName().compareTo(person2.getName()))
.collect(toList()));
Hasil keluarannya akan diurutkan secara leksikografis berdasarkan nama.
Copy kode kodenya sebagai berikut:
Diurutkan dalam urutan menaik berdasarkan nama:
Greg - 35
Jane - 21
Yohanes - 20
Sara - 21
Sejauh ini, kami telah mengurutkannya berdasarkan usia atau nama. Kita bisa membuat logika ekspresi lambda lebih cerdas. Misalnya, kita dapat mengurutkan berdasarkan umur dan nama secara bersamaan.
Mari kita pilih orang termuda dalam daftar. Pertama-tama kita dapat mengurutkan berdasarkan usia dari yang terkecil hingga yang terbesar, lalu memilih yang pertama dari hasilnya. Tapi itu tidak benar-benar berhasil. Stream memiliki metode min() untuk mencapai ini. Metode ini juga menerima Komparator, namun mengembalikan objek terkecil dalam koleksi. Mari kita gunakan.
Copy kode kodenya sebagai berikut:
orang.aliran()
.min(Orang::ageDifference)
.ifPresent(yang termuda -> System.out.println("Yang termuda: " + termuda));
Saat memanggil metode min(), kami menggunakan referensi metode ageDifference. Metode min() mengembalikan objek Optinal karena daftarnya mungkin kosong dan mungkin terdapat lebih dari satu orang termuda di dalamnya. Kemudian kita mendapatkan orang termuda melalui metode ifPrsend() Optinal dan mencetak informasi detailnya. Mari kita lihat hasilnya.
Copy kode kodenya sebagai berikut:
Bungsu: John - 20
Mengekspor yang terlama juga sangat sederhana. Cukup sampaikan referensi metode ini ke metode max().
Copy kode kodenya sebagai berikut:
orang.aliran()
.max(Orang::ageDifference)
.ifPresent(yang tertua -> System.out.println("Yang tertua: " + yang tertua));
Mari kita lihat nama dan umur si sulung.
Copy kode kodenya sebagai berikut:
Tertua: Greg - 35
Dengan ekspresi lambda dan referensi metode, implementasi komparator menjadi lebih sederhana dan nyaman. JDK juga memperkenalkan banyak metode mudah ke kelas Compararor, memungkinkan kita membandingkan dengan lebih lancar, seperti yang akan kita lihat di bawah.
Berbagai perbandingan dan perbandingan streaming
Mari kita lihat metode baru yang mudah digunakan yang disediakan oleh antarmuka Comparator dan menggunakannya untuk membandingkan beberapa properti.
Mari lanjutkan menggunakan contoh dari bagian sebelumnya. Urutkan berdasarkan nama, ini yang kami tulis di atas:
Copy kode kodenya sebagai berikut:
orang.aliran()
.sorted((orang1, orang2) ->
person1.getName().compareTo(person2.getName()));
Dibandingkan dengan metode penulisan kelas dalam pada abad yang lalu, metode penulisan ini terlalu sederhana. Namun, hal ini dapat dibuat lebih sederhana jika kita menggunakan beberapa fungsi di kelas Comparator. Menggunakan fungsi-fungsi ini dapat memungkinkan kita untuk mengekspresikan tujuan kita dengan lebih lancar. Misalnya, jika kita ingin mengurutkan berdasarkan nama, kita dapat menulis:
Copy kode kodenya sebagai berikut:
Fungsi akhir<Orang, String> byName = orang -> orang.getName();
orang.aliran()
.sorted(membandingkan(berdasarkanNama));
Dalam kode ini kita telah mengimpor metode statis yang membandingkan () dari kelas Komparator. Metode Compare() menggunakan ekspresi lambda yang diteruskan untuk menghasilkan objek Comparator. Dengan kata lain, ini juga merupakan fungsi tingkat tinggi yang menerima suatu fungsi sebagai parameter masukan dan mengembalikan fungsi lainnya. Selain membuat sintaksisnya lebih ringkas, kode tersebut juga dapat mengekspresikan masalah sebenarnya yang ingin kita selesaikan dengan lebih baik.
Dengan itu, banyak perbandingan bisa menjadi lebih lancar. Misalnya, kode berikut yang membandingkan berdasarkan nama dan usia menjelaskan semuanya:
Copy kode kodenya sebagai berikut:
Fungsi akhir<Orang, Integer> berdasarkanUmur = orang -> orang.getAge();
Fungsi akhir<Orang, String> byTheirName = orang -> orang.getName();
printPeople("Diurutkan berdasarkan umur dan nama: ",
orang.aliran()
.sorted(membandingkan(menurut Usia).laluMembandingkan(menurutNamaMereka))
.collect(toList()));
Kami pertama kali membuat dua ekspresi lambda, satu mengembalikan usia orang yang ditentukan, dan yang lainnya mengembalikan namanya. Saat memanggil metode sortir(), kami menggabungkan kedua ekspresi ini sehingga beberapa atribut dapat dibandingkan. Metode Compare() membuat dan mengembalikan Comparator berdasarkan usia. Lalu kita memanggil metode ThenComparing() pada Comparator yang dikembalikan untuk membuat komparator gabungan yang membandingkan usia dan nama. Output di bawah ini merupakan hasil pengurutan terlebih dahulu berdasarkan umur, kemudian berdasarkan nama.
Copy kode kodenya sebagai berikut:
Diurutkan dalam urutan menaik berdasarkan usia dan nama:
Yohanes - 20
Jane - 21
Sara - 21
Greg - 35
Seperti yang Anda lihat, implementasi Comparator dapat dengan mudah digabungkan menggunakan ekspresi lambda dan kelas alat baru yang disediakan oleh JDK. Mari perkenalkan Kolektor di bawah ini.