Перетворити Iterable в Stream за допомогою Java 8 JDK


418

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

Я хотів би маніпулювати цим результатом за допомогою Java 8 Stream API.

Однак Iterable не може "потокувати".

Будь-яка ідея, як використовувати Iterable як Stream, не перетворюючи його на List?


2
Якщо ви можете повторити, чому б не просто скористатися циклом, щоб перевірити його стан чи значення або що ще?
Афзаал Ахмад Зеешан

26
@AfzaalAhmadZeeshan, тому що потоки набагато кращі
Слейман Джаніді

1
Як я вже сказав, мені потрібно зробити кілька маніпуляцій у цьому списку (фільтри, картографування). Я хотів би використовувати новий Java 8 JDK API -> Stream. але Iterable isnot "SteamAble"
rayman

Дивно здається, що myIterable.stream()не існує!
Джош М.

7
@Guillaume: Так, але Stream.of(iterable)створює Stream<Iterable<Object>>.
Маттіас Браун

Відповіді:


571

Є набагато краща відповідь, ніж використання 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 ().
відбірковий


11
@BrianGoetz Здається, що більшість людей не знаходяться на рівні, щоб зрозуміти, що ви сказали вище, або це не цікавить. вони хочуть написати простий код лише зателефонувавши на простий API. З іншого боку, ці речі (паралельно ...), можливо, не важливі для більшості щоденних ітерабельних операцій.
user_3380739

71

Якщо ви можете використовувати бібліотеку Guava, починаючи з версії 21, ви можете використовувати

Streams.stream(iterable)

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
    );
}

2
Ви повинні написати цю функцію один раз, а потім просто викликати її. Чому дзвінок stream(...)забиває ваш код?
гексицид

1
Хотів зробити це Inline короткий і елегантний .. ви праві, я можу написати цю функцію один раз .. але я впадаю в код (і не додаю код). у будь-якому випадку ця відповідь правильна, тому що це спосіб її конвертувати.
Райман

статичний імпорт зазначеної функції. коротка і елегантна. (хоча не обов’язково прозорий)
aepurniet

9

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


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
  );
}

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).


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>може бути тривіально зробленим streamable, лише оголосивши їх implements Streamable<T>замість Iterable<T>.


0

Якщо ви випадково використовуєте Vavr (раніше відомий як Javaslang), це може бути так само просто, як:

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

-1

Ще один спосіб зробити це з Java 8 та без зовнішніх ліб:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.