Aikido

Perché è importante utilizzare modelli sicuri quando si rimuovono elementi dalle collezioni

Leggibilità

Regola
Utilizzo metodi metodi quando rimuovere dalle collezioni.
Modifica una collezione mentre iterazione su di essa spesso causa errori.

Linguaggi supportati: PY, Java, C/C++, C#, 
Swift/Objective-C, Ruby, PHP, Kotlin, Go,
Scala, Rust, Groovy, Dart, Julia, Elixit, 
Erlang, Clojure, OCaml, Lua

Introduzione

La rimozione di elementi da una collezione durante l'iterazione provoca eccezioni di modifica concorrente in Java e un comportamento imprevedibile in C#. L'iteratore mantiene un puntatore interno che diventa non valido quando la collezione sottostante subisce modifiche. Ciò comporta il salto di elementi, arresti anomali o cicli infiniti, a seconda del tipo di collezione e dello schema di rimozione utilizzato.

Perché è importante

Stabilità del sistema: le eccezioni dovute a modifiche simultanee causano l'arresto immediato dell'applicazione. In ambiente di produzione, ciò comporta la perdita di richieste e l'indisponibilità del servizio. L'eccezione si verifica spesso in casi limite con dati specifici, rendendo difficile individuarla durante i test.

Integrità dei dati: quando la logica di rimozione fallisce nel corso di un'iterazione, la collezione rimane in uno stato parzialmente modificato. Alcuni elementi vengono rimossi, mentre altri che avrebbero dovuto essere rimossi rimangono. Ciò genera dati incoerenti che influenzano la logica a valle.

Complessità del debug: i bug legati alle modifiche concorrenti dipendono dal tempismo e possono manifestarsi solo con determinate combinazioni di dati. Sono difficili da riprodurre in modo coerente, il che rende complicato il debug e la correzione affidabile.

Esempi di codice

❌ Non conforme:

List<User> users = getUserList();
for (User user : users) {
    if (!user.isActive()) {
        users.remove(user); // ConcurrentModificationException
    }
}

Perché è sbagliato: Rimuovere da utenti mentre l'iterazione con il ciclo "for" potenziato comporta ConcurrentModificationException. L'iteratore rileva che la collezione è stata modificata al di fuori dell'iteratore e genera immediatamente un'eccezione. Gli utenti attivi successivi al primo utente inattivo non vengono mai elaborati.

✅ Conforme:

List<User> users = getUserList();
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()) {
    User user = iterator.next();
    if (!user.isActive()) {
        iterator.remove(); // Safe removal through iterator
    }
}

Perché è importante: Utilizzo di iterator.remove() rimuove in modo sicuro gli elementi durante l'iterazione. L'iteratore mantiene uno stato coerente e prosegue l'elaborazione degli elementi rimanenti. Tutti gli utenti inattivi vengono rimossi correttamente senza eccezioni.

Conclusione

Utilizzare gli iteratori remove() metodo per la rimozione sicura durante l'iterazione. In alternativa, utilizzare gli stream con filter() per creare nuove collezioni oppure removeIf() per lo smaltimento di rifiuti ingombranti. Non chiamare mai il servizio di raccolta remove() direttamente durante l'iterazione.

Domande frequenti

Hai delle domande?

E se si usassero dei normali cicli "for" con un indice?

L'iterazione all'indietro con l'indice funziona: for (int i = list.size() - 1; i >= 0; i--). La rimozione degli elementi sposta quelli successivi, ma l'iterazione all'indietro evita di saltarne alcuni. Tuttavia, iterator.remove() o removeIf() sono più chiari e meno soggetti a errori.

Posso usare invece il metodo `removeIf` di Java 8+?

Sì, `users.removeIf(user -> !user.isActive())` è l'approccio moderno preferito. È più conciso e gestisce l'iterazione in modo sicuro internamente. Usa `removeIf()` quando devi rimuovere elementi in base a un predicato, gli stream per le trasformazioni e i metodi dell'iteratore quando la logica di rimozione è complessa.

Questo vale per tutti i tipi di raccolta?

Sì, ArrayList, HashSet, HashMap e la maggior parte delle collezioni generano un'eccezione ConcurrentModificationException se vengono modificate durante l'iterazione. Le collezioni thread-safe, come ConcurrentHashMap, consentono la modifica ma presentano una semantica diversa. È sempre consigliabile consultare la documentazione della collezione per conoscere le regole relative alle modifiche.

E le collezioni in C#?

C# genera un'eccezione `InvalidOperationException` quando le collezioni vengono modificate durante l'iterazione. Utilizza `ToList()` per creare una copia prima di eseguire l'iterazione: `foreach (var user in users.ToList())`, quindi rimuovi gli elementi dalla collezione originale. Oppure utilizza LINQ: `users = users.Where(u => u.IsActive).ToList()`.

Come posso eliminare più elementi in modo efficiente?

Utilizza removeIf() per una singola collezione oppure gli stream per filtri complessi: users = users.stream().filter(User::isActive).collect(Collectors.toList()). Questi approcci sono ottimizzati per le operazioni in blocco e risultano più efficienti rispetto alla rimozione degli elementi uno per uno all'interno di un ciclo.

Metti in sicurezza ora

Metti in sicurezza il tuo codice, il cloud e il runtime in un unico sistema centralizzato.
Trova e risolvi le vulnerabilità rapidamente e automaticamente.

Nessuna carta di credito richiesta | Risultati della scansione in 32 secondi.