Al ser algo nuevo en el lenguaje Java, estoy tratando de familiarizarme con todas las formas (o al menos las no patológicas) que uno podría recorrer en iteración a través de una lista (o quizás otras colecciones) y las ventajas o desventajas de cada una.
Dado un List<E> list objeto, conozco las siguientes formas de recorrer todos los elementos:
Básico para bucle (por supuesto, también hay bucles while/ equivalentes do while)
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
Nota: Como señaló @amarseillan, este formulario es una mala elección para iterar sobre Lists, porque la implementación real del getmétodo puede no ser tan eficiente como cuando se usa un Iterator. Por ejemplo,LinkedList implementaciones deben atravesar todos los elementos que preceden a i para obtener el elemento i-ésimo.
En el ejemplo anterior, no hay forma de que la Listimplementación "guarde su lugar" para que las iteraciones futuras sean más eficientes. Para un ArrayListno importa realmente, porque la complejidad / costo de getes tiempo constante (O (1)) mientras que para a LinkedListes proporcional al tamaño de la lista (O (n)).
Para obtener más información sobre la complejidad computacional de las Collectionsimplementaciones integradas , consulte esta pregunta .
Mejorado para bucle (bien explicado en esta pregunta )
for (E element : list) {
// 1 - can call methods of element
// ...
}
Iterador
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
ListIterator
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
Java funcional
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach , ...
(Un método de mapa de la API Stream de Java 8 (consulte la respuesta de @ i_am_zero)).
En Java 8, las clases de colección que implementan Iterable(por ejemplo, todas las Lists) ahora tienen un forEachmétodo, que se puede utilizar en lugar de la instrucción for loop mostrada anteriormente. (Aquí hay otra pregunta que proporciona una buena comparación).
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
¿Qué otras formas hay, si hay alguna?
(Por cierto, mi interés no proviene en absoluto del deseo de optimizar el rendimiento ; solo quiero saber qué formularios están disponibles para mí como desarrollador).

Listinterfaz específicamente?Respuestas:
Las tres formas de bucle son casi idénticas. El
forbucle mejorado :es, de acuerdo con la Especificación del lenguaje Java , idéntico en efecto al uso explícito de un iterador con un
forbucle tradicional . En el tercer caso, solo puede modificar el contenido de la lista eliminando el elemento actual y, luego, solo si lo hace a través deremovemétodo del iterador. Con la iteración basada en índices, puede modificar la lista de cualquier forma. Sin embargo, agregar o eliminar elementos que vienen antes del índice actual corre el riesgo de que su ciclo omita elementos o procese el mismo elemento varias veces; debe ajustar el índice de bucle correctamente cuando realice dichos cambios.En todos los casos,
elementes una referencia al elemento de lista real. Ninguno de los métodos de iteración hace una copia de nada en la lista. Los cambios en el estado interno deelementsiempre se verán en el estado interno del elemento correspondiente en la lista.Esencialmente, solo hay dos formas de iterar sobre una lista: usando un índice o usando un iterador. El bucle for mejorado es solo un atajo sintáctico introducido en Java 5 para evitar el tedio de definir explícitamente un iterador. Para ambos estilos, se puede llegar a las variaciones esencialmente triviales utilizando
for,whileodo whilebloques, pero todas se reducen a lo mismo (o, más bien, dos cosas).EDITAR: Como @ iX3 señala en un comentario, puede usar a
ListIteratorpara establecer el elemento actual de una lista a medida que itera. Debería usar enList#listIterator()lugar deList#iterator()inicializar la variable de bucle (que, obviamente, debería declararse como un enListIteratorlugar de unIterator).fuente
e = iterator.next()entonces,e = somethingElsesimplemente cambia a qué objetoese refiere en lugar de cambiar la tienda real de la queiterator.next()recuperó el valor.eno cambiará lo que está en la lista; usted tiene que llamarlist.set(index, thing). Puede cambiar el contenido dee(por ejemplo,e.setSomething(newValue)), pero para cambiar qué elemento está almacenado en la lista a medida que lo itera, debe seguir con una iteración basada en índices.e, tendría que llamar a uno deelos métodos porque la asignación solo cambia el puntero (perdón por mi C).IntegeryStringno sería posible cambiar el contenido usandofor-eachoIteratormétodos, tendría que manipular el objeto de la lista para reemplazar a los elementos. ¿Está bien?Ejemplo de cada tipo enumerado en la pregunta:
ListIterationExample.java
fuente
No se recomienda el bucle básico, ya que no conoce la implementación de la lista.
Si esa era una LinkedList, cada llamada a
estaría iterando sobre la lista, resultando en una complejidad de tiempo N ^ 2.
fuente
Una iteración de estilo JDK8:
fuente
En Java 8 tenemos múltiples formas de iterar sobre las clases de colección.
Usando Iterable para cada uno
Las colecciones que implementan
Iterable(por ejemplo, todas las listas) ahora tienenforEachmétodo. Podemos usar el método de referencia introducido en Java 8.Uso de secuencias para cada uno y para cada pedido
También podemos iterar sobre una lista usando Stream como:
Deberíamos preferir
forEachOrderedmás queforEachporque el comportamiento deforEaches explícitamente no determinista, ya que a medida queforEachOrderedrealiza una acción para cada elemento de esta secuencia, en el orden de encuentro de la secuencia si la secuencia tiene un orden de encuentro definido. Por lo tanto, forEach no garantiza que se mantenga el pedido.La ventaja de las transmisiones es que también podemos hacer uso de transmisiones paralelas siempre que sea apropiado. Si el objetivo es solo imprimir los artículos independientemente del pedido, entonces podemos usar la transmisión paralela como:
fuente
No sé qué considera patológico, pero permítame brindarle algunas alternativas que no podría haber visto antes:
O su versión recursiva:
Además, una versión recursiva de lo clásico
for(int i=0...:Los menciono porque eres "algo nuevo en Java" y esto podría ser interesante.
fuente
while(!copyList.isEmpty()){ E e = copyList.remove(0); ... }. Eso es más efectivo que la primera versión;).Puede usar forEach a partir de Java 8:
fuente
Para una búsqueda hacia atrás debe usar lo siguiente:
Si desea conocer una posición, use iterator.previousIndex (). También ayuda a escribir un bucle interno que compara dos posiciones en la lista (los iteradores no son iguales).
fuente
Correcto, se enumeran muchas alternativas. Lo más fácil y más limpio sería simplemente usar la
fordeclaración mejorada como se muestra a continuación. ElExpressiones de algún tipo que es iterable.Por ejemplo, para recorrer en iteración los ID de lista <String>, podemos simplemente así,
fuente
En
java 8puede usar elList.forEach()método conlambda expressionpara iterar sobre una lista.fuente
eugene82la respuesta yi_am_zerola respuesta de ?Siempre puede cambiar el primer y el tercer ejemplo con un ciclo while y un poco más de código. Esto le brinda la ventaja de poder usar do-while:
Por supuesto, este tipo de cosas podría causar una NullPointerException si list.size () devuelve 0, porque siempre se ejecuta al menos una vez. Esto se puede solucionar probando si el elemento es nulo antes de usar sus atributos / métodos. Aún así, es mucho más simple y fácil usar el bucle for
fuente