enhanced - for loop java español
for-each vs for vs while (4)
En respuesta a este comentario del OP.
Sin embargo, se requiere el # 1 cuando se actualiza (si no solo se muda el elemento actual o se crean los resultados como una nueva lista) y se incluye con el índice. Como List <> es una ArrayList <> en este caso, get () (y size ()) es O (1), pero no es lo mismo para todos los tipos de contrato de lista.
Veamos estos problemas:
Ciertamente, es cierto que get(int)
no es O(1)
para todas las implementaciones del contrato de la List
. Sin embargo, AFAIK, size()
es O(1)
para todas las implementaciones de List
en java.util
. Pero tiene razón en que el # 1 no es óptimo para muchas implementaciones de List
. De hecho, para listas como LinkedList
donde get(int)
es O(N)
, el enfoque # 1 resulta en una iteración de lista O(N^2)
.
En el caso de ArrayList
, es una cuestión simple elevar manualmente la llamada a size()
, asignándola a una variable local (final). Con esta optimización, el código n. ° 1 es significativamente más rápido que los otros casos ... para ArrayList
s.
Su idea de cambiar la lista mientras itera los elementos plantea una serie de problemas:
Si hace esto con una solución que explícita o implícitamente utiliza iteradores, entonces, dependiendo de la clase de lista, puede obtener
ConcurrentModificationException
s. Si usa una de las clases de colección simultáneas, no obtendrá la excepción, pero los javadocs afirman que el iterador no devolverá necesariamente todos los elementos de la lista.Si haces esto usando el código n. ° 1 (como está) entonces, tienes un problema. Si la modificación se realiza con el mismo hilo, debe ajustar la variable de índice para evitar entradas faltantes o devolverlas dos veces. Incluso si obtiene todo bien, una entrada de lista insertada simultáneamente antes de la posición actual no se mostrará.
Si la modificación en el caso n. ° 1 se realiza con un hilo diferente, es difícil sincronizarlo correctamente. El problema principal es que
get(int)
ysize()
son operaciones separadas. Incluso si están sincronizados individualmente, no hay nada que impida que el otro hilo modifique la lista entre unsize
yget
llamada.
En resumen, iterar una lista que se está modificando al mismo tiempo es complicado y debe evitarse ... a menos que realmente sepa lo que está haciendo .
Me pregunto cuál es la mejor manera de implementar un bucle "para-cada" sobre un ArrayList o cada tipo de lista.
¿Cuál de las siguientes implementaciones es la mejor y por qué? ¿O hay una mejor manera?
Gracias por tu ayuda.
values.add("one");
values.add("two");
values.add("three");
... //#0 //#1 //#2 //#3
List values = new ArrayList();
for(String value : values) {
...
}
for(int i = 0; i < values.size(); i++) {
String value = values.get(i);
...
}
for(Iterator it = values.iterator(); it.hasNext(); ) {
String value = it.next();
...
}
Iterator it = values.iterator();
while (it.hasNext()) {
String value = (String) it.next();
...
}
# 0 es el más fácil de leer, en mi opinión, pero el # 2 y el # 3 funcionarán igual de bien. No debe haber diferencia de rendimiento entre esos tres.
En casi ninguna circunstancia deberías usar # 1. Usted dice en su pregunta que es posible que desee iterar sobre "todo tipo de lista". Si usted está iterando sobre una lista LinkedList
entonces # 1 será n ^ 2 complejidad: no es bueno. Incluso si está absolutamente seguro de que está utilizando una lista que admite un acceso aleatorio eficiente (por ejemplo, ArrayList
), generalmente no hay ninguna razón para usar el # 1 sobre ninguno de los otros.
# 3 tiene una desventaja porque el alcance del iterador it
extiende más allá del final del ciclo. Las otras soluciones no tienen este problema.
# 2 es exactamente lo mismo que # 0, excepto que # 0 es más legible y menos propenso a error.
# 1 es (probablemente) menos eficiente porque llama a .size()
cada vez que .size()
el ciclo.
# 0 generalmente es mejor porque:
- es el más corto
- es menos propenso al error
- es idiomático y fácil de leer para otras personas de un vistazo
- es implementado eficientemente por el compilador
- no contamina el alcance de su método (fuera del ciclo) con nombres innecesarios
La respuesta breve es usar la versión 0. Eche un vistazo al título de la sección Use Enhanced For Loop Syntax en la documentación de Android para Designing for Performance . Esa página tiene muchas cosas buenas y es muy clara y concisa.