Panduan untuk Java 8 forEach

1. Ikhtisar

Diperkenalkan di Java 8, loop forEach menyediakan cara baru, ringkas, dan menarik bagi pemrogram untuk melakukan iterasi atas koleksi .

Dalam artikel ini, kita akan melihat bagaimana menggunakan forEach dengan koleksi, argumen seperti apa yang diperlukan dan bagaimana perulangan ini berbeda dari perulangan for yang ditingkatkan .

Jika Anda perlu memoles beberapa konsep Java 8, kami memiliki kumpulan artikel yang dapat membantu Anda.

2. Dasar-dasar forEach

Di Java, antarmuka Collection memiliki Iterable sebagai antarmuka supernya - dan dimulai dengan Java 8, antarmuka ini memiliki API baru:

void forEach(Consumer action)

Sederhananya, Javadoc of forEach statistik yang "melakukan tindakan yang diberikan untuk setiap elemen Iterable sampai semua elemen telah diproses atau tindakan tersebut mengeluarkan pengecualian".

Jadi, dengan forEach , kita dapat mengulang koleksi dan melakukan tindakan tertentu pada setiap elemen, seperti Iterator lainnya .

Misalnya, versi for-loop dari iterasi dan pencetakan Collection of Strings :

for (String name : names) { System.out.println(name); }

Kita dapat menulis ini menggunakan forEach sebagai:

names.forEach(name -> { System.out.println(name); });

3. Menggunakan Metode forEach

Kami menggunakan forEach untuk mengulang koleksi dan melakukan tindakan tertentu pada setiap elemen. Tindakan yang akan dilakukan berada dalam kelas yang mengimplementasikan antarmuka Konsumen dan diteruskan ke forEach sebagai argumen.

The Consumer antarmuka adalah antarmuka fungsional (interface dengan metode abstrak tunggal). Ia menerima masukan dan tidak mengembalikan hasil.

Berikut definisinya:

@FunctionalInterface public interface Consumer { void accept(T t); }

Oleh karena itu, implementasi apa pun, misalnya, konsumen yang hanya mencetak String :

Consumer printConsumer = new Consumer() { public void accept(String name) { System.out.println(name); }; };

bisa diteruskan ke forEach sebagai argumen:

names.forEach(printConsumer);

Tapi itu bukan satu-satunya cara untuk membuat tindakan melalui konsumen dan menggunakan API forEach .

Mari kita lihat 3 cara paling populer di mana kita akan menggunakan metode forEach :

3.1. Implementasi Konsumen Anonim

Kita dapat membuat contoh implementasi antarmuka Konsumen menggunakan kelas anonim dan kemudian menerapkannya sebagai argumen ke metode forEach :

Consumer printConsumer= new Consumer() { public void accept(String name) { System.out.println(name); } }; names.forEach(printConsumer);

Ini bekerja dengan baik tetapi jika kita menganalisis pada contoh di atas kita akan melihat bahwa bagian sebenarnya yang digunakan adalah kode di dalam metode accept () .

Meskipun ekspresi Lambda sekarang menjadi norma dan cara yang lebih mudah untuk melakukan ini, masih perlu diketahui cara mengimplementasikan antarmuka Konsumen .

3.2. Ekspresi Lambda

Manfaat utama dari antarmuka fungsional Java 8 adalah kita dapat menggunakan ekspresi Lambda untuk membuat instance-nya dan menghindari penggunaan implementasi kelas anonim yang besar.

Karena Antarmuka Konsumen adalah antarmuka fungsional, kami dapat mengekspresikannya di Lambda dalam bentuk:

(argument) -> { //body }

Oleh karena itu, printConsumer kami menyederhanakan untuk:

name -> System.out.println(name)

Dan kita bisa meneruskannya ke forEach sebagai:

names.forEach(name -> System.out.println(name));

Sejak pengenalan ekspresi Lambda di Java 8, ini mungkin cara paling umum untuk menggunakan metode forEach .

Lambdas memang memiliki kurva pembelajaran yang sangat nyata, jadi jika Anda memulai, artikel ini membahas beberapa praktik yang baik dalam menggunakan fitur bahasa baru.

3.3. Referensi Metode

Kita bisa menggunakan sintaks referensi metode alih-alih sintaks Lambda normal di mana metode sudah ada untuk melakukan operasi pada kelas:

names.forEach(System.out::println);

4. Bekerja dengan forEach

4.1. Iterasi Selama Koleksi

Setiap iterable tipe Collection - list, set, queue, dll. Memiliki sintaks yang sama untuk menggunakan forEach.

Oleh karena itu, seperti yang telah kita lihat, untuk mengulang elemen daftar:

List names = Arrays.asList("Larry", "Steve", "James"); names.forEach(System.out::println);

Demikian pula untuk satu set:

Set uniqueNames = new HashSet(Arrays.asList("Larry", "Steve", "James")); uniqueNames.forEach(System.out::println);

Atau katakanlah untuk Antrian yang juga merupakan Koleksi :

Queue namesQueue = new ArrayDeque(Arrays.asList("Larry", "Steve", "James")); namesQueue.forEach(System.out::println);

4.2. Iterasi Melalui Peta - Menggunakan Map's forEach

Peta bukanlah Iterable , tetapi menyediakan varian forEach mereka sendiri yang menerima BiConsumer .

Sebuah BiConsumer diperkenalkan sebagai pengganti Consumer di forEach Iterable sehingga tindakan dapat dilakukan pada kunci dan nilai Peta secara bersamaan.

Mari buat Peta yang memiliki entri:

Map namesMap = new HashMap(); namesMap.put(1, "Larry"); namesMap.put(2, "Steve"); namesMap.put(3, "James");

Selanjutnya, mari kita ulangi namesMap menggunakan Map's forEach :

namesMap.forEach((key, value) -> System.out.println(key + " " + value));

Seperti yang bisa kita lihat di sini, kita telah menggunakan BiConsumer :

(key, value) -> System.out.println(key + " " + value)

untuk mengulangi entri Peta .

4.3. Iterasi Melalui Peta - dengan Iterasi entrySet

Kita juga bisa mengulang EntrySet dari Map menggunakan Iterable's forEach.

Karena entri Peta disimpan dalam Set yang disebut EntrySet, kita dapat mengulanginya menggunakan forEach:

namesMap.entrySet().forEach(entry -> System.out.println( entry.getKey() + " " + entry.getValue()));

5. Foreach vs For-Loop

Dari sudut pandang sederhana, kedua loop menyediakan fungsionalitas yang sama - loop melalui elemen dalam sebuah koleksi.

Perbedaan utama di antara keduanya adalah bahwa keduanya merupakan iterator yang berbeda - for-loop yang ditingkatkan adalah iterator eksternal sedangkan metode forEach yang baru adalah yang internal .

5.1. Iterator Internal - forEach

Jenis iterator ini mengelola iterasi di latar belakang dan membiarkan pemrogram hanya memberi kode apa yang seharusnya dilakukan dengan elemen koleksi.

Sebagai gantinya, iterator mengelola iterasi dan memastikan untuk memproses elemen satu per satu.

Mari kita lihat contoh iterator internal:

names.forEach(name -> System.out.println(name));

Dalam metode forEach di atas, kita dapat melihat bahwa argumen yang diberikan adalah ekspresi lambda. Ini berarti bahwa metode tersebut hanya perlu mengetahui apa yang harus dilakukan dan semua pekerjaan pengulangan akan ditangani secara internal.

5.2. Iterator Eksternal - untuk loop

Iterator eksternal menggabungkan apa dan bagaimana pengulangan dilakukan.

Enumerasi , Iterator , dan for-loop yang ditingkatkan semuanya adalah iterator eksternal (ingat metode iterator (), next () atau hasNext () ?). Dalam semua iterator ini, tugas kita adalah menentukan cara melakukan iterasi.

Pertimbangkan loop familiar ini:

for (String name : names) { System.out.println(name); }

Meskipun kami tidak secara eksplisit memanggil metode hasNext () atau next () saat melakukan iterasi pada daftar, kode yang mendasari yang membuat iterasi ini berfungsi menggunakan metode ini. Ini menyiratkan bahwa kompleksitas operasi ini tersembunyi dari pemrogram tetapi masih ada.

Berlawanan dengan iterator internal yang mana collection melakukan iterasi itu sendiri, di sini kita memerlukan kode eksternal yang mengeluarkan setiap elemen dari koleksi.

6. Kesimpulan

Dalam artikel ini, kami menunjukkan bahwa forEach loop lebih nyaman daripada for-loop normal .

Kami juga melihat cara kerja metode forEach dan jenis implementasi apa yang dapat diterima sebagai argumen untuk melakukan tindakan pada setiap elemen dalam koleksi.

Terakhir, semua cuplikan yang digunakan dalam artikel ini tersedia di repositori Github kami.