¿Por qué Stream no implementa Iterable ?

En Java 8 tenemos la clase Stream , que curiosamente tiene un método

Iterator iterator() 

Así que esperaría que implementara la interfaz Iterable , que requiere exactamente este método, pero ese no es el caso.

Cuando quiero iterar sobre un Stream utilizando un bucle foreach, tengo que hacer algo como

 public static Iterable getIterable(Stream s) { return new Iterable { @Override public Iterator iterator() { return s.iterator(); } }; } for (T element : getIterable(s)) { ... } 

¿Me estoy perdiendo de algo?

Ya hay personas que preguntaron lo mismo en la lista de correo ☺. La razón principal es Iterable también tiene una semántica iterativa, mientras que Stream no lo es.

Creo que la razón principal es que Iterable implica reutilización, mientras que Stream es algo que solo se puede usar una vez, más como un Iterator .

Si Stream extendió Iterable , el código existente podría sorprenderse cuando reciba un Iterable que arroje una Exception la segunda vez que lo haga for (element : iterable) .

Para convertir un Stream en un Iterable , puede hacer

 Stream stream = null; Iterable iterable = stream::iterator 

Pasar un Stream a un método que espera Iterable ,

 void foo(Iterable iterable) 

simplemente

 foo(stream::iterator) 

sin embargo, probablemente se ve raro; podría ser mejor ser un poco más explícito

 foo( (Iterable)stream::iterator ); 

Me gustaría señalar que StreamEx implementa Iterable (y Stream ), así como una gran cantidad de otras funcionalidades inmensamente asombrosas que faltan en Stream .

kennytm describió por qué no es seguro tratar un Stream como un Iterable , y Zhong Yu ofreció una solución alternativa que permite usar un Stream como en Iterable , aunque de una manera insegura. Es posible obtener lo mejor de ambos mundos: un Iterable reutilizable de un Stream que cumpla con todas las garantías establecidas por la especificación Iterable .

Nota: SomeType no es un parámetro de tipo aquí; debe reemplazarlo por un tipo apropiado (por ejemplo, String ) o recurrir a la reflexión.

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

Hay una gran desventaja:

Los beneficios de la iteración perezosa se perderán. Si planea iterar inmediatamente sobre todos los valores en el hilo actual, cualquier sobrecarga será insignificante. Sin embargo, si planea iterar solo parcialmente o en un hilo diferente, esta iteración inmediata y completa podría tener consecuencias no deseadas.

La gran ventaja, por supuesto, es que puede reutilizar Iterable , mientras que (Iterable) stream::iterator solo permitiría un único uso. Si el código de recepción se repetirá varias veces sobre la colección, esto no solo es necesario, sino también beneficioso para el rendimiento.

Puede usar un flujo en un bucle for siguiente manera:

 Stream stream = ...; for (T x : (Iterable) stream::iterator) { ... } 

(Ejecute este fragmento aquí )

(Esto usa un molde de interfaz funcional Java 8).

(Esto está cubierto en algunos de los comentarios anteriores (por ejemplo, Aleksandr Dubinsky ), pero quería sacarlo en una respuesta para hacerlo más visible).

Si no te importa utilizar bibliotecas de terceros, cyclops-reaction define un Stream que implementa tanto Stream como Iterable y también se puede volver a reproducir (resolviendo el problema descrito por kennytm ).

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

o:

  Iterable stream = ReactiveSeq.of("hello","world") .map(s->"prefix-"+s); stream.forEach(System.out::println); stream.forEach(System.out::println); 

[Divulgación Soy el desarrollador principal de cyclops-react]

No es perfecto, pero funcionará:

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

No es perfecto porque recogerá todos los elementos de la secuencia y los colocará en esa List , que no es exactamente lo que tratan Iterable y Stream . Se supone que son flojos .

Puede iterar sobre todos los archivos en una carpeta usando Stream como este:

 Path path = Paths.get("..."); Stream files = Files.list(path); for (Iterator it = files.iterator(); it.hasNext(); ) { Object file = it.next(); // ... } 

Stream no implementa Iterable . La comprensión general de Iterable es cualquier cosa que puede repetirse, a menudo una y otra vez. Stream no puede ser reproducido.

La única solución que puedo pensar, donde un iterable basado en una secuencia también se puede volver a reproducir, es volver a crear la secuencia. Estoy utilizando un Supplier continuación para crear una nueva instancia de transmisión, cada vez que se crea un nuevo iterador.

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