recorrer que listas linkedlist for java syntax iterator language-design iterable

que - recorrer linkedlist java



¿Por qué Java no permite foreach en iteradores(solo en iterables)? (5)

¿Alguien sabe por qué el lenguaje fue diseñado de esta manera?

Porque para-cada uno solo tiene sentido sobre las cosas que son iter capaces , y no tiene sentido sobre los interesados . Si ya tiene un iterador, ya tiene lo que necesita para hacer esto con un simple bucle.

Compare: comienzo con un iter capaz :

// Old way Iterator<Thingy> it = iterable.iterator(); while (it.hasNext()) { Thingy t = it.next(); // Use `t` } // New way for (Thingy t : iterable) { // Use `t` }

Versus Comienzo con un iterador:

// Old/current way while (iterator.hasNext()) { Thing t = iterator.next(); // Use `t` } // Imagined way for (Thingy t : iterator) { // Use `t` }

No hay mucho en esto en el segundo ejemplo, y complica la semántica de cada uno al crear un caso especial.

Las preguntas "por qué" siempre son difíciles cuando no están dirigidas a los principales participantes que participaron en la decisión, pero creo que la complejidad adicional no valía la utilidad marginal.

Posible duplicado:
¿Por qué el iterador de Java no es Iterable?

¿Forma idiomática de usar para cada bucle dado un iterador?

¿Podemos usar for-each loop para iterar los objetos del tipo Iterator?

El bucle foreach es, por lo que sé, la sintaxis añadida en Java 5. Entonces

Iterable<O> iterable; for(O o : iterable) { // Do something }

esencialmente producirá el mismo bytecode como

Iterable<O> iterable; for(Iterator<O> iter = iterable.iterator(); iter.hasNext(); /* NOOP */) { O o = iter.next(); // Do something }

Sin embargo, si no tengo un iterable en primer lugar, sino solo un iterador (digamos, porque una clase ofrece dos iteradores diferentes), no puedo usar el ciclo de sintaxis sugar foreach. Obviamente, todavía puedo hacer la iteración simple de estilo antiguo. Sin embargo, realmente me gustaría hacer:

Iterator<O> iter; for(O o : iter /* Iterator<O>, not Iterable<O>! */) { // Do something }

Y, por supuesto, puedo hacer un Iterable falso:

class Adapter<O> implements Iterable<O> { Iterator<O> iter; public Adapter(Iterator<O> iter) { this.iter = iter; } @Override public Iterator<O> iterator() { return iter; } }

(¡Lo que de hecho es un feo abuso de la API Iterable, ya que solo se puede repetir una vez!)

Si se diseñara alrededor de Iterator lugar de iterable, se podrían hacer varias cosas interesantes:

for(O o : iterable.iterator()) {} // Iterate over Iterable and Collections for(O o : list.backwardsIterator()) {} // Or backwards Iterator<O> iter; for(O o : iter) { if (o.something()) { iter.remove(); } if (o.something()) { break; } } for(O : iter) { } // Do something with the remaining elements only.

¿Alguien sabe por qué el lenguaje fue diseñado de esta manera? ¿Para evitar la ambigüedad si una clase implementara Iterator e Iterable ? Para evitar errores del programador que supongan que "para (O o: iter)" procesará todos los elementos dos veces (y se olvida de obtener un iterador nuevo)? ¿O hay alguna otra razón para esto?

¿O hay algún truco de idioma que simplemente no sé?


Creo que una parte de la respuesta puede estar oculta en el hecho de que el bucle for-each es azúcar sintáctico. El punto es que quieres hacer algo que la gente haga mucho, mucho más fácil. Y (al menos en mi experiencia) el idioma

Iterator iterator = iterable.iterator(); while( iterator.hasNext() ) { Element e = (Element)iterator.next() ; }

ocurrió todo el tiempo en el código antiguo. Y hacer cosas sofisticadas con iteradores múltiples no.


Entonces tengo una explicación algo razonable ahora:

Versión corta: porque la sintaxis también se aplica a las matrices , que no tienen iteradores.

Si la sintaxis se diseñó alrededor de Iterator como propuse, sería inconsistente con las matrices. Déjame darte tres variantes:

A) según lo elegido por los desarrolladores de Java:

Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list) { } Iterator<Object> iter; while(iter.hasNext()) { Object o = iter.next(); }

Se comporta de la misma manera y es altamente consistente en las matrices y colecciones. Los iteradores, sin embargo, tienen que usar el estilo de iteración clásico (que al menos no es probable que cause errores).

B) permitir matrices e Iterators :

Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list.iterator()) { } Iterator<Object> iter; for(Object o : iter) { }

Ahora las matrices y las colecciones son inconsistentes; pero las matrices y ArrayList están muy relacionadas y deberían comportarse de la misma manera. Ahora, si en algún momento, el idioma se extiende para hacer que las matrices implementen Iterable , se vuelve inconsistente.

C) permite los tres:

Object[] array; for(Object o : array) { } Iterable<Object> list; for(Object o : list) { } Iterator<Object> iter; for(Object o : iter) { }

Ahora, si terminamos en situaciones poco claras cuando alguien implementa Iterable e Iterator (se supone que el bucle for obtiene un nuevo iterador o itera sobre el actual, ¡ocurre fácilmente en estructuras tipo árbol!?!). Un simple tie-braker ala "Iterable beats Iterator" lamentablemente no funcionará: de repente introduce el tiempo de ejecución frente a la diferencia de tiempo de compilación y los problemas genéricos.

Ahora, de repente, debemos prestar atención a si queremos iterar sobre colecciones / iterables o matrices, en cuyo punto hemos obtenido muy pocos beneficios a costa de una gran confusión.

La forma en que "para cada uno" está en Java (A) es muy consistente, causa muy pocos errores de programación y permite el posible cambio futuro de convertir las matrices en objetos regulares.

Hay una variante D) que probablemente también funcionaría bien: para cada uno solo para Iteradores. Preferiblemente al agregar un método .iterator() a matrices primitivas:

Object[] array; for(Object o : array.iterator()) { } Iterable<Object> list; for(Object o : list.iterator()) { } Iterator<Object> iter; for(Object o : iter) { }

Pero esto requiere cambios en el entorno de ejecución, no solo en el compilador, y rompe la compatibilidad con versiones anteriores. Además, la confusión mencionada todavía está presente que

Iterator<Object> iter; for(Object o : iter) { } for(Object o : iter) { }

Solo itera sobre los datos una vez.


La interfaz Iterable se creó exactamente para ese propósito (mejorada para el bucle) como se describe en el JSR original , aunque la interfaz del iterador ya estaba en uso.


Porque el bucle "for" sería destructivo para el iterador. El iterador no se puede reiniciar (es decir, volver al principio) a menos que implemente la subinterfaz ListIterator.

Una vez que coloque un iterador a través de un bucle "for", ya no podrá usarlo. Creo que los diseñadores de idiomas decidieron que esto combinado con los casos especiales adicionales (de los cuales ya hay dos para Iterable y matrices) en el compilador para traducir esto en bytecode (no se podía reutilizar la misma transformación como iterable) era suficiente un detractor para no implementarlo.

Cuando hagas esto tú mismo en el código a través de la interfaz del iterador, al menos será aparentemente obvio lo que está sucediendo.

Con las lambdas viniendo, podrían hacer que esto sea agradable y fácil:

Iterator<String> iterator = ...; Collections.each ( iterator, (String s) => { System.out.println(s); } ); List<String> list = ...; Collections.each ( list, (String s) => { System.out.println(s); } );

sin romper la compatibilidad hacia atrás, y aún tener una sintaxis relativamente simple. Dudo que construyan métodos como "cada", "recopilar" y "mapear" en las diferentes interfaces porque eso rompería la compatibilidad hacia atrás, y además tendrías matrices que resolver.