Почему 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)) { ... }
Я что-то пропустил?
Ответы
Ответ 1
Уже есть люди, которые задали тот же в списке рассылки ☺. Основная причина заключается в том, что Iterable также имеет повторную итеративную семантику, а Stream - нет.
Я думаю, главная причина в том, что Iterable
подразумевает повторное использование, тогда как Stream
- это то, что можно использовать только один раз - больше похоже на Iterator
.
Если Stream
extended Iterable
, тогда существующий код может быть удивлен, когда он получит Iterable
, который выдает Exception
второй раз они делают for (element : iterable)
.
Ответ 2
Чтобы преобразовать a 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 );
Ответ 3
Я хотел бы указать, что StreamEx
реализует Iterable
(и Stream
), а также множество другие невероятно функциональные функции отсутствуют в Stream
.
Ответ 4
kennytm описал, почему небезопасно относиться к Stream
как Iterable
, а Чжун Юй предложил обходное решение, который позволяет использовать Stream
как в Iterable
, хотя и небезопасно. Можно получить лучшее из обоих миров: многоразовый Iterable
от Stream
, который отвечает всем гарантиям, указанным в спецификации Iterable
.
Примечание: SomeType
здесь не является параметром типа - вам нужно заменить его на соответствующий тип (например, String
) или прибегнуть к отражению
Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):
Существует один главный недостаток:
Преимущества ленивой итерации будут потеряны. Если вы планируете немедленно перебирать все значения в текущем потоке, любые накладные расходы будут незначительными. Однако, если вы планировали итерацию только частично или в другом потоке, эта немедленная и полная итерация может иметь непреднамеренные последствия.
Большое преимущество, конечно, состоит в том, что вы можете повторно использовать Iterable
, тогда как (Iterable<SomeType>) stream::iterator
допускает только одно использование. Если код приема будет повторяться через коллекцию несколько раз, это не только необходимо, но и полезно для производительности.
Ответ 5
Вы можете использовать Stream в цикле for
следующим образом:
Stream<T> stream = ...;
for (T x : (Iterable<T>) stream::iterator) {
...
}
(Запустите этот фрагмент здесь)
(для этого используется функциональный интерфейс Java 8).
(Это описано в некоторых комментариях выше (например, Александр Дубинский), но я хотел вытащить его в ответ, чтобы сделать его более заметным.)
Ответ 6
Если вы не возражаете против использования сторонних библиотек cyclops-react определяет поток, который реализует как 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);
[Раскрытие информации Я ведущий разработчик реакции циклопов]
Ответ 7
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);
}
Ответ 8
Не идеально, но будет работать:
iterable = stream.collect(Collectors.toList());
Не идеально, потому что он будет извлекать все элементы из потока и помещать их в это List
, что не совсем соответствует Iterable
и Stream
. Предполагается, что они lazy.
Ответ 9
Вы можете перебирать все файлы в папке, используя Stream<Path>
следующим образом:
Path path = Paths.get("...");
Stream<Path> files = Files.list(path);
for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
Object file = it.next();
// ...
}