Pindah ke item berikutnya menggunakan Java 8 foreach loop in stream

127

Saya punya masalah dengan aliran Java 8 foreach mencoba untuk melanjutkan item berikutnya dalam lingkaran. Saya tidak dapat mengatur perintah seperti continue;, hanya return;berfungsi tetapi Anda akan keluar dari loop dalam kasus ini. Saya perlu melanjutkan item berikutnya dalam lingkaran. Bagaimana saya bisa melakukan itu?

Contoh (tidak berfungsi):

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
            filteredLines = lines.filter(...).foreach(line -> {
           ...
           if(...)
              continue; // this command doesn't working here
    });
}

Contoh (bekerja):

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
    filteredLines = lines.filter(...).collect(Collectors.toList());
}

for(String filteredLine : filteredLines){
   ...
   if(...)
      continue; // it's working!
}
Viktor V.
sumber
Tolong posting contoh lengkap tapi minimal agar lebih mudah menjawabnya.
Tunaki
Saya perlu melanjutkan item berikutnya dalam lingkaran.
Viktor V.
2
Intinya adalah, dengan kode yang Anda tunjukkan, tidak memiliki keinginan continueuntuk tetap pindah ke item berikutnya tanpa perubahan fungsi apa pun.
DoubleDouble
Jika tujuannya adalah untuk menghindari mengeksekusi baris yang datang setelah panggilan untuk melanjutkan, dan Anda tidak menunjukkan kepada kami, cukup letakkan semua baris ini dalam satu elseblok. Jika tidak ada setelahnya continue, maka lepaskan blok if dan lanjutkan: tidak ada gunanya.
JB Nizet

Jawaban:

279

Menggunakan return;akan bekerja dengan baik. Ini tidak akan mencegah pengulangan penuh dari penyelesaian. Ini hanya akan berhenti menjalankan iterasi forEachloop saat ini.

Coba program kecil berikut ini:

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");

    stringList.stream().forEach(str -> {
        if (str.equals("b")) return; // only skips this iteration.

        System.out.println(str);
    });
}

Keluaran:

a
c

Perhatikan bagaimana return;dieksekusi untuk biterasi, tetapi cmencetak pada iterasi berikut dengan baik.

Mengapa ini berhasil?

Alasan mengapa perilaku tersebut tampak tidak intuitif pada awalnya adalah karena kita terbiasa dengan returnpernyataan yang mengganggu pelaksanaan seluruh metode. Jadi dalam kasus ini, kami mengharapkan maineksekusi metode secara keseluruhan dihentikan.

Namun yang perlu dipahami adalah ungkapan lambda itu, seperti:

str -> {
    if (str.equals("b")) return;

    System.out.println(str);
}

... benar-benar perlu dianggap sebagai "metode" tersendiri, benar-benar terpisah dari mainmetode, meskipun itu terletak di dalamnya. Jadi sungguh, returnpernyataan itu hanya menghentikan eksekusi ekspresi lambda.

Hal kedua yang perlu dipahami adalah:

stringList.stream().forEach()

... benar-benar hanya loop normal di bawah sampul yang mengeksekusi ekspresi lambda untuk setiap iterasi.

Dengan memperhatikan 2 poin ini, kode di atas dapat ditulis ulang dengan cara yang setara berikut (hanya untuk tujuan pendidikan):

public static void main(String[] args) {
    ArrayList<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");

    for(String s : stringList) {
        lambdaExpressionEquivalent(s);
    }
}

private static void lambdaExpressionEquivalent(String str) {
    if (str.equals("b")) {
        return;
    }

    System.out.println(str);
}

Dengan padanan kode "kurang ajaib" ini, cakupan returnpernyataan menjadi lebih jelas.

sstan
sumber
3
Saya sudah mencoba cara ini dan terkadang tidak berhasil, saya telah mengulangi trik ini lagi dan berhasil. Terima kasih, ini membantu saya. Sepertinya saya hanya terlalu banyak bekerja =)
Viktor V.
2
Bekerja seperti pesona! Bisakah Anda menambahkan penjelasan mengapa ini berhasil?
sparkyspider
2
@Spider: ditambahkan.
sstan
5

Solusi lain: melalui filter dengan kondisi terbalik Anda: Contoh:

if(subscribtion.isOnce() && subscribtion.isCalled()){
                continue;
}

bisa diganti dengan

.filter(s -> !(s.isOnce() && s.isCalled()))

Pendekatan paling lugas tampaknya menggunakan "kembali;" meskipun.

Anarkopsykotik
sumber
2

Lambda yang Anda teruskan forEach()dievaluasi untuk setiap elemen yang diterima dari aliran. Iterasi itu sendiri tidak terlihat dari dalam cakupan lambda, jadi Anda tidak dapat continuemelakukannya seolah-olah forEach()makro praprosesor C. Sebagai gantinya, Anda dapat melewati sisa pernyataan di dalamnya secara kondisional.

John Bollinger
sumber
0

Anda dapat menggunakan ifpernyataan sederhana daripada melanjutkan. Jadi, alih-alih cara Anda memiliki kode, Anda dapat mencoba:

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
            filteredLines = lines.filter(...).foreach(line -> {
           ...
           if(!...) {
              // Code you want to run
           }
           // Once the code runs, it will continue anyway
    });
}

Predikat dalam pernyataan if hanya akan menjadi kebalikan dari predikat dalam if(pred) continue;pernyataan Anda , jadi gunakan saja !(tidak logis).

Hassan
sumber