Почему Stream<T> не реализует Iterable<T>?

В Java 8 у нас есть класс Stream;, который, как ни странно, имеет метод

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 ответов

Решение

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

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

Если Stream расширенный 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 );

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

Stream<T> stream = ...;

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

(Запустите этот фрагмент здесь)

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

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

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

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

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

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

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

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

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

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

Если вы не возражаете против использования сторонних библиотек, циклоп-реактив определяет Stream, который реализует как Stream, так и Iterable, а также является воспроизводимым (решение описанной проблемы 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);

[Раскрытие Я ведущий разработчик циклоп-реакции]

Вы можете перебирать все файлы в папке, используя Stream<Path> как это:

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

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

    // ...
}

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

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

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

Другие вопросы по тегам