Fa un container Java offrono un iteratore fail-safe
Domanda
Ecco il mio problema:
Questo pezzo di codice genera un java.util.ConcurrentModificationException
, perché il Vector
listeners
viene modificato, mentre esiste un Iterator
per questa struttura dati.
Il java-doc dice che questo contenitore offre solo un iteratore fail-fast.
C'è la possibilità di ottenere un Iterator
su un contenitore standard come Vector
o List
in Java che mi offre un Iterator
, che non ottiene non valido (non è fail-veloce), se v'è un elemento rimosso durante quel Iterator
" vive "?
dovrei avere lo stesso comportamento come la std::list
in C ++. C'è l'iteratore è sempre valida anche se l'iteratore corrente è rimuovere. Che l'iteratore è impostato per l'elemento successivo nell'elenco.
public class ClientHandle {
private final Vector<ClientHandleListener> listeners = new Vector<ClientHandleListener>();
public synchronized void addListener(ClientHandleListener chl) {
listeners.add(chl);
}
public synchronized void removeListener(ClientHandleListener chl) {
listeners.remove(chl);
}
private void fireConnectionClosed() {
final ClientHandle c = this;
final Iterator<ClientHandleListener> it = listeners.iterator();
new Thread(){
@Override
public void run() {
while (it.hasNext()) {
it.next().connectionClosed(c); //FIXME the iterator gets modified
}
};
}.start();
}}
public class ClientHandlePool implements ClientHandleListener, TaskManagerListener {
/*...*/
public synchronized void removeClientHandle(ClientHandle ch) {
//here the listeners Vector from the ClientHandle gets modified
ch.removeListener(this);
ch.removeListener(currentListener);
clientHandles.remove(ch);
}
@Override
public void connectionClosed(ClientHandle ch) {
removeClientHandle(ch);
}
}
Soluzione
Per quanto ne so non c'è modo di aggiungere retroattivamente che la capacità di qualsiasi implementazione predefinita Collection
(Iterable
appunto).
Ma ci sono implementazioni che supportano questo tipo di comportamento da avere risposte ben definiti ai modifica concomitante mentre iterazione.
Un esempio è il CopyOnWriteList
.
Altri suggerimenti
In caso di ascoltatori, si potrebbe pensare di utilizzare java.util.concurrent.CopyOnWriteArrayList
come si deve in genere molto più letture di scrittura.
dare un'occhiata al pacchetto di java.util.concurrent troverete tutto il necessario.
Un modo pigro per creare un veloce, iteratore fail-safe: prendere una copia della lista come un array mentre bloccato, e foreach () sopra la matrice, mentre sbloccato ... Può essere fatto con qualsiasi tipo di List
private void fireConnectionClosed() {
final ClientHandle c = this;
final ClientHandleListener[] listenersArr;
synchronized(this) {
listenersArr=listeners.toArray(new ClientHandleListener[0]);
}
new Thread(){
@Override
public void run() {
for(ClientHandleListener listener : listenersArr )
listener.connectionClosed(c);
}
};
}.start();
}