2010-03-01 9 views
93

Se ho capito bene, ci sono un paio di modi (forse altri) per creare una copia di un Map in Java:copia dei riferimenti una mappa in Java

Map<String, Object> data = new HashMap<String, Object>(); 
Map<String, Object> shallowCopy; 

// first way 
shallowCopy = new HashMap<String, Object>(data); 

// second way 
shallowCopy = (Map<String, Object>) ((HashMap<String, Object>) data).clone(); 

è un modo preferito su l'altro, e se sì, perché?

Una cosa che vale la pena menzionare è che il secondo modo fornisce un avviso "Cast non selezionato". Quindi devi aggiungere @SuppressWarnings("unchecked") per aggirarlo, il che è un po 'irritante (vedi sotto).

@SuppressWarnings("unchecked") 
public Map<String, Object> getDataAsMap() { 
    // return a shallow copy of the data map 
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone(); 
} 

risposta

98

È sempre meglio copiare utilizzando un costruttore di copie. clone() in Java è rotto (vedere SO: How to properly override clone method?).

Josh Bloch on Design - Copy Constructor versus Cloning

Se avete letto l'articolo sulla clonazione nel mio libro, soprattutto se si legge tra le righe, si sa che penso clone è profondamente rotto. [...] È un peccato che Cloneable non funzioni, ma succede.

Bloch (che tra l'altro, ha progettato e realizzato la struttura di raccolta) anche andato oltre nel dire che egli fornisce solo il metodo clone() solo "perché la gente si aspetta". In realtà NON consiglia di usarlo affatto.


penso che il dibattito più interessante è se un costruttore di copia è meglio di una fabbrica di copia, ma questa è una discussione del tutto diversa.

+2

Nizza risposta, grazie. – dcp

+0

Sì, questa è una delle mie parti preferite del libro. – polygenelubricants

+1

Non mi piace dire che clone() è rotto. Preferisco dire che il clone era una pessima decisione progettuale e può danneggiarti molto se non lo usi correttamente.Inoltre, potresti non fidarti mai dei metodi clone() di altre persone. Quindi finiamo allo stesso modo, cerchiamo di evitarlo, ma non è rotto. – santiagobasulto

49

Nessuno dei due: il constructor che si fa riferimento è definito per la HashMap implementazione di un Map, (così come per gli altri), ma non per la mappa stessa interfaccia (ad esempio, si consideri la Provider attuazione del Interfaccia della mappa: non troverai questo costruttore).

D'altro canto non è consigliabile utilizzare il metodo clone(), come spiegato da Josh Bloch.

Per quanto riguarda l'interfaccia Map (e della tua domanda, in cui si chiede come copiare una mappa, non un HashMap), si dovrebbe usare Map#putAll():

Copia tutti i mapping dal specificato mappa a questa mappa (operazione opzionale). L'effetto di questa chiamata è equivalente a quello di chiamando put (k, v) su questa mappa una volta per ogni mappatura dalla chiave k al valore v nella mappa specificata.

Esempio:

// HashMap here, but it works for every implementation of the Map interface 
Map<String, Object> data = new HashMap<String, Object>(); 
Map<String, Object> shallowCopy = new HashMap<String, Object>(); 

shallowCopy.putAll(data); 
+2

Quindi, per chiarire: se * so * sta copiando * in * un'implementazione di 'Map' che ha un costruttore di copie, non c'è motivo di non usare il costruttore di copie allora? –

+2

Esattamente, e si può anche pensare al contrario: se si usa 'putAll' non è necessario sapere _ se l'implementazione' Map' che si sta utilizzando ha un costruttore di copie o meno. Un semplice costruttore di copie di qualsiasi implementazione 'Map' è quindi ridondante. –

+1

Certo, anche se generalmente mi piacciono le 1-liners meglio delle 2-liner. ;) –

7

Copia una mappa senza conoscere la sua attuazione:

static final Map shallowCopy(final Map source) throws Exception { 
    final Map newMap = source.getClass().newInstance(); 
    newMap.putAll(source); 
    return newMap; 
} 
+2

Considerare l'aggiunta di parametri di tipo '' per garantire la sicurezza del tipo. – Barett

+0

E le mappe senza costruttori con argomento zero? –