2011-09-12 20 views
65

Digamos que tengo un conjunto de enteros, y quiero incrementar cada entero en el conjunto. ¿Cómo haría esto?¿Cómo iterar y modificar los conjuntos de Java?

¿Puedo agregar y quitar elementos del conjunto mientras lo repito?

¿Tendría que crear un nuevo conjunto en el que "copiaría y modificaría" los elementos, mientras estoy iterando el conjunto original?

EDITAR: ¿Qué pasa si los elementos del conjunto son inmutables?

+0

debe seleccionar una respuesta adecuada – MarianP

Respuesta

81

Puede eliminar con seguridad de un conjunto durante la iteración con un objeto iterador; intentar modificar un conjunto a través de su API mientras se itera romperá el iterador. la clase Set proporciona un iterador a través de getIterator().

sin embargo, los objetos enteros son inmutables; Mi estrategia sería iterar a través del conjunto y para cada Entero i, agregar i + 1 a un nuevo conjunto temporal. Cuando termine de iterar, elimine todos los elementos del conjunto original y agregue todos los elementos del nuevo conjunto temporal.

Set<Integer> s; //contains your Integers 
... 
Set<Integer> temp = new Set<Integer>(); 
for(Integer i : s) 
    temp.add(i+1); 
s.clear(); 
s.addAll(temp); 
+1

no sería sólo más fácil dejar el conjunto original al recolector de basura y continuar usando el nuevo conjunto? ¿O el conjunto temporal se recopila o el conjunto original, el mío, así como simplemente mantener el conjunto temporal y no molestar a la mutación del original? Sin embargo, esto no fue posible en mi caso porque el set original era final, así que acepté. – Buttons840

+0

@JonathanWeatherhead ¿Querías decir que la segunda palabra es 'cannot' en lugar de' can'? Como en, "No se puede eliminar de forma segura" en lugar de "Puedes eliminar de forma segura". Parece una contradicción en ese primer párrafo. –

+0

@BasilBourque no, quise decir "puedo". El método Iterator :: remove() es seguro, como el estado de los documentos. https://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html –

0

Se puede crear un envoltorio mutable de la int primitivo y crear un conjunto de aquellos:

class MutableInteger 
{ 
    private int value; 
    public int getValue() 
    { 
     return value; 
    } 
    public void setValue(int value) 
    { 
     this.value = value; 
    } 
} 

class Test 
{ 
    public static void main(String[] args) 
    { 
     Set<MutableInteger> mySet = new HashSet<MutableInteger>(); 
     // populate the set 
     // .... 

     for (MutableInteger integer: mySet) 
     { 
      integer.setValue(integer.getValue() + 1); 
     } 
    } 
} 

Por supuesto, si usted está usando un HashSet se debe implementar el hash, es igual método en su MutableInteger pero que está fuera el alcance de esta respuesta.

36

Puede hacer lo que desee si utiliza un objeto iterador para revisar los elementos de su conjunto. Puedes eliminarlos sobre la marcha y está bien. Sin embargo la eliminación de ellos, mientras que en un bucle ("estándar", de la de cada especie) que se meterán en problemas:

Set<Integer> set = new TreeSet<Integer>(); 
    set.add(1); 
    set.add(2); 
    set.add(3); 

    //good way: 
    Iterator<Integer> iterator = set.iterator(); 
    while(iterator.hasNext()) { 
     Integer setElement = iterator.next(); 
     if(setElement==2) { 
      iterator.remove(); 
     } 
    } 

    //bad way: 
    for(Integer setElement:set) { 
     if(setElement==2) { 
      //might work or might throw exception, Java calls it indefined behaviour: 
      set.remove(setElement); 
     } 
    } 

Según comentario de @ mrgloom, Aquí hay más detalles de por qué el "malo" La forma descrita arriba es, bueno ... mala:

Sin entrar en demasiados detalles sobre cómo Java implementa esto, en un nivel alto, podemos decir que la "mala" manera es mala porque está claramente estipulada como tal en la documentación de Java:

https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html

estipulan, entre otras cosas, de que (el énfasis es mío):

"Por ejemplo, generalmente no es permisible para un hilo de modificar una colección mientras que otro hilo está interactuando sobre ella. En en general, los resultados de la iteración no están definidos en estas circunstancias . Algunas implementaciones Iterator (incluyendo los de todos las implementaciones de recogida de propósito general que proporciona el JRE) pueden optar por lanzar esta excepción si se detecta este comportamiento "(...)

" Tenga en cuenta que esta excepción no siempre indican que un objeto ha sido modificado simultáneamente por un hilo diferente. Si un único hilo emite una secuencia de invocaciones de método que infringe el contrato de un objeto, el objeto puede lanzar esta excepción. Para ejemplo, si un hilo modifica una colección directamente si bien es iterar sobre la colección con un iterador fail-fast, el iterador arrojará esta excepción "

Para ir más en detalles:. Un objeto que se puede usar en un ciclo forEach necesita implementar la interfaz "java.lang.Iterable" (javadoc here). Esto produce un Iterator (a través del método "Iterator" que se encuentra en esta interfaz), que se instancia a petición y lo hará contienen internamente una referencia al objeto Iterable desde el que se creó. Sin embargo, cuando se utiliza un objeto Iterable en un ciclo forEach, la instancia de este iterador está oculta para el usuario (no puede acceder a ella usted mismo). de cualquier manera).

Esto, junto con el hecho de que un iterador es bastante estable, es decir, para hacer su magia y tener respuestas coherentes para sus métodos "siguiente" y "tiene ahora", necesita que el objeto de respaldo no cambie por otra cosa que el iterador mismo mientras está iterando, lo hace de modo que arroje una excepción tan pronto como detecte que algo cambió en el objeto de respaldo mientras se itera sobre él.

Java llama a esta iteración "a prueba de fallos": es decir, hay algunas acciones, normalmente aquellas que modifican una instancia Iterable (mientras un iterador está iterando sobre ella). La parte de "falla" de la noción de "falla rápida" se refiere a la capacidad de un iterador para detectar cuándo ocurren tales acciones de "falla". La parte "rápida" del "fail-fast" (y, que en mi opinión debería llamarse "best-effort-fast"), terminará la iteración mediante ConcurrentModificationException tan pronto como como puede detectar que un " fallar "la acción ha sucedido.

+1

¿Puedes aclarar por qué falla el mal camino? – mrgloom

+0

@mrgloom He agregado más detalles sobre, básicamente, por qué se puede lanzar una ConcurrentModificationException incluso cuando solo hay un hilo operando en un objeto Iterable –

+0

Para algunos casos también tienes java.lang.UnsupportedOperationException – Aguid

4

no me gusta mucho de semántica iterador, por favor considere esto como una opción. También es más seguro, ya publicada menos de su estado interno

private Map<String, String> JSONtoMAP(String jsonString) { 

    JSONObject json = new JSONObject(jsonString); 
    Map<String, String> outMap = new HashMap<String, String>(); 

    for (String curKey : (Set<String>) json.keySet()) { 
     outMap.put(curKey, json.getString(curKey)); 
    } 

    return outMap; 

} 
Cuestiones relacionadas