Перерыв или возврат из потока Java 8 для каждого?

При использовании внешней итерации по Iterable мы используем break или return из расширенного для каждого цикла как:

for (SomeObject obj : someObjects) {
   if (some_condition_met) {
      break; // or return obj
   }
}

Как мы можем break или return использовать внутреннюю итерацию в выражении лямбда Java 8, например:

someObjects.forEach(obj -> {
   //what to do here?
})

Ответ 1

Если вам это нужно, вы не должны использовать forEach, но один из других методов, доступных в потоках; какой из них зависит от вашей цели.

Например, если целью этого цикла является поиск первого элемента, который соответствует некоторому предикату:

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findFirst();

(Примечание. Это не приведет к итерации всей коллекции, потому что потоки оцениваются лениво - она ​​остановится на первом объекте, который соответствует условию).

Если вы просто хотите узнать, есть ли в коллекции элемент, для которого условие истинно, вы можете использовать anyMatch:

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);

Ответ 2

Это возможно для Iterable.forEach() (но не надежно с Stream.forEach()). Решение не хорошо, но это возможно.

ВНИМАНИЕ: Вы не должны использовать его для управления бизнес-логикой, а исключительно для обработки исключительной ситуации, возникающей во время выполнения forEach(). Например, если ресурс внезапно перестает быть доступным, один из обработанных объектов нарушает контракт (например, контракт говорит, что все элементы в потоке не должны быть null но внезапно и неожиданно один из них становится null) и т.д.

Согласно документации для Iterable.forEach():

Выполняет данное действие для каждого элемента Iterable пока все элементы не будут обработаны или действие не вызовет исключение... Исключения, сгенерированные действием, передаются вызывающей стороне.

Таким образом, вы генерируете исключение, которое немедленно прервет внутренний цикл.

Код будет примерно таким - я не могу сказать, что мне это нравится, но он работает. Вы создаете свой собственный класс BreakException который расширяет RuntimeException.

try {
    someObjects.forEach(obj -> {
        // some useful code here
        if(some_exceptional_condition_met) {
            throw new BreakException();
       }
    }
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

Обратите внимание, что try...catch находится не вокруг лямбда-выражения, а вокруг всего метода forEach(). Чтобы сделать его более видимым, посмотрите следующую транскрипцию кода, которая показывает это более четко:

Consumer<? super SomeObject> action = obj -> {
    // some useful code here
    if(some_exceptional_condition_met) {
        throw new BreakException();
    }
});

try {
    someObjects.forEach(action);
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

Ответ 3

Возвращение в лямбде равно продолжению в каждом для каждого, но эквивалента перерыва нет. Вы можете просто вернуться к продолжению:

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})

Ответ 4

Ниже вы найдете решение, которое я использовал в проекте. Вместо forEach просто используйте allMatch:

someObjects.allMatch(obj -> {
    return !some_condition_met;
});

Ответ 5

Либо вам нужно использовать метод, который использует предикат, указывающий, следует ли продолжать движение (так что вместо этого он имеет разрыв), либо вам нужно выкинуть исключение - это очень уродливый подход, конечно.

Итак, вы можете написать способ forEachConditional, подобный этому:

public static <T> void forEachConditional(Iterable<T> source,
                                          Predicate<T> action) {
    for (T item : source) {
        if (!action.test(item)) {
            break;
        }
    }
}

Вместо Predicate<T> вы можете определить свой собственный функциональный интерфейс с тем же общим методом (что-то принимает T и возвращает bool), но с именами, которые указывают на ожидание более четко - Predicate<T> здесь не идеальна.

Ответ 6

Вы можете использовать java8 + rxjava.

//import java.util.stream.IntStream;
//import rx.Observable;

    IntStream intStream  = IntStream.range(1,10000000);
    Observable.from(() -> intStream.iterator())
            .takeWhile(n -> n < 10)
            .forEach(n-> System.out.println(n));

Ответ 7

Для максимальной производительности в параллельных операциях используйте findAny(), который похож на findFirst().

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findAny();

Однако, если требуется стабильный результат, вместо этого используйте findFirst().

Также обратите внимание, что сопоставление шаблонов (anyMatch()/allMatch) возвращает только логическое значение, вы не получите согласованный объект.

Ответ 8

Я достиг чего-то вроде этого

  private void doSomething() {
            List<Action> actions = actionRepository.findAll();
            boolean actionHasFormFields = actions.stream().anyMatch(actionHasMyFieldsPredicate());
            if (actionHasFormFields){
                context.addError(someError);
            }
        }
    }

    private Predicate<Action> actionHasMyFieldsPredicate(){
        return action -> action.getMyField1() != null;
    }

Ответ 9

Вы можете достичь этого, используя комбинацию peek (..) и anyMatch (..).

Используя ваш пример:

someObjects.stream().peek(obj -> {
   <your code here>
}).anyMatch(obj -> !<some_condition_met>);

Или просто напишите общий метод утилиты:

public static <T> void streamWhile(Stream<T> stream, Predicate<? super T> predicate, Consumer<? super T> consumer) {
    stream.peek(consumer).anyMatch(predicate.negate());
}

И затем используйте его, например:

streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> {
   <your code here>
});

Ответ 10

Это нелегко сделать с помощью API, предоставляемых в JDK. Но мы можем сделать это с помощью AbacusUtil. Вот пример кода:

Stream.of("a", "b", "c", "d", "e").peek(N::println) //
        .forEach("", (r, e) -> r + e, (e, r) -> e.equals("c"));
// print: a b c

Раскрытие информации: я разработчик AbacusUtil.

Ответ 11

Что насчет этого:

окончательное условие BooleanWrapper = new BooleanWrapper();

someObjects.forEach(obj -> {
   if (condition.ok()) {
     // YOUR CODE to control
     condition.stop();
   }
});

Где BooleanWrapper - это класс, который вы должны реализовать для управления потоком.