2012-06-19 13 views
7

Deseo recorrer un conjunto, pero el contenido del conjunto modificaré durante su iteración. Deseo iterar sobre el conjunto original en el momento en que se creó el iterador y no iterar sobre ninguno de los nuevos elementos agregados al conjunto. ¿Cómo es esto posible? ¿Este es el comportamiento predeterminado del conjunto o cómo puedo lograr esto?Java: recorrer un conjunto de contenidos, mientras se están modificando conjunto

Una forma que se me ocurre es conseguir un nuevo juego de la serie original que no será modificado pero esto parece poco elegante y tiene que haber una solución mejor.

+2

La forma en que propones parece estar bien. – assylias

+1

Para aclarar: ¿es de subproceso único o multiproceso? – templatetypedef

+0

multiproceso. Un hilo está iterando, otro hilo está cambiando el conjunto. No deseo suspender ninguno de los hilos por problemas de rendimiento. – nomel7

Respuesta

8

Tomar una instantánea del conjunto suena como la solución adecuada para mí, si usted quiere asegurarse de que no hay elementos nuevos. Hay algunos sistemas, tales como ConcurrentSkipListSet que permitirán a mantener la iteración, pero no pueden ver ninguna garantía de todo comportamiento de un iterador en términos de ver nuevos elementos.

EDIT: CopyOnWriteArraySet tiene los requisitos que necesita, pero las escrituras son costosas, lo que parece que no es apropiado para usted.

Esos son los únicos conjuntos que puedo ver en java.util.concurrent, que es el paquete natural para este tipo de colecciones. Tomando una copia sigue siendo probable que sea más sencillo :)

+0

Depende. Si no se requiere la instantánea (no sucede que nadie inserte mientras está iterando), CopyOnWriteArraySet será más rápido. Por lo tanto, depende de la frecuencia con la que haya colisiones reales. – user949300

+0

@ user949300: No sé los detalles de cuándo CopyOnWriteArraySet realmente requiere tomar una copia, pero la documentación dice que es "por lo general" (lo que sea que eso signifique). Sería bueno si solo copiara cuando realmente, realmente necesario ... –

7

EDIT: Esta respuesta fue diseñada para un caso de un solo hilo, ya que había interpretado la pregunta del OP como evitar la comodificación en lugar de evitar problemas de multihilo. Dejo esta respuesta aquí en caso de que termine siendo útil para cualquier persona en el futuro que esté usando un enfoque de subproceso único.

No hay una manera directa de lograr esto. Sin embargo, una opción que es bastante agradable es tener dos conjuntos: el conjunto principal, sobre el cual se itera, y un conjunto secundario en el que se insertan todos los elementos nuevos que se deben agregar. Luego puede iterar sobre el conjunto primario, y luego, una vez que haya terminado, vaya y use addAll para agregar todos los elementos nuevos al conjunto primario.

Por ejemplo:

Set<T> masterSet = /* ... */ 

Set<T> newElems = /* ... */ 
for (T obj: masterSet) { 
    /* ... do something to each object ... */ 
} 

masterSet.addAll(newElems); 

Espero que esto ayude!

+1

Me gusta este método porque (a) produce menos objetos temporales que la copia de todo el conjunto original y (b) evita una gran sobrecarga de simultaneidad que podría no necesitar. El 'No puedo ver ninguna garantía sobre el comportamiento de un iterador en términos de ver elementos nuevos' puede ser un problema para' ConcurrentSkipListSet' –

+0

No estoy seguro de cómo puede funcionar esto. ¿Cómo sabe el segundo hilo que tiene que agregarse a newElems, no a masterSet ??? Y, si NO está iterando, ¿quién sabe para fusionar newElems en masterSet? – user949300

+0

@ user949300- Asumiría que cualquier código que haya, que agregue elementos al conjunto, podría saber qué es lo que establecen los nuevos elementos. También tenga en cuenta que la pregunta de OP no dice nada sobre multihilo; Creo que el problema era la comodificación en lugar de la concurrencia. Sería muy fácil comunicar esta nueva información a otros hilos si existieran. – templatetypedef

2

Hacer una copia de la Set es la solución elegante.

Set<Obj> copyOfObjs = new HashSet<Obj>(originalSet); 
for(Obj original : originalSet) { 
    //add some more stuff to copyOfObjs 
} 
0

Ahora que la OP ha aclarado los requisitos, las soluciones son

  1. Copia el conjunto antes de la iteración
  2. Uso CopyOnWriteArraySet
  3. Escriba su propio código personalizado y tratar de ser más inteligente que muchos de personas inteligentes.

El inconveniente de # 1 es que siempre copiar el conjunto, incluso si no puede ser necesaria (por ejemplo, si no hay inserciones ocurren realmente mientras que usted está iterando) me gustaría sugerir la opción # 2, a menos que se pruebe que frecuentes las inserciones están causando un problema de rendimiento real.

0

Como han sugerido otros aquí, no existe una solución óptima para lo que busca. Todo depende del uso-caso de su aplicación, o el uso del conjunto
Dado que Set es una interfaz, puede definir su propia clase DoubleSet que implementará Set y digamos que usará dos campos HashSet.
Al recuperar un iterador, debe marcar uno de estos conjuntos para estar en "iteración único modo", por lo que el método add agregará solamente al otro conjunto


Todavía estoy nuevo a Stackoverlflow, así que necesito Entiendo cómo insertar código en mis respuestas :(pero, en general, debe tener una clase llamada MySet (Genérico de tipo genérico T) que implemente un conjunto de tipo genérico T.
Debe implementar todos los métodos y tener dos campos: uno se llama iterationSet y el otro se llama insertionSet.
También tendrá un campo booleano que indica si se debe insertar en los dos conjuntos o no. Cuando se llama al método iterator(), este booleano debe establecerse en falso, lo que significa que debe insertar solo para el conjunto de inserción
Debería tener un método que sincronice el contenido de los dos conjuntos una vez que haya terminado con el iterador.
Espero haber sido claro

Cuestiones relacionadas