Как перебирать цикл foreach через поток java 8

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

Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");

stream.forEach(s -> writer.append(s)); // Unhandled exception: java.io.IOException

Это не будет компилироваться.

Один способ обхода - исключить проверенное исключение в RuntimeException, но это усложняет последующую обработку исключений, и это просто уродливо:

stream.forEach(s -> {
    try {
        writer.append(s);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
});

Альтернативным обходным решением может быть преобразование ограниченного функционала forEach в обычный старый foreach loop, который более дружелюбен к проверенным исключениям.

Но наивные подходы терпят неудачу:

for (String s : stream) { // for-each not applicable to expression type 'java.util.stream.Stream<java.lang.String>'
    writer.append(s);
}

for (String s : stream.iterator()) { // foreach not applicable to type 'java.util.Iterator<java.lang.String>'
    writer.append(s);
}

Обновление

Трюк, который отвечает на этот вопрос, был ранее опубликован в Почему Stream <T> не реализуйте Iterable <T> ? как ответ на вопрос, который не отвечает на этот вопрос сам по себе. Я думаю, этого недостаточно, чтобы квалифицировать этот вопрос как дубликат этого, потому что они задают разные вещи.

Ответы

Ответ 1

По определению для цикла foreach требуется Iterable для передачи в.

Это может быть достигнуто с помощью анонимного класса:

    for (String s : new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return stream.iterator();
        }
    }) {
        writer.append(s);
    }

Это можно упростить с помощью lambda, поскольку Iterable является функциональный интерфейс:

    for (String s : (Iterable<String>) () -> stream.iterator()) {
        writer.append(s);
    }

Это может быть преобразовано в ссылка метода:

    for (String s : (Iterable<String>) stream::iterator) {
        writer.append(s);
    }

Явное литье можно избежать с помощью промежуточной переменной или параметра метода:

    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        writer.append(s);
    }

Существует также библиотека StreamEx в центре maven, которая имеет встроенные потоки и другие привилегии.


Вот несколько наиболее популярных вопросов и подходов, которые обеспечивают обходные пути для проверки обработок исключений в lambdas и потоках:

Функция Java 8 Lambda, которая выдает исключение?

Java 8: Лямбда-потоки, Фильтр по методу с исключением

Как я могу удалить CHECKED исключения изнутри потоков Java 8? (Не обертывая его в непроверенные исключения)

Java 8: Обязательная проверка обработки исключений в лямбда-выражениях. Почему обязательный, а не факультативный?

jOOX Unchecked

Kotlin;)

Ответ 2

Поток не реализует Iterable, и не является массивом, поэтому он не подходит для использования в расширенном цикле. Причина, по которой он не реализует Iterable, состоит в том, что это единая структура данных. Каждый раз, когда вызывается Iterable.iterator, должен быть возвращен новый Iterator, который охватывает все элементы. Метод итератора Stream также отражает текущее состояние Stream. Это фактически другой взгляд на поток.

Вы можете создать класс, который реализует Iterable, обертывая поток для использования в расширенном цикле. Но это сомнительная идея, поскольку вы действительно не можете реализовать Iterable (как описано выше).

Ответ 3

I написал расширение в Stream API, который позволяет проверять исключенные исключения.

Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");

ThrowingStream.of(stream, IOException.class).forEach(writer::append);

Ответ 4

Вы также можете сделать это следующим образом:

Path inputPath = Paths.get("...");
Stream<Path> stream = Files.list(inputPath);

for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext(); )
{
    Path path = iterator.next();
    // ...
}