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

420

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

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

Однако Iterable не может «течь».

Любая идея, как использовать Iterable в качестве потока без преобразования его в список?

Rayman
источник
2
Если вы можете выполнять итерации, почему бы не использовать цикл для проверки его состояния или значения или чего-либо еще?
Афзаал Ахмад Зеешан
26
@AfzaalAhmadZeeshan, потому что потоки намного лучше
Слейман Джнейди
1
Как я уже сказал, мне нужно сделать некоторые манипуляции в этом списке (фильтры, отображение). Я хотел бы использовать новый Java 8 JDK API -> Stream. но Iterable не является «SteamAble»
Rayman
Кажется странным, что myIterable.stream()не существует!
Джош М.
7
@Guillaume: Да, но Stream.of(iterable)производит Stream<Iterable<Object>>.
Матиас Браун

Ответы:

573

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

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

Как видите, получение потока от Iterable(см. Также этот вопрос ) не очень болезненно.

Брайан Гетц
источник
109
Статический метод Streamбыл бы хорош, например Stream.ofIterable(iterable).
Робинст
59
@robinst Это не было упущением; это был осознанный (и весьма спорный) выбор. Проблема заключается в том, что если бы это существовало, было бы слишком легко достичь этого, не понимая компромиссов, которые с ним связаны. Поскольку Iterable является настолько абстрактным, такой метод может привести к худшему возможному потоку (без параллельной поддержки, без информации о размере или характеристик (используется для оптимизации выбора выполнения)). Принуждение к более продуманному результату приводит к улучшению API во всей экосистеме. Это было компромиссом «что лучше для кода XYZ», чем «что лучше для всего кода Java».
Брайан Гетц
2
Исходя из вашего объяснения, мне любопытно, почему мы получили Collection.stream (), но не Iterable.stream (). Кажется, что причина опускать Iterable.stream () (или Stream.ofIterable ()) в равной степени относится к Collection.stream ().
квалификационный вечер
6
@qualidafial См. stackoverflow.com/questions/23114015/… .
Брайан Гетц
11
@BrianGoetz Похоже, большинство людей не в том уровне, чтобы понять, что вы сказали выше, или не заботятся о них. они хотят написать простой код, вызывая простой API. С другой стороны, эти вещи (параллельные ...), возможно, не важны для большинства ежедневных итерируемых операций.
user_3380739
71

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

Streams.stream(iterable)
numéro6
источник
2
Или, если вы застряли на старой версии, используйте Lists.newArrayList(Iterable).
Джейкоб ван Линген
1
Текущая реализация Guava не хуже принятого ответа: github.com/google/guava/blob/master/guava/src/com/google/common/…
Вадим
23

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

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
источник
2
Вы должны написать эту функцию один раз, а затем просто вызвать ее. Почему звонят, чтобы stream(...)загромождать ваш код?
Гексицид
1
Хотел сделать это встроенным, коротким и элегантным ... вы правы, я могу написать эту функцию один раз ... но я нахожусь в отбрасывании кода (и не добавляя код) в любом случае, этот ответ правильный, потому что это способ конвертировать это.
Rayman
статический импорт указанной функции. коротко и элегантно (хотя и не обязательно прозрачный)
aepurniet
9

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

Shaggie
источник
7

Итак, в качестве другого упомянутого ответа Guava поддерживает это, используя:

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
  );
}
Alex
источник
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);
    }
}

Я думаю, что он отлично читается, потому что вам не нужно думать о разделителях и логических значениях (isParallel).

GT
источник
4

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

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

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

OldCurmudgeon
источник
0

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

Iterable i = //...
Stream.ofAll(i);
Гжегож Пивоварек
источник
-1

Еще один способ сделать это с Java 8 и без внешних библиотек:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Грег Витчак
источник