Перейдіть до наступного елемента, використовуючи цикл foreach Java 8 у потоці


126

У мене проблема з потоком Java 8 foreach, який намагався перейти до наступного елемента в циклі. Я не можу встановити команду як continue;, return;працює лише, але ви вийдете з циклу в цьому випадку. Мені потрібно перейти до наступного елемента в циклі. Як я можу це зробити?

Приклад (не працює):

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

Приклад (робочий):

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

Будь ласка, опублікуйте повний, але мінімальний приклад, щоб було легше відповісти.
Тунакі

Мені потрібно перейти до наступного елемента в циклі.
Віктор В.

2
Його справа полягає в тому, що, за вказаним вами кодом, все-таки неможливо continueперейти до наступного пункту без будь-яких функціональних змін.
DoubleDouble

Якщо мета - уникнути виконання рядків, що надходять після дзвінка, щоб продовжити, і щоб ви не показували нам, просто покладіть всі ці рядки в elseблок. Якщо після continueцього нічого немає , скиньте блок if і продовжте: вони марні.
JB Nizet

Відповіді:


278

Використання return;буде добре працювати. Це не завадить завершити повний цикл. Це лише зупинить виконання поточної ітерації forEachциклу.

Спробуйте наступну маленьку програму:

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

Вихід:

a
c

Зауважте, як return;виконується bітерація для ітерації, але cдрукує наступну ітерацію просто чудово.

Чому це працює?

Причина, яку поведінка спочатку здається неінтуїтивною, полягає в тому, що ми звикли до returnтвердження, яке перериває виконання всього методу. Тож у цьому випадку ми очікуємо, що mainвиконання методу в цілому припиниться.

Однак потрібно розуміти, що це лямбда-вираз, наприклад:

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

    System.out.println(str);
}

... насправді потрібно розглядати як власний окремий "метод", повністю відокремлений від mainметоду, незважаючи на те, що він зручно розташований у ньому. Тож справді returnвисловлювання лише зупиняє виконання лямбдаського виразу.

Друге, що потрібно розуміти, це:

stringList.stream().forEach()

... насправді просто нормальний цикл під обкладинками, який виконує лямбда-вираз для кожної ітерації.

Маючи на увазі ці 2 пункти, вищезазначений код можна переписати наступним рівнозначним способом (лише для освітніх цілей):

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

З цим "менш магічним" еквівалентом коду сфера returnвисловлення стає більш очевидною.


3
Я вже спробував цей спосіб, і чомусь це не працює, я повторив цю хитрість ще раз, і вона працює. Дякую, мені це допомагає. Здається, я просто перевантажений роботою =)
Віктор В.

2
Працює як шарм! Чи можете ви додати пояснення, чому це працює, будь ласка?
sparkyspider

2
@Spider: додано.
sstan

5

Інше рішення: пройти фільтр із перевернутими умовами: Приклад:

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

можна замінити на

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

Найбільш прямолінійним підходом, здається, є використання "return;" хоч.


2

Лямбда, до якої ви переходите forEach(), оцінюється для кожного елемента, отриманого з потоку. Сама ітерація не видно в межах лямбда, тому ви не можете continueце як би forEach()макросом препроцесора C. Натомість ви можете умовно пропустити решту тверджень у ньому.


0

Ви можете використовувати просту ifзаяву, а не продовжувати. Тож замість того, як у вас є ваш код, ви можете спробувати:

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

Присудок у викладі if буде просто протилежним до присудка у вашому if(pred) continue;операторі, тому просто використовуйте !(логічно, ні).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.