it-swarm.com.ru

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

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

Iterator<T> iterator()

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

Когда я хочу перебрать поток через цикл 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)) { ... }

Я что-то здесь упускаю?

237
roim

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

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

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

176
kennytm

Чтобы преобразовать 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 );
140
ZhongYu

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

10
Aleksandr Dubinsky

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

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

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

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

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

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

6
Zenexer

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

Stream<T> stream = ...;

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

(Выполнить этот фрагмент здесь )

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

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

3
Rich

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);
    }
2
Ashish Tyagi

Если вы не возражаете против использования сторонних библиотек cyclops-реагировать определяет 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);

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

2
John McClean

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

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

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

0
yegor256

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

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

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

    // ...
}
0
BullyWiiPlaza