Почему поток не реализует Iterable?


в Java 8 класс Поток, которые на удивление метод

Iterator<T> iterator()

таким образом, вы ожидаете, что он реализует интерфейс Iterable, что требует именно этого метода, но это не так.

когда я хочу перебирать поток с помощью цикла foreach, я должен сделать что-то вроде

public static Iterable<T> getIterable(Stream<T> s) {
    return new Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return s.iterator();
        }
    };
}

for (T element : getIterable(s)) { ... }

Я что-то пропустил?

9 221

9 ответов:

там уже люди спрашивали то же самое в списке рассылки☺. Основная причина заключается в том, что Iterable также имеет повторную итерацию семантики, а Stream-нет.

Я думаю, что главная причина в том, что Iterable подразумевает возможность повторного использования, тогда как Stream это то, что может быть использован только один раз - больше как Iterator.

если Stream extended Iterable тогда существующий код может быть удивлен, когда он получает Iterable Что выдает Exception в во второй раз они делают for (element : iterable).

преобразование Stream до Iterable, вы можете сделать

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

передать Stream к методу, который ожидает Iterable,

void foo(Iterable<X> iterable)

просто

foo(stream::iterator) 

однако это, вероятно, выглядит смешно; это может быть лучше, чтобы быть немного более явным

foo( (Iterable<X>)stream::iterator );

Я хотел бы отметить, что StreamEx реализует IterableStream), а также множество других чрезвычайно удивительных функций, отсутствующих в Stream.

kennytm описано почему небезопасно лечить Stream как Iterable и Чжун Юй предложил обходной путь что позволяет использовать Stream а в Iterable, хотя и небезопасным образом. Можно получить лучшее из обоих миров: многоразовый Iterable С Stream Это соответствует всем гарантиям, сделанным Iterable спецификация.

Примечание: SomeType здесь не параметр типа--вам нужно заменить его на правильный тип (например, String) или прибегнуть к рефлексии

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

есть один главный недостаток:

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

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

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

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

(Run вот этот фрагмент)

(Это использует приведение функционального интерфейса Java 8.)

(это описано в некоторых комментариях выше (например,Александр Дубинский), но я хотел вытащить его в ответ, чтобы сделать его более заметным.)

Если вы не против использовать сторонние библиотеки Циклоп-реагировать определяет поток, который реализует как поток, так и итерацию и также воспроизводится (решение проблемы kennytm описано).

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

или

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[раскрытие я ведущий разработчик cyclops-react]

не идеально, но будет работать:

iterable = stream.collect(Collectors.toList());

не идеально, потому что он будет извлекать все элементы из потока и помещать их в это List, что не совсем то, что Iterable и Stream про. Они должны быть лень.

вы можете перебирать все файлы в папке с помощью Stream<Path> такой:

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}

Stream не реализует Iterable. Общее понимание Iterable это все, что можно повторить, часто снова и снова. Stream может не воспроизводиться.

единственный обходной путь, который я могу придумать, где итерируемый на основе потока также воспроизводится,-это воссоздать поток. Я использую Supplier ниже, чтобы создать новый экземпляр потока, каждый раз создается новый итератор.

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }