for each java list
¿Por qué el iterador de Java no es Iterable? (15)
Como han dicho otros, se puede invocar un Iterable varias veces, devolviendo un iterador nuevo en cada llamada; un iterador se usa solo una vez. Entonces están relacionados, pero sirven para diferentes propósitos. Sin embargo, de manera frustrante, el método "compacto para" funciona solo con un iterable.
Lo que describiré a continuación es una forma de obtener lo mejor de ambos mundos: devolver un Iterable (para una sintaxis más agradable) incluso cuando la secuencia de datos subyacente es única.
El truco es devolver una implementación anónima de Iterable que realmente desencadena el trabajo. Entonces, en lugar de hacer el trabajo que genera una secuencia única y luego devolver un iterador sobre eso, usted devuelve un Iterable que, cada vez que se accede, rehace el trabajo. Eso puede parecer un desperdicio, pero a menudo solo llamarás al Iterable una vez, e incluso si lo llamas varias veces, aún tiene una semántica razonable (a diferencia de un simple contenedor que hace que un Iterator parezca un Iterable, este won '' t fallar si se usa dos veces).
Por ejemplo, supongamos que tengo un DAO que proporciona una serie de objetos de una base de datos, y quiero proporcionar acceso a eso a través de un iterador (por ejemplo, para evitar crear todos los objetos en la memoria si no son necesarios). Ahora podría simplemente devolver un iterador, pero eso hace que usar el valor devuelto en un bucle sea feo. Entonces, en lugar de eso, envuelvo todo en un Anter Iterable:
class MetricDao {
...
/**
* @return All known metrics.
*/
public final Iterable<Metric> loadAll() {
return new Iterable<Metric>() {
@Override
public Iterator<Metric> iterator() {
return sessionFactory.getCurrentSession()
.createQuery("from Metric as metric")
.iterate();
}
};
}
}
esto se puede usar en un código como este:
class DaoUser {
private MetricDao dao;
for (Metric existing : dao.loadAll()) {
// do stuff here...
}
}
lo que me permite usar el circuito compacto for while mientras sigo usando la memoria incremental.
Este enfoque es "flojo": el trabajo no se realiza cuando se solicita Iterable, sino solo más tarde cuando se vuelven a iterar los contenidos, y debe conocer las consecuencias de ello. En el ejemplo con un DAO eso significa iterar sobre los resultados dentro de la transacción de la base de datos.
Por lo tanto, hay varias advertencias, pero esto aún puede ser una expresión útil en muchos casos.
¿Por qué la interfaz de Iterator
no se extiende Iterable
?
El método iterator()
simplemente podría devolver this
.
¿Es a propósito o solo un descuido de los diseñadores de Java?
Sería conveniente poder usar un bucle for-each con iteradores como este:
for(Object o : someContainer.listSomeObjects()) {
....
}
donde listSomeObjects()
devuelve un iterador.
Como lo señalaron otros, Iterator
e Iterable
son dos cosas diferentes.
Además, las implementaciones de Iterator
son anteriores a los bucles mejorados.
También es trivial superar esta limitación con un método de adaptador simple que se ve así cuando se usa con importaciones de métodos estáticos:
for (String line : in(lines)) {
System.out.println(line);
}
Implementación de ejemplo:
/**
* Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
* loops. If {@link Iterable#iterator()} is invoked more than once, an
* {@link IllegalStateException} is thrown.
*/
public static <T> Iterable<T> in(final Iterator<T> iterator) {
assert iterator != null;
class SingleUseIterable implements Iterable<T> {
private boolean used = false;
@Override
public Iterator<T> iterator() {
if (used) {
throw new IllegalStateException("SingleUseIterable already invoked");
}
used = true;
return iterator;
}
}
return new SingleUseIterable();
}
En Java 8, la adaptación de un Iterator
a un Iterable
es más simple:
for (String s : (Iterable<String>) () -> iterator) {
Como un aparte: Scala tiene un método toIterable () en iterador. Ver conversión implícita o explícita de scala de iterador a iterable
Debido a que un iterador generalmente apunta a una sola instancia en una colección. Iterable implica que uno puede obtener un iterador de un objeto para recorrer sus elementos, y no hay necesidad de iterar en una sola instancia, que es lo que representa un iterador.
En aras de la simplicidad, Iterator e Iterable son dos conceptos distintos, Iterable es simplemente una abreviatura de "I can return an Iterator". Creo que tu código debería ser:
for(Object o : someContainer) {
}
con algunosContainer instanceof SomeContainer extends Iterable<Object>
En una nota relacionada, puede encontrar útil el adaptador IteradorIterable en Apache Commons Collections4. Simplemente crea una instancia desde un iterador, y tienes el iterable correspondiente.
ID: org.apache.commons: commons-collections4: 4.0
Estoy de acuerdo con la respuesta aceptada, pero quiero agregar mi propia explicación.
Iterator representa el estado de desplazamiento, por ejemplo, puede obtener el elemento actual de un iterador y avanzar al siguiente.
Iterable representa una colección que puede atravesarse, puede devolver tantos iteradores como desee, cada uno representa su propio estado de desplazamiento, un iterador puede estar apuntando al primer elemento, mientras que otro puede estar apuntando al tercer elemento.
Sería bueno si Java for loop acepta tanto Iterator como Iterable.
Increíblemente, nadie más ha dado esta respuesta todavía. A continuación, le mostramos cómo puede iterar "fácilmente" sobre un Iterator
utilizando el nuevo método Java 8 Iterator.forEachRemaining()
:
Iterator<String> it = ...
it.forEachRemaining(System.out::println);
Por supuesto, hay una solución "más simple" que funciona con el bucle foreach directamente, envolviendo el Iterator
en una lambda Iterable
:
for (String s : (Iterable<String>) () -> it)
System.out.println(s);
Los iteradores tienen estado, tienen un elemento "siguiente" y se vuelven "agotados" una vez que se repiten. Para ver dónde está el problema, ejecute el siguiente código, ¿cuántos números se imprimen?
Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);
Para mi $ 0.02, estoy totalmente de acuerdo en que Iterator no debería implementar Iterable, pero creo que el bucle for mejorado debería aceptar cualquiera. Creo que todo el argumento "make iterators iterable" aparece como una solución a un defecto en el lenguaje.
El motivo de la introducción del bucle for mejorado fue que "elimina la fatiga y la propensión a errores de los iteradores y las variables de índice al iterar sobre colecciones y matrices" [ 1 ].
Collection<Item> items...
for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
Item item = iter.next();
...
}
for (Item item : items) {
...
}
¿Por qué entonces este mismo argumento no es válido para los iteradores?
Iterator<Iter> iter...
..
while (iter.hasNext()) {
Item item = iter.next();
...
}
for (Item item : iter) {
...
}
En ambos casos, las llamadas a hasNext () y next () se han eliminado, y no hay referencia al iterador en el bucle interno. Sí, entiendo que los Iterables se pueden reutilizar para crear múltiples iteradores, pero que todo sucede fuera del ciclo for: dentro del ciclo solo hay una progresión hacia delante un ítem a la vez sobre los ítems devueltos por el iterador.
Además, permitir esto también facilitaría el uso del bucle for para Enumeraciones, que, como se ha señalado en otro lugar, son análogas a Iterators not Iterables.
Así que no hagas que Iterator implemente Iterable, pero actualiza el ciclo for para aceptar cualquiera.
Aclamaciones,
Puedes probar el siguiente ejemplo:
List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
Si vino aquí en busca de una solución alternativa, puede usar IteratorIterable . (disponible para Java 1.6 y superior)
Ejemplo de uso (invirtiendo un Vector).
import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
public static void main(String ... args) {
Vector<String> vs = new Vector<String>();
vs.add("one");
vs.add("two");
for ( String s: vs ) {
System.out.println(s);
}
Iterable<String> is
= new IteratorIterable(new ReverseListIterator(vs));
for ( String s: is ) {
System.out.println(s);
}
}
}
huellas dactilares
one
two
two
one
También veo muchos haciendo esto:
public Iterator iterator() {
return this;
}
¡Pero eso no lo hace correcto! ¡Este método no sería lo que quieres!
El método iterator()
se supone que devuelve un nuevo iterador comenzando desde cero. Entonces uno debe hacer algo como esto:
public class IterableIterator implements Iterator, Iterable {
//Constructor
IterableIterator(IterableIterator iter)
{
this.initdata = iter.initdata;
}
// methods of Iterable
public Iterator iterator() {
return new MyClass(this.somedata);
}
// methods of Iterator
public boolean hasNext() {
// ...
}
public Object next() {
// ...
}
public void remove() {
// ...
}
}
La pregunta es: ¿habría alguna forma de hacer que una clase abstracta realice esto? De modo que para obtener un IterableIterator solo necesita implementar los dos métodos next () y hasNext ()
Un iterador es con estado. La idea es que si llamas a Iterable.iterator()
dos veces obtendrás iteradores independientes , para la mayoría de los iterables, de todos modos. Eso claramente no sería el caso en su escenario.
Por ejemplo, generalmente puedo escribir:
public void iterateOver(Iterable<String> strings)
{
for (String x : strings)
{
System.out.println(x);
}
for (String x : strings)
{
System.out.println(x);
}
}
Eso debería imprimir la colección dos veces, pero con su esquema, el segundo ciclo siempre terminaría instantáneamente.
Iterator
es una interfaz que le permite iterar sobre algo. Es una implementación de moverse a través de una colección de algún tipo.
Iterable
es una interfaz funcional que denota que algo contiene un iterador accesible.
En Java8, esto hace que la vida sea más fácil ... Si tienes un Iterator
pero necesitas un Iterable
, simplemente puedes:
Iterator<T> someIterator;
Iterable<T> = ()->someIterator;
Esto también funciona en el for-loop:
for (T item : ()->someIterator){
//doSomething with item
}