Perkenalan
Ekspresi Lambda adalah fitur baru yang penting di Java SE 8. Ekspresi Lambda memungkinkan Anda mengganti antarmuka fungsional dengan ekspresi. Ekspresi lambda sama seperti metode, ia menyediakan daftar parameter normal dan isi (yang bisa berupa ekspresi atau blok kode) yang menggunakan parameter ini.
Ekspresi Lambda juga menyempurnakan perpustakaan koleksi. Java SE 8 menambahkan dua paket untuk operasi batch pada pengumpulan data: paket java.util.function dan paket java.util.stream. Aliran sama seperti iterator, tetapi dengan banyak fitur tambahan. Secara keseluruhan, ekspresi dan aliran lambda adalah perubahan terbesar sejak penambahan generik dan anotasi ke bahasa Java. Pada artikel ini, kita akan melihat kekuatan ekspresi dan aliran lambda dari contoh sederhana hingga contoh kompleks.
Persiapan lingkungan
Jika Java 8 belum terinstal, Anda harus menginstalnya terlebih dahulu sebelum Anda dapat menggunakan lambda dan stream (penerjemah menyarankan untuk menginstalnya di mesin virtual untuk pengujian). Alat dan IDE seperti NetBeans dan IntelliJ IDEA mendukung fitur Java 8, termasuk ekspresi lambda, anotasi berulang, profil ringkas, dan fitur lainnya.
Sintaks ekspresi Lambda
Sintaks dasar:
(parameter) -> ekspresi
atau
(parameter) ->{ pernyataan;
Berikut adalah contoh sederhana ekspresi lambda Java:
Copy kode kodenya sebagai berikut:
// 1. Tidak diperlukan parameter, nilai yang dikembalikan adalah 5
() -> 5
// 2. Menerima parameter (tipe numerik) dan mengembalikan 2 kali nilainya
x -> 2*x
// 3. Terima 2 parameter (angka) dan kembalikan selisihnya
(x, y) -> xy
// 4. Terima 2 bilangan bulat tipe int dan kembalikan jumlahnya
(int x, int y) -> x + y
// 5. Terima objek string dan cetak di konsol tanpa mengembalikan nilai apa pun (sepertinya mengembalikan batal)
(String s) -> System.out.print(s)
Contoh dasar Lambda
Sekarang setelah kita mengetahui apa itu ekspresi lambda, mari kita mulai dengan beberapa contoh dasar. Di bagian ini, kita akan melihat bagaimana ekspresi lambda memengaruhi cara kita membuat kode. Misalkan ada Daftar pemain, programmer dapat menggunakan pernyataan for ("for loop") untuk melintasi, yang dapat dikonversi ke bentuk lain di Java SE 8:
Copy kode kodenya sebagai berikut:
String[] atp = {"Rafael Nadal", "Novak Djokovic",
Stanislas Wawrinka,
"David Ferrer", "Roger Federer",
"Andy Murray", "Tomas Berdych",
"Juan Martin Del Potro"};
Daftar<String> pemain = Arrays.asList(atp);
//Metode loop sebelumnya
untuk (Pemain dawai : pemain) {
Sistem.keluar.cetak(pemain + "; ");
}
//Gunakan ekspresi lambda dan operasi fungsional
pemain.forEach((pemain) -> System.out.print(pemain + "; "));
//Gunakan operator titik dua di Java 8
pemain.forEach(System.out::println);
Seperti yang Anda lihat, ekspresi lambda dapat mengurangi kode kita menjadi satu baris. Contoh lain adalah dalam program antarmuka pengguna grafis dimana kelas anonim dapat digantikan dengan ekspresi lambda. Demikian pula, dapat juga digunakan seperti ini ketika mengimplementasikan antarmuka Runnable:
Copy kode kodenya sebagai berikut:
//Gunakan kelas dalam anonim
btn.setOnAction(EventHandler baru<ActionEvent>() {
@Mengesampingkan
pegangan kekosongan publik(acara ActionEvent) {
System.out.println("Halo Dunia!");
}
});
// Atau gunakan ekspresi lambda
btn.setOnAction(acara -> System.out.println("Halo Dunia!"));
Berikut ini adalah contoh penggunaan lambda untuk mengimplementasikan antarmuka Runnable:
Copy kode kodenya sebagai berikut:
// 1.1 Gunakan kelas dalam anonim
Utas baru(yang baru Dapat Dijalankan() {
@Mengesampingkan
menjalankan kekosongan publik() {
System.out.println("Halo dunia!");
}
}).awal();
// 1.2 Gunakan ekspresi lambda
Thread baru(() -> System.out.println("Halo dunia!")).start();
// 2.1 Gunakan kelas dalam anonim
Race1 yang dapat dijalankan = Runnable baru() {
@Mengesampingkan
menjalankan kekosongan publik() {
System.out.println("Halo dunia!");
}
};
// 2.2 Gunakan ekspresi lambda
Race2 yang dapat dijalankan = () -> System.out.println("Halo dunia!");
// Panggil metode run secara langsung (tidak ada thread baru yang dibuka!)
balapan1.lari();
balapan2.lari();
Ekspresi lambda Runnable menggunakan format blok untuk mengubah lima baris kode menjadi satu pernyataan baris. Selanjutnya, di bagian selanjutnya kita akan menggunakan lambda untuk mengurutkan koleksi.
Menyortir koleksi menggunakan Lambdas
Di Java, kelas Komparator digunakan untuk mengurutkan koleksi. Pada contoh di bawah ini, kita akan berdasarkan nama pemain, nama belakang, panjang nama dan huruf terakhir. Seperti pada contoh sebelumnya, pertama-tama kita menggunakan kelas dalam anonim untuk mengurutkan, lalu menggunakan ekspresi lambda untuk menyederhanakan kode kita.
Pada contoh pertama, kita akan mengurutkan daftar berdasarkan nama. Dengan menggunakan cara lama, kodenya akan terlihat seperti ini:
Copy kode kodenya sebagai berikut:
String[] pemain = {"Rafael Nadal", "Novak Djokovic",
Stanislas Wawrinka, David Ferrer,
"Roger Federer", "Andi Murray",
"Tomas Berdych", "Juan Martin Del Potro",
"Richard Gasquet", "John Isner"};
// 1.1 Gunakan kelas dalam anonim untuk mengurutkan pemain berdasarkan nama
Arrays.sort(pemain, Pembanding baru<String>() {
@Mengesampingkan
perbandingan int publik(String s1, String s2) {
kembali (s1.compareTo(s2));
}
});
Menggunakan lambdas, fungsi yang sama dapat dicapai dengan kode berikut:
Copy kode kodenya sebagai berikut:
// 1.2 Gunakan ekspresi lambda untuk mengurutkan pemain
Pembanding<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(pemain, sortByName);
// 1.3 juga dapat mengambil bentuk berikut:
Array.sort(pemain, (String s1, String s2) -> (s1.compareTo(s2)));
Pemeringkatan lainnya adalah sebagai berikut. Seperti contoh di atas, kode mengimplementasikan Comparator melalui inner class anonim dan beberapa ekspresi lambda:
Copy kode kodenya sebagai berikut:
// 1.1 Gunakan kelas dalam anonim untuk mengurutkan pemain berdasarkan nama belakang
Arrays.sort(pemain, Pembanding baru<String>() {
@Mengesampingkan
perbandingan int publik(String s1, String s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
}
});
// 1.2 Gunakan ekspresi lambda untuk mengurutkan berdasarkan nama keluarga
Pembanding<String> sortBySurname = (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) );
Arrays.sort(pemain, sortBySurname);
// 1.3 Atau seperti ini, saya penasaran apakah penulis aslinya melakukan kesalahan, tanda kurungnya banyak sekali...
Array.sort(pemain, (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )
);
// 2.1 Gunakan kelas dalam anonim untuk mengurutkan pemain berdasarkan panjang nama
Arrays.sort(pemain, Pembanding baru<String>() {
@Mengesampingkan
perbandingan int publik(String s1, String s2) {
kembali (s1.panjang() - s2.panjang());
}
});
// 2.2 Gunakan ekspresi lambda untuk mengurutkan berdasarkan panjang nama
Pembanding<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(pemain, sortByNameLenght);
// 2.3 atau ini
Array.sort(pemain, (String s1, String s2) -> (s1.length() - s2.length()));
// 3.1 Gunakan inner class anonim untuk mengurutkan pemain berdasarkan huruf terakhir
Arrays.sort(pemain, Pembanding baru<String>() {
@Mengesampingkan
perbandingan int publik(String s1, String s2) {
kembali (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 Gunakan ekspresi lambda untuk mengurutkan berdasarkan huruf terakhir
Pembanding<String> sortByLastLetter =
(Tali s1, Tali s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(pemain, sortByLastLetter);
// 3.3 atau ini
Array.sort(pemain, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
Itu saja, sederhana dan intuitif. Di bagian selanjutnya kita akan menjelajahi lebih banyak kemampuan lambda dan menggunakannya dengan stream.
Menggunakan Lambdas dan Stream
Stream adalah pembungkus koleksi dan biasanya digunakan bersama dengan lambda. Menggunakan lambda dapat mendukung banyak operasi, seperti peta, filter, batas, pengurutan, penghitungan, min, maks, penjumlahan, pengumpulan, dll. Demikian pula, Streams menggunakan operasi lambat, mereka tidak benar-benar membaca semua data, dan sintaksis rantai berakhir ketika menemukan metode seperti getFirst(). Dalam contoh berikut, kita akan menjelajahi apa yang bisa dilakukan lambda dan stream. Kami membuat kelas Person dan menggunakan kelas ini untuk menambahkan beberapa data ke daftar, yang akan digunakan untuk operasi streaming lebih lanjut. Person hanyalah kelas POJO sederhana:
Copy kode kodenya sebagai berikut:
kelas publik Orang {
String pribadi Nama Depan, Nama Belakang, pekerjaan, jenis kelamin;
gaji int swasta, usia;
Orang publik (String Nama Depan, String Nama Belakang, Pekerjaan String,
String jenis kelamin, usia int, gaji int) {
ini.NamaDepan = NamaDepan;
ini.NamaBelakang = NamaBelakang;
this.gender = jenis kelamin;
this.usia = usia;
this.pekerjaan = pekerjaan;
this.salary = gaji;
}
// Pengambil dan Penyetel
// .
}
Selanjutnya, kita akan membuat dua daftar, keduanya digunakan untuk menyimpan objek Person:
Copy kode kodenya sebagai berikut:
Daftar<Orang> javaProgrammer = Daftar Array baru<Orang>() {
{
tambahkan(Orang baru("Elsdon", "Jaycob", "Pemrogram Java", "pria", 43, 2000));
tambahkan(Orang baru("Tamsen", "Brittany", "Pemrogram Java", "perempuan", 23, 1500));
tambahkan(Orang baru("Floyd", "Donny", "Pemrogram Java", "pria", 33, 1800));
tambahkan(Orang baru("Sindy", "Jonie", "Pemrogram Java", "perempuan", 32, 1600));
tambahkan(Orang baru("Vere", "Hervey", "Pemrogram Java", "pria", 22, 1200));
tambahkan(Orang baru("Maude", "Jaimie", "Pemrogram Java", "perempuan", 27, 1900));
tambahkan(Orang baru("Shawn", "Randall", "Pemrogram Java", "pria", 30, 2300));
tambahkan(Orang baru("Jayden", "Corrina", "Pemrogram Java", "perempuan", 35, 1700));
add(Orang baru("Palmer", "Dene", "Java programmer", "male", 33, 2000));
tambahkan(Orang baru("Addison", "Pam", "Pemrogram Java", "perempuan", 34, 1300));
}
};
Daftar<Orang> phpProgrammer = Daftar Array baru<Orang>() {
{
add(Orang baru("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
add(Orang baru("Clarette", "Cicely", "PHP programmer", "perempuan", 23, 1200));
add(Orang baru("Victor", "Channing", "PHP programmer", "male", 32, 1600));
add(Orang baru("Tori", "Sheryl", "PHP programmer", "perempuan", 21, 1000));
add(Orang baru("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
tambahkan(Orang baru("Rosalind", "Layla", "Pemrogram PHP", "perempuan", 25, 1300));
add(Orang baru("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
add(Orang baru("Quinn", "Tamara", "PHP programmer", "perempuan", 21, 1000));
add(Orang baru("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
add(Orang baru("Evonne", "Shari", "PHP programmer", "perempuan", 40, 1800));
}
};
Sekarang kita menggunakan metode forEach untuk mengulangi dan menampilkan daftar di atas:
Copy kode kodenya sebagai berikut:
System.out.println("Nama semua programmer:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Kami juga menggunakan metode forEach untuk meningkatkan gaji programmer sebesar 5%:
Copy kode kodenya sebagai berikut:
System.out.println("Beri programmer kenaikan gaji 5%:");
Konsumen<Orang> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);
Metode lain yang berguna adalah filter(), yang memungkinkan kita menampilkan programmer PHP dengan gaji bulanan lebih dari $1400:
Copy kode kodenya sebagai berikut:
System.out.println("Berikut adalah programmer PHP dengan gaji bulanan lebih dari $1,400:")
phpProgrammer.stream()
.filter((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Kita juga dapat menentukan filter dan menggunakannya kembali untuk melakukan operasi lain:
Copy kode kodenya sebagai berikut:
// tentukan filter
Predikat<Orang> ageFilter = (p) -> (p.getAge() > 25);
Predikat<Orang> SalaryFilter = (p) -> (p.getSalary() > 1400);
Predikat<Orang> genderFilter = (p) -> ("perempuan".sama dengan(p.getGender()));
System.out.println("Berikut adalah programmer PHP wanita yang berusia lebih dari 24 tahun dan memiliki gaji bulanan lebih dari $1,400:");
phpProgrammer.stream()
.filter(ageFilter)
.filter(gajiFilter)
.filter(filter gender)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
//Gunakan kembali filter
System.out.println("Pemrogram Java wanita berusia lebih dari 24 tahun:");
javaProgrammer.stream()
.filter(ageFilter)
.filter(filter gender)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Gunakan metode batas untuk membatasi jumlah kumpulan hasil:
Copy kode kodenya sebagai berikut:
System.out.println("3 programmer Java pertama:");
javaProgrammer.stream()
.batas(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("3 programmer Java wanita teratas:");
javaProgrammer.stream()
.filter(filter gender)
.batas(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Bagaimana dengan penyortiran? Bisakah kita menanganinya di aliran? Dalam contoh berikut, kita akan mengurutkan programmer Java berdasarkan nama dan gaji, memasukkannya ke dalam daftar, dan kemudian menampilkan daftarnya:
Copy kode kodenya sebagai berikut:
System.out.println("Urutkan berdasarkan nama dan tampilkan 5 programmer Java teratas:");
Daftar<Orang> diurutkanJavaProgrammers = javaProgrammers
.sungai kecil()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.batas(5)
.collect(toList());
diurutkanJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
System.out.println("Urutkan programmer Java berdasarkan gaji:");
diurutkanJavaProgrammers = javaProgrammers
.sungai kecil()
.diurutkan( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.mengumpulkan( keDaftar() );
diurutkanJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
Jika kita hanya tertarik pada gaji terendah dan tertinggi, yang lebih cepat dari memilih gaji pertama/terakhir setelah diurutkan adalah metode min dan max:
Copy kode kodenya sebagai berikut:
System.out.println("Programmer Java dengan bayaran terendah:");
Orang pers = javaProgrammers
.sungai kecil()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.mendapatkan()
System.out.printf("Nama: %s %s; Gaji: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
System.out.println("Programmer Java dengan gaji tertinggi:");
Orang orang = javaProgrammers
.sungai kecil()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.mendapatkan()
System.out.printf("Nama: %s %s; Gaji: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())
Dalam contoh di atas kita telah melihat cara kerja metode pengumpulan. Sehubungan dengan metode peta, kita dapat menggunakan metode pengumpulan untuk memasukkan kumpulan hasil kita ke dalam sebuah String, Kumpulan, atau TreeSet:
Copy kode kodenya sebagai berikut:
System.out.println("Gabungkan nama depan pemrogram PHP menjadi sebuah string:");
String phpDevelopers = phpProgrammers
.sungai kecil()
.map(Orang::getFirstName)
.collect(joining(" ; ")); // Dapat digunakan sebagai token dalam operasi selanjutnya
System.out.println("Simpan nama depan pemrogram Java ke Set:");
Setel<String> javaDevFirstName = javaProgrammers
.sungai kecil()
.map(Orang::getFirstName)
.collect(toSet());
System.out.println("Simpan nama depan pemrogram Java ke TreeSet:");
TreeSet<String> javaDevLastName = javaProgrammers
.sungai kecil()
.map(Orang::dapatkanNamaBelakang)
.collect(toCollection(TreeSet::baru));
Aliran juga bisa paralel. Contohnya adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
System.out.println("Hitung semua uang yang dibayarkan kepada pemrogram Java:");
int totalGaji = javaProgrammer
.parallelStream()
.mapToInt(p -> p.getSalary())
.jumlah();
Kita dapat menggunakan metode ringkasanStatistik untuk memperoleh berbagai data ringkasan elemen dalam aliran. Selanjutnya, kita dapat mengakses metode berikut seperti getMax, getMin, getSum atau getAverage:
Copy kode kodenya sebagai berikut:
//Hitung hitungan, min, maks, jumlah, dan rata-rata untuk angka
Daftar<Bilangan Bulat> angka = Array.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Statistik IntSummaryStatistics = angka
.sungai kecil()
.mapToInt((x) -> x)
.ringkasanStatistik();
System.out.println("Angka terbesar dalam Daftar: " + stats.getMax());
System.out.println("Angka terkecil dalam Daftar: " + stats.getMin());
System.out.println("Jumlah semua bilangan : " + stats.getSum());
System.out.println("Rata-rata semua angka: " + stats.getAverage());
Oke, itu saja, semoga Anda menyukainya!
Meringkaskan
Dalam artikel ini, kita mempelajari berbagai cara menggunakan ekspresi lambda, dari contoh dasar, hingga contoh yang lebih kompleks menggunakan lambda dan aliran. Selain itu, kita juga mempelajari cara menggunakan ekspresi lambda dan kelas Comparator untuk mengurutkan koleksi Java.
Catatan Penerjemah: Meskipun terlihat sangat canggih, inti dari ekspresi Lambda hanyalah "gula sintaksis" yang disimpulkan oleh kompiler dan membantu Anda mengonversi dan membungkusnya menjadi kode biasa, sehingga Anda dapat menggunakan lebih sedikit kode untuk mencapai fungsi yang sama . Saya menyarankan untuk tidak menggunakannya sembarangan, karena ini seperti kode yang ditulis oleh beberapa peretas tingkat lanjut, ringkas, sulit dipahami, dan sulit di-debug, dan staf pemeliharaan pasti ingin memarahi Anda.