2012-06-19 10 views
7

Desidero ripetere su un set ma il contenuto del set verrà modificato durante la sua iterazione. Desidero ripetere il set originale nel momento in cui è stato creato l'iteratore e non iterare su nessuno dei nuovi elementi aggiunti al set. Com'è possibile? È questo il comportamento predefinito dell'insieme o come posso ottenerlo?Java: Iterate su un set mentre il contenuto del set è in fase di modifica

Un modo mi viene in mente è quello di ottenere una nuova serie dal set originale che non sarà modificato, ma questo sembra poco elegante e ci deve essere una soluzione migliore.

+2

Il modo in cui proponi sembra a posto. – assylias

+1

Per chiarire: questo è single-threaded o multithreading? – templatetypedef

+0

Multithreaded. Un thread sta iterando, un altro thread sta cambiando il set. Non desidero sospendere nessuno dei thread per problemi di prestazioni. – nomel7

risposta

8

Facendo una fotografia del set suona come esattamente la soluzione giusta per me, se si vuole fare in modo che non si vede alcun elemento nuovo. Ci sono alcuni set come ConcurrentSkipListSet che ti permetteranno di continuare a ripetere, ma non posso vedere qualsiasi garanzia sul comportamento di un iteratore in termini di visualizzazione di nuovi elementi.

MODIFICA: CopyOnWriteArraySet ha i requisiti necessari, ma le scritture sono costose, il che suona come se non fosse appropriato per te.

Queste sono le uniche set posso vedere in java.util.concurrent, che è il pacchetto naturale per tali raccolte. Facendo una copia è ancora probabile che sia più semplice :)

+0

Dipende. Se lo snapshot _non è richiesto (nessuno si inserisce per inserirlo mentre stai iterando) CopyOnWriteArraySet sarà più veloce. Quindi dipende da quante volte ci sono collisioni reali. – user949300

+0

@ user949300: Non conosco i dettagli di quando CopyOnWriteArraySet richiede realmente l'assunzione di una copia, ma la documentazione afferma che è "di solito" (qualunque cosa significhi). Sarebbe certamente bello se copiasse solo quando veramente, davvero necessario ... –

7

EDIT: Questa risposta è stata progettata per un caso singolo thread, poiché avevo interpretato domanda del PO come evitare comodification piuttosto che evitare problemi di multithreading. Lascio questa risposta qui nel caso in cui finisca per essere utile a chiunque in futuro utilizzi un approccio a thread singolo.

Non esiste un modo diretto per farlo. Tuttavia, un'opzione abbastanza buona è quella di avere due set: il set principale, che si itera sopra, e un set secondario in cui si inseriscono tutti i nuovi elementi che devono essere aggiunti. È quindi possibile eseguire l'iterazione sul set principale, quindi, una volta terminato, utilizzare addAll per aggiungere tutti i nuovi elementi al set principale.

Ad esempio:

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

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

masterSet.addAll(newElems); 

Spero che questo aiuti!

+1

Questo metodo mi piace perché (a) produce meno oggetti temporanei rispetto alla copia dell'intero set originale e (b) evita un sovraccarico di concorrenza che potrebbe non essere necessario. Il 'Non posso vedere alcuna garanzia sul comportamento di un iteratore in termini di visualizzazione di nuovi elementi' può essere un problema per il' ConcurrentSkipListSet' –

+0

Non sono sicuro di come questo possa funzionare. Come sa il secondo thread che deve aggiungere a newElems, non a masterset ??? E, se NON stai iterando, chi può quindi unire newElems in masterSet ??? – user949300

+0

@ user949300- Assumerei che qualunque codice ci sia che aggiunge elementi al set potrebbe sapere quale sia il nuovo insieme di elementi. Si noti inoltre che la domanda dell'OP non dice nulla sul multithreading; Penso che il problema fosse la comodazione piuttosto che la concorrenza. Sarebbe molto facile comunicare queste nuove informazioni in altri thread se dovessero esistere. – templatetypedef

2

Fare una copia del Setè la soluzione elegante.

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

Ora che OP ha chiarito i requisiti, le soluzioni sono

  1. Copiare il set prima iterazione
  2. Usa CopyOnWriteArraySet
  3. scrivere il proprio codice personalizzato e cercare di essere più intelligente di un sacco di persone intelligenti

Lo svantaggio di 1 # è che, anche se non può essere necessario sempre copiare il set (ad esempio, se non inserimenti in realtà verificarsi durante l'iterazione) Io suggerirei di opzione # 2, a meno che non si dimostri che frequenti gli inserimenti stanno causando un vero problema di prestazioni.

0

Come altri hanno suggerito qui, non esiste una soluzione ottimale a ciò che si cerca. Tutto dipende dal caso d'uso dell'applicazione o dall'uso del set
Poiché Set è un'interfaccia, è possibile definire la propria classe DoubleSet che implementerà Set e, diciamo, utilizzerà due campi Hashset.
Quando si recupera un iteratore, si dovrebbe segnare uno di tali gruppi per essere in "dell'interazione modalità solo", in modo che il metodo add aggiungerà solo per l'altra serie


io sono ancora nuovo per Stackoverlflow, quindi ho bisogno di capire come incorporare il codice nelle mie risposte :(ma in generale dovresti avere una classe chiamata MySet (Generico di tipo generico T) che implementa Set di tipo generico T.
Devi implementare tutti i metodi e avere due campi: uno si chiama iterationSet e l'altro si chiama insertionSet
Avrai anche un campo booleano che indica se inserire o meno i due set. Quando viene chiamato il metodo iterator(), questo booleano deve essere impostato su false, ovvero devi inserire solo per l'insertion Set.
È necessario disporre di un metodo che sincronizzi il contenuto dei due set una volta terminato l'iteratore.
Spero di essere stato chiaro

Problemi correlati