Преобразование Iterable в Stream с использованием Java 8 JDK

У меня есть интерфейс, который возвращает java.lang.Iterable<T>.

Я хотел бы обработать этот результат с помощью Java 8 Stream API.

Однако Iterable не может "Поток".

Как лучше использовать потоки (без преобразования Iterable в List)?

Ответ 1

Там гораздо лучший ответ, чем использование spliteratorUnknownSize напрямую, что также легче и получает лучший результат. Iterable имеет метод spliterator(), поэтому вы должны просто использовать его для получения разделителя. В худшем случае, тот же код (реализация по умолчанию использует spliteratorUnknownSize), но в более общем случае, когда ваш Iterable уже является коллекцией, вы получите лучший разделитель и, следовательно, лучшую производительность потока ( может быть, даже хороший parallelism.) Его также меньше кода:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

Как вы можете видеть, получение потока из Iterable (см. Почему Iterable <T> не предоставляет методы stream() и parallelStream()?) не является очень болезненный.

Ответ 2

Если вы можете использовать библиотеку Guava, начиная с версии 21, вы можете использовать

Streams.stream(iterable)

Ответ 3

Вы можете легко создать Stream из Iterable или Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}

Ответ 4

Я хотел бы предложить использовать библиотеку JOOL, она скрывает магию spliterator за вызовом Seq.seq(iterable), а также предоставляет целую кучу дополнительных полезных функциональность.

Ответ 5

Я создал этот класс:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

Я думаю, что он отлично читается, потому что вам не нужно думать о spliterators и booleans (isParallel).

Ответ 6

Очень простая работа для этой проблемы заключается в создании интерфейса Streamable<T>, расширяющего Iterable<T>, который содержит метод default <T> stream().

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Теперь любой из ваших Iterable<T> можно создать тривиально, просто объявив их implements Streamable<T> вместо Iterable<T>.

Ответ 7

Если вы используете Vavr (ранее известный как Javaslang), это может быть так же просто, как:

Iterable i = //...
Stream.ofAll(i);

Ответ 8

Итак, как еще один ответ, упомянутый Гуава, поддерживает это:

Streams.stream(iterable);

Я хочу подчеркнуть, что реализация делает что-то немного отличное от других предложенных ответов. Если Iterable имеет тип Collection, он их произносит.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}