Panduan untuk Java 8 forEach

1. Gambaran keseluruhan

Diperkenalkan di Java 8, loop forEach menyediakan programmer dengan cara baru, ringkas dan menarik untuk melakukan lelaran ke atas koleksi .

Dalam artikel ini, kita akan melihat bagaimana menggunakan forEach dengan koleksi, jenis argumen apa yang diperlukan dan bagaimana gelung ini berbeza dengan loop untuk yang disempurnakan .

Sekiranya anda perlu menyusun beberapa konsep Java 8, kami mempunyai koleksi artikel yang dapat membantu anda.

2. Dasar-dasar dari foreach

Di Java, antara muka Koleksi mempunyai Iterable sebagai antara muka supernya - dan bermula dengan Java 8 antara muka ini mempunyai API baru:

void forEach(Consumer action)

Ringkasnya, Javadoc forEach stats bahawa ia "melakukan tindakan yang diberikan untuk setiap elemen Iterable sehingga semua elemen diproses atau tindakan membuang pengecualian."

Oleh itu, dengan forEach , kita dapat melakukan pengulangan pada koleksi dan melakukan tindakan tertentu pada setiap elemen, seperti Iterator lain .

Sebagai contoh, untuk gelung versi iterating dan mencetak Collection of Strings :

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

Kita boleh menulis menggunakan ini foreach sebagai:

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

3. Menggunakan Kaedah forEach

Kami menggunakan forEach untuk mengulangi koleksi dan melakukan tindakan tertentu pada setiap elemen. Tindakan yang perlu dilakukan terkandung di dalam kelas yang melaksanakan Pengguna antara muka dan diluluskan untuk foreach sebagai hujah.

Antara muka Pengguna adalah antara muka yang berfungsi (antara muka dengan kaedah abstrak tunggal). Ia menerima input dan tidak memberikan hasil.

Inilah definisi:

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

Oleh itu, pelaksanaan apa pun, misalnya, pengguna yang hanya mencetak String :

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

boleh disampaikan kepada forEach sebagai hujah:

names.forEach(printConsumer);

Tetapi itu bukan satu-satunya cara untuk membuat tindakan melalui pengguna dan menggunakan forEach API.

Mari lihat 3 kaedah paling popular di mana kita akan menggunakan kaedah forEach :

3.1. Pelaksanaan Pengguna Tanpa Nama

Kami dapat mewujudkan pelaksanaan antara muka Pengguna menggunakan kelas tanpa nama dan kemudian menerapkannya sebagai argumen untuk kaedah forEach :

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

Ini berfungsi dengan baik tetapi jika kita menganalisis pada contoh di atas kita akan melihat bahawa bahagian sebenarnya yang digunakan adalah kod di dalam kaedah accept () .

Walaupun ungkapan Lambda kini menjadi kaedah biasa dan lebih mudah untuk melakukan ini, masih perlu diketahui bagaimana melaksanakan antara muka Pengguna .

3.2. Ungkapan Lambda

Manfaat utama antara muka fungsional Java 8 adalah kita dapat menggunakan ungkapan Lambda untuk memberi contoh dan menghindari penggunaan implementasi kelas tanpa nama yang besar.

Oleh kerana Antaramuka Pengguna adalah antara muka yang berfungsi, kami dapat menyatakannya dalam Lambda dalam bentuk:

(argument) -> { //body }

Oleh itu, printConsumer kami mempermudah untuk:

name -> System.out.println(name)

Dan kita boleh lulus untuk foreach sebagai:

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

Sejak pengenalan ungkapan Lambda di Java 8, ini mungkin merupakan kaedah yang paling biasa untuk menggunakan kaedah forEach .

Lambdas memang mempunyai keluk pembelajaran yang sangat nyata, jadi jika anda memulakan, penulisan ini akan merangkumi beberapa amalan baik dalam menggunakan ciri bahasa baru.

3.3. Rujukan Kaedah

Kita boleh menggunakan sintaks rujukan kaedah dan bukannya sintaks Lambda biasa di mana kaedah sudah ada untuk melakukan operasi di kelas:

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

4. Bekerja dengan forEach

4.1. Mengulangi Koleksi

Sebarang jenis Koleksi - senarai, set, barisan dan lain-lain mempunyai sintaks yang sama untuk menggunakan forEach.

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

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

Begitu juga untuk satu set:

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

Atau katakan untuk Antrian yang juga merupakan Koleksi :

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

4.2. Iterating Lebih satu Map - Menggunakan Peta ini foreach

Peta tidak Iterable , tetapi, mereka menyediakan varian mereka sendiri foreach yang menerima yang BiConsumer .

A BiConsumer diperkenalkan bukannya Pengguna dalam Iterable ini foreach supaya tindakan yang boleh dilakukan pada kedua-dua kunci dan nilai sesuatu Peta serentak.

Mari buat Peta dengan entri:

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

Seterusnya, mari Itekadar ke atas namesMap menggunakan Peta yang foreach :

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

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

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

untuk mengulangi entri Peta .

4.3. Pengulangan Melampaui Peta - dengan Iterating entrySet

Kita juga boleh melelar yang EntrySet daripada Peta menggunakan Iterable ini foreach.

Oleh kerana entri Peta disimpan dalam Set yang disebut EntrySet, kita dapat mengulanginya dengan menggunakan forEach:

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

5. Foreach vs For-Loop

Dari sudut pandangan sederhana, kedua-dua gelung memberikan fungsi yang sama - gelung melalui elemen dalam koleksi.

Perbezaan utama antara keduanya adalah bahawa mereka adalah iterator yang berbeza - for-loop yang disempurnakan adalah iterator luaran sedangkan kaedah forEach yang baru adalah kaedah dalaman .

5.1. Pelelar dalaman - foreach

Jenis iterator ini mengatur lelaran di latar belakang dan membiarkan pengaturcara hanya membuat kod apa yang dimaksudkan untuk dilakukan dengan elemen koleksi.

Pengulangan sebaliknya, menguruskan lelaran dan memastikan memproses elemen satu demi satu.

Mari lihat contoh iterator dalaman:

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

Dalam kaedah forEach di atas, kita dapat melihat bahawa argumen yang diberikan adalah ungkapan lambda. Ini bermaksud bahawa kaedah tersebut hanya perlu mengetahui apa yang harus dilakukan dan semua kerja-kerja iterasi akan dijaga secara dalaman.

5.2. Iterator luaran - untuk gelung

Iterator luaran mencampurkan apa dan bagaimana gelung itu dilakukan.

Penghitungan , Iterator dan peningkatan untuk gelung adalah semua iterator luaran (ingat kaedah iterator (), next () atau hasNext () ?). Dalam semua iterator ini adalah tugas kita untuk menentukan cara melakukan lelaran.

Pertimbangkan gelung biasa ini:

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

Walaupun kami tidak secara eksplisit menggunakan kaedah hasNext () atau next () semasa melakukan lelaran ke atas senarai, kod yang mendasari yang menjadikan iterasi ini berfungsi menggunakan kaedah ini. Ini menunjukkan bahawa kerumitan operasi ini tersembunyi dari pengaturcara tetapi masih ada.

Berbeza dengan iterator dalaman di mana koleksi melakukan iterasi itu sendiri, di sini kita memerlukan kod luaran yang mengeluarkan setiap elemen dari koleksi.

6. Kesimpulannya

Dalam artikel ini, kita menunjukkan bahawa foreach gelung adalah lebih mudah daripada biasa untuk gelung .

Kami juga melihat bagaimana kaedah forEach berfungsi dan jenis pelaksanaan apa yang dapat diterima sebagai argumen untuk melakukan tindakan pada setiap elemen dalam koleksi.

Akhirnya, semua coretan yang digunakan dalam artikel ini terdapat di repositori Github kami.