Почему поток не реализует 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 ответов:
там уже люди спрашивали то же самое в списке рассылки☺. Основная причина заключается в том, что Iterable также имеет повторную итерацию семантики, а Stream-нет.
Я думаю, что главная причина в том, что
Iterable
подразумевает возможность повторного использования, тогда какStream
это то, что может быть использован только один раз - больше какIterator
.если
Stream
extendedIterable
тогда существующий код может быть удивлен, когда он получает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
реализуетIterable
(иStream
), а также множество других чрезвычайно удивительных функций, отсутствующих в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); }