Akışta Java 8 foreach döngüsü kullanarak sonraki öğeye git


127

Döngüdeki sonraki öğeye geçmeye çalışan Java 8 akışıyla ilgili bir sorun yaşıyorum. Komutu continue;sadece return;çalışıyor gibi ayarlayamıyorum ancak bu durumda döngüden çıkacaksınız. Döngüdeki bir sonraki maddeye geçmem gerekiyor. Bunu nasıl yapabilirim?

Örnek (çalışmıyor):

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

Örnek (çalışma):

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!
}

Lütfen yanıtlamanın daha kolay olması için eksiksiz ancak minimal bir örnek gönderin.
Tunaki

Döngüdeki bir sonraki maddeye geçmem gerekiyor.
Viktor V.

2
Demek istediğim, gösterdiğiniz kodla, continueyine de herhangi bir işlevsel değişiklik olmadan bir sonraki öğeye geçmemesi .
DoubleDouble

Amaç, devam etmek için çağrıdan sonra gelen satırları yürütmekten kaçınmaksa ve bize göstermiyorsanız, tüm bu satırları bir elsebloğa koyun . continueSonrasında hiçbir şey yoksa, if bloğunu bırakın ve devam edin: işe yaramazlar.
JB Nizet

Yanıtlar:


279

Kullanmak return;işe yarayacak. Tam döngünün tamamlanmasını engellemeyecektir. Yalnızca forEachdöngünün mevcut yinelemesini yürütmeyi durduracaktır .

Aşağıdaki küçük programı deneyin:

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);
    });
}

Çıktı:

a
c

Yineleme return;için nasıl yürütüldüğüne dikkat edin b, ancak caşağıdaki yinelemede gayet iyi yazdırır.

Bu neden işe yarıyor?

Davranışın ilk bakışta sezgisel görünmemesinin nedeni return, tüm yöntemin yürütülmesini kesintiye uğratan ifadeye alışkın olmamızdır . Dolayısıyla bu durumda, mainbir bütün olarak yöntem yürütmesinin durdurulmasını bekliyoruz.

Ancak anlaşılması gereken şey şu gibi bir lambda ifadesidir:

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

    System.out.println(str);
}

... mainiçine uygun bir şekilde yerleştirilmiş olmasına rağmen , gerçekten yöntemden tamamen ayrı, kendine özgü bir "yöntem" olarak düşünülmelidir . Yani gerçekten, returnifade sadece lambda ifadesinin çalışmasını durdurur.

Anlaşılması gereken ikinci şey şudur:

stringList.stream().forEach()

... her yineleme için lambda ifadesini çalıştıran kapakların altındaki normal bir döngüdür.

Bu 2 nokta göz önünde bulundurularak, yukarıdaki kod aşağıdaki eşdeğer şekilde yeniden yazılabilir (yalnızca eğitim amaçlı):

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);
}

Bu "daha az sihirli" kod eşdeğeriyle, returnifadenin kapsamı daha belirgin hale gelir.


3
Bu şekilde zaten denedim ve bir şekilde işe yaramıyor, bu numarayı tekrar tekrarladım ve işe yarıyor. Teşekkürler bana yardımcı oluyor. Görünüşe göre çok çalıştım =)
Viktor V.

2
Tıkır tıkır çalışıyor! Bunun neden işe yaradığına dair bir açıklama ekleyebilir misiniz lütfen?
sparkyspider

2
@Spider: eklendi.
sstan

5

Başka bir çözüm: tersine çevrilmiş koşullarınızla bir filtreden geçin: Örnek:

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

ile değiştirilebilir

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

En basit yaklaşım "dönüş" kullanmak gibi görünüyor; rağmen.


2

Geçiş yaptığınız lambda forEach(), akıştan alınan her öğe için değerlendirilir. Yinelemenin kendisi lambda kapsamında görünmez, bu nedenle bir C önişlemci makrosuymuş continuegibi yapamazsınız forEach(). Bunun yerine, koşullu olarak içindeki ifadelerin geri kalanını atlayabilirsiniz.


0

ifDevam etmek yerine basit bir ifade kullanabilirsiniz . Dolayısıyla, kodunuzu elde etme şekliniz yerine şunları deneyebilirsiniz:

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
    });
}

İf ifadesindeki yüklem, ifadenizdeki yüklemin tam tersi olacaktır if(pred) continue;, bu yüzden sadece !(mantıksal değil) kullanın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.