In passato, ho detto di copiare in sicurezza una raccolta facendo qualcosa del tipo:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
o
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Ma questi costruttori "copia", metodi e flussi di creazione statici simili, sono davvero sicuri e dove sono specificate le regole? Con sicurezza intendo che le garanzie di integrità semantica di base offerte dal linguaggio Java e dalle raccolte vengono applicate contro un chiamante malintenzionato, supponendo che siano supportate da un ragionevole SecurityManager
e che non vi siano difetti.
Sono felice con il metodo di lancio ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, ecc, o forse anche appeso.
Ho scelto String
come esempio di argomento di tipo immutabile. Per questa domanda, non mi interessano le copie profonde per raccolte di tipi mutabili che hanno i propri gotchas.
(Per essere chiari, ho esaminato il codice sorgente OpenJDK e ho una sorta di risposta per ArrayList
e TreeSet
.)
NavigableSet
e altre Comparable
raccolte basate possono talvolta rilevare se una classe non si implementa compareTo()
correttamente e generare un'eccezione. Non è chiaro cosa intendi con argomenti non attendibili. Vuoi dire che un malfattore crea una collezione di stringhe cattive e quando le copi nella tua collezione succede qualcosa di brutto? No, il framework delle collezioni è piuttosto solido, è in circolazione dall'1.2.
HashSet
(e tutte le altre raccolte di hashing in generale) si basano sulla correttezza / integrità hashCode
dell'implementazione degli elementi TreeSet
e PriorityQueue
dipendono dal Comparator
(e non puoi nemmeno crea una copia equivalente senza accettare il comparatore personalizzato se ce n'è uno), si EnumSet
fida dell'integrità del enum
tipo particolare che non viene mai verificata dopo la compilazione, quindi un file di classe, non generato con javac
o creato a mano, può sovvertirlo.
new TreeSet<>(strs)
dov'è strs
a NavigableSet
. Questa non è una copia in blocco, poiché il risultante TreeSet
utilizzerà il comparatore della fonte, che è persino necessario per conservare la semantica. Se stai bene solo elaborando gli elementi contenuti, toArray()
è la strada da percorrere; manterrà anche l'ordine di iterazione. Quando stai bene con "prendi elemento, convalida elemento, usa elemento", non hai nemmeno bisogno di fare una copia. I problemi iniziano quando si desidera verificare tutti gli elementi, seguiti dall'uso di tutti gli elementi. Quindi, non puoi fidarti di una TreeSet
copia con un comparatore personalizzato
checkcast
per ciascun elemento è toArray
con un tipo specifico. Lo finiamo sempre. Le raccolte generiche non conoscono nemmeno il loro tipo di elemento effettivo, quindi i loro costruttori di copie non possono fornire una funzionalità simile. Certo, puoi rinviare qualsiasi controllo al giusto uso precedente, ma poi non so a cosa mirino le tue domande. Non hai bisogno di "integrità semantica", quando stai bene controllando e fallendo immediatamente prima di usare gli elementi.