Возврат значения из метода в выражении лямбда

Я пытаюсь выяснить, как вернуть значение метода из выражения лямбда:

public int findMissingNumber(Collection<Integer> ints) {
    Single<Integer> start = new Single<>(1);
    ints.stream().mapToInt(Integer::valueOf).parallel().forEach(i -> {
        if (i != start.setValue(start.getValue() + 1)) {
            //return here
        }
    });
    return -1;
}

Однако, кажется, что использование ключевого слова return в выражении лямбда будет явно возвращаться к самой лямбда-функции. Есть ли какой-либо способ разбить или принудительно вернуть весь метод?

Ответ 1

Есть ли способ разбить или принудительно вернуть весь метод?

Нет. По крайней мере, если вы не сделаете исключение.

В принципе, это не то, что означает forEach. Вы можете написать метод, который принял функцию, которая вернула бы null для "keep going" и non-null для "stop" и сделать это результатом "... но этот метод не является forEach.

Тот факт, что вы используете выражение лямбда, действительно случайный здесь. Представьте, что вы просто вызывали forEach и передавали некоторый аргумент - не было бы действительно странно, если бы этот вызов заставлял ваш метод findMissingNumber возвращаться (без исключения), без самого метода findMissingNumber, имеющего оператор return?

Ответ 2

(Является ли это экземпляром проблемы XY?)

Вопрос о том, как вернуться из лямбда внутри forEach. Jon Skeet предоставил некоторую полезную информацию о findFirst, а также предупредил о побочных эффектах в параллельном параллельном лямбда-режиме - обе отличные точки.

Но в отношении исходного вопроса я все еще думаю: какая проблема вы пытаетесь решить?

Имя метода findMissingNumber в этом примере наводит на размышления. Метод принимает набор чисел в качестве параметра и выполняет итерацию по нему при увеличении счетчика. Он возвращается, когда он находит несоответствие, или возвращает -1, если нет несоответствия. Поскольку счетчик увеличивается один раз, когда каждое значение в коллекции ints обрабатывается, похоже, что эта коллекция должна быть в порядке, если только номер отсутствует.

Если это так, параметр должен быть List вместо Collection. (Сделаем здесь большое предположение.) В этом предположении можно переписать код с использованием lambdas в виде потоков следующим образом:

static OptionalInt findMissingNumber(List<Integer> ints) {
    return
        IntStream.rangeClosed(1, ints.size())
            .filter(i -> i != ints.get(i-1))
            .findFirst();
}

Вместо того, чтобы увеличивать счетчик, мы используем IntStream.range для генерации ожидаемых значений в списке. Затем мы полагаемся на случайный доступ к списку до значений get из их ожидаемых позиций в списке. Мы фильтруем для несоответствий и возвращаем первый, если таковой имеется. Это позволяет избежать мутации и поэтому должно работать правильно параллельно. (Обратите внимание, что это не очень хорошо распараллеливается, если список не является произвольным доступом.)

Возвращаемое значение представляет собой OptionalInt, который является пустым, если не найдено никакого несоответствия. Это более явное, чем использование значения часового типа, например -1, чтобы указать условие "не найдено".