for example collection java collections iteration

collection - iterator java example



ArrayList.remove da un resultado diferente cuando se llama como Collection.remove (3)

Ambos fragmentos se rompen de diferentes maneras!

Caso 1 (con la Collection<String> col ):

Dado que una Collection no está indexada, el único método de remove que expone su interfaz es Collection.remove(Object o) , que elimina el objeto igual especificado. Haciendo col.remove(1); primero llama a Integer.valueOf(1) para obtener un objeto Integer , luego pide a la lista que elimine ese objeto. Como la lista no contiene ningún objeto Integer , no se elimina nada. La iteración continúa normalmente a través de la lista y se imprime abc .

Caso 2 (con ArrayList<String> col ):

Cuando el tipo de tiempo de compilación de col es ArrayList , se llama col.remove(1); en su lugar, invoca el método ArrayList.remove(int index) para eliminar el elemento en la posición especificada, eliminando así b .

Ahora, ¿por qué no se imprime c ? Para recorrer una colección con la sintaxis for (X : Y) , detrás de escena llama a la colección para obtener un objeto Iterator . Para el Iterator devuelto por un ArrayList (y la mayoría de las colecciones) no es seguro realizar modificaciones estructurales a la lista durante la iteración , a menos que lo modifique a través de los métodos del propio Iterator , ya que el Iterator se confundirá y perderá la pista de qué elemento para volver a continuación. Esto puede hacer que los elementos se repitan varias veces, se omitan elementos u otros errores. Eso es lo que sucede aquí: el elemento c está presente en la lista pero nunca se imprime porque confundiste el Iterator .

Cuando un Iterator puede detectar que ha ocurrido este problema, se lo advertirá lanzando una ConcurrentModificationException . Sin embargo, la verificación que hace un Iterator para el problema está optimizada para la velocidad, no al 100% de corrección, y no siempre detecta el problema. En su código, si cambia s.equals("b") por s.equals("a") o s.equals("c") , arroja la excepción (aunque esto puede depender de la versión particular de Java) . De la documentación de ArrayList :

Los iteradores devueltos por el iterador de esta clase y los métodos listIterator son a prueba de fallas : si la lista se modifica estructuralmente en cualquier momento después de que se crea el iterador, de cualquier forma, excepto a través de los propios métodos remove o add del iterador, el iterador emitirá una excepción ConcurrentModificationException . Por lo tanto, ante una modificación concurrente, el iterador falla de manera rápida y limpia, en lugar de arriesgarse a comportamientos arbitrarios y no deterministas en un momento indeterminado en el futuro.

Tenga en cuenta que el comportamiento a prueba de fallas de un iterador no se puede garantizar ya que, en términos generales, es imposible hacer ninguna garantía en presencia de modificaciones concurrentes no sincronizadas. Los iteradores rápidos de fallos lanzan ConcurrentModificationException el mejor esfuerzo.

Para eliminar elementos durante la iteración, debe cambiar el estilo for (X : Y) del bucle en un bucle manual sobre un Iterator explícito, utilizando su método de remove :

for (Iterator<String> it = col.iterator(); it.hasNext();) { String s = it.next(); if (s.equals("b")) it.remove(); System.out.print(s); }

Esto ahora es completamente seguro. Ititerará todos los elementos exactamente una vez (imprimiendo abc ), mientras que el elemento b se eliminará.

Si lo desea, puede lograr el mismo efecto sin un Iterator utilizando un bucle int i style, si ajusta el índice con cuidado después de cualquier eliminación:

for (int i = 0; i < col.size(); i++) { String s = col.get(i); if (s.equals("b")) { col.remove(i); i--; } System.out.print(s); }

Este codigo

Collection<String> col = new ArrayList<String>(); col.add("a"); col.add("b"); col.add("c"); for(String s: col){ if(s.equals("b")) col.remove(1); System.out.print(s); }

impresiones: abc

Mientras tanto éste:

ArrayList<String> col = new ArrayList<String>(); col.add("a"); col.add("b"); col.add("c"); for(String s: col){ if(s.equals("b")) col.remove(1); System.out.print(s); }

impresiones: ab

Sin embargo, debería imprimir el mismo resultado ... ¿Cuál es el problema?


Para eliminar de forma segura de una Collection mientras se itera sobre ella, debe usar un Iterator .

ArrayList<String> col = new ArrayList<String>(); col.add("a"); col.add("b"); col.add("c"); Iterator<String> i = col.iterator(); while (i.hasNext()) { String s = i.next(); // must be called before you can call remove if(s.equals("b")) i.remove(); System.out.print(s); }

En cuanto a, la razón por la cual la eliminación de la colección no le funciona mientras ArrayList funcionó es por lo siguiente:

  1. El método java.util.ArrayList.remove(int index) elimina el elemento en la posición especificada en esta lista. Desplaza cualquier elemento posterior a la izquierda (resta uno de sus índices). Por lo tanto, este funcionó para usted.

  2. El método java.util.Collection.remove(Object o) elimina una instancia única del elemento especificado de esta colección, si está presente (es una operación opcional). Más formalmente, elimina un elemento e tal que (o==null ? e==null : o.equals(e)) , si esta colección contiene uno o más de estos elementos. Devuelve true si esta colección contenía el elemento especificado (o de manera equivalente, si esta colección cambió como resultado de la llamada).

Espero que esto ayude.


Collection solo tiene el método boolean remove(Object o) , que elimina el objeto pasado si se encuentra.

ArrayList también tiene public E remove(int index) , que puede eliminar un elemento por su índice.

Su primer fragmento llama a boolean remove(Object o) , que no elimina nada, ya que su ArrayList no contiene 1 . Su segundo fragmento llama a public E remove(int index) y elimina el elemento cuyo índice era 1 (es decir, elimina "b" ).

El comportamiento diferente se debe al hecho de que la resolución de la sobrecarga del método se produce en el momento de la compilación y depende del tipo de tiempo de compilación de la variable para la que está llamando el método. Cuando el tipo de col es Collection , solo se tienen en cuenta para la resolución de sobrecargas los métodos de remove de la interfaz de Collection (y los métodos heredados por esa interfaz).

Si reemplaza col.remove(1) con col.remove("b") , ambos fragmentos se comportarán igual.

Como comentó Tamoghna Chowdhury, la boolean remove(Object o) puede aceptar un argumento primitivo, int en su caso, debido al auto-boxing del int a una instancia de Integer . Para el segundo fragmento, la razón por public E remove(int index) se elige la public E remove(int index) sobre la boolean remove(Object o) es que el proceso de resolución de sobrecarga del método intenta primero encontrar un método coincidente sin hacer conversiones de auto-boxing / unboxing, por lo que solo considera public E remove(int index) .