Raccomandazione di utilità clone profonda

C’è qualche utilità per la clonazione profonda per le raccolte java:

  • Array
  • elenchi
  • Mappe

NOTA: preferire alcune soluzioni senza l’uso della serializzazione, ma con l’uso del metodo Object.clone (). Posso essere sicuro che il mio object personalizzato implementerà il metodo clone () e userà solo classi java-standard che sono clonabili …

Penso che la precedente risposta verde fosse negativa , perché potresti chiedere?

  • Aggiunge molto codice
  • Richiede di elencare tutti i campi da copiare e farlo
  • Questo non funzionerà per gli elenchi quando si usa clone () (questo è ciò che dice clone () per HashMap: restituisce una copia superficiale di questa istanza di HashMap: le chiavi e gli oggetti di valutazione non vengono clonati.) Così si finisce per farlo manualmente (ciò rende io piango)

Oh, anche se la serializzazione è anche pessima, potresti dover aggiungere Serializable dappertutto (questo mi fa anche piangere).

Quindi qual è la soluzione:

Libreria Java Deep-Cloning La libreria di clonazione è una piccola libreria java open source (apache license) che crea oggetti deep clone. Gli oggetti non devono implementare l’interfaccia Cloneable. Effettivamente, questa libreria può clonare QUALSIASI oggetti java. Può essere utilizzato, ad esempio, nelle implementazioni della cache se non si desidera modificare l’object memorizzato nella cache o quando si desidera creare una copia profonda degli oggetti.

Cloner cloner=new Cloner(); XX clone = cloner.deepClone(someObjectOfTypeXX); 

Dai un’occhiata su https://github.com/kostaskougios/cloning

Tutti gli approcci per copiare oggetti in Java hanno gravi difetti:

Clone

  1. Il metodo clone () è protetto, quindi non puoi chiamarlo direttamente a meno che la class in questione non la sostituisca con un metodo pubblico.
  2. clone () non chiama il costruttore. Qualsiasi costruttore. Assegna memoria, assegna il campo della class interna (che puoi leggere tramite getClass() ) e copia i campi dell’originale.

Per ulteriori problemi con clone (), vedere l’articolo 11 del libro di Joshua Bloch ” Effective Java, Second Edition ”

serializzare

Serializzare è anche peggio; ha molti difetti di clone() e poi alcuni. Joshua ha un intero capitolo con quattro voci per questo argomento da solo.

La mia soluzione

La mia soluzione è aggiungere una nuova interfaccia ai miei progetti:

 public interface Copyable { T copy (); T createForCopy (); void copyTo (T dest); } 

Il codice si presenta così:

 class Demo implements Copyable { public Demo copy () { Demo copy = createForCopy (); copyTo (copy); return copy; } public Demo createForCopy () { return new Demo (); } public void copyTo (Demo dest) super.copyTo (dest); ...copy fields of Demo here... } } 

Sfortunatamente, devo copiare questo codice su tutti i miei oggetti ma è sempre lo stesso codice, quindi posso usare un modello di editor di Eclipse. vantaggi:

  1. Posso decidere quale costruttore chiamare e come inizializzare quale campo.
  2. L’inizializzazione avviene in un ordine deterministico (dalla class radice alla class di istanza)
  3. Posso riutilizzare oggetti esistenti e sovrascriverli
  4. Digita sicuro
  5. Singletons rimangono singleton

Per i tipi di Java standard (come collezioni, ecc.), Utilizzo una class di utilità che può copiarli. I metodi hanno flag e callback, quindi posso controllare quanto dovrebbe essere profonda una copia.

La semplice clonazione di una raccolta è semplice, ma se vuoi fare una clonazione profonda, probabilmente una libreria ti farà meglio di codificarla manualmente (dato che vuoi anche clonare gli elementi all’interno della raccolta).

Proprio come questa risposta , ho usato la libreria Cloner e in particolare la performance è stata testata su XStream (che può ‘clonare’ serializzandolo poi deserializzando) e serializzazione binaria. Sebbene XStream sia molto veloce nella serializzazione da / a xml, Cloner è molto più veloce alla clonazione:

0,0851 ms: xstream (clonare serializzando / deserializzando)
0,0223 ms: serializzazione binaria (clonatura mediante serializzazione / deserializzazione)
0,0017 ms: cloner
* tempo medio per clonare un object semplice (due campi) e nessun costruttore pubblico predefinito. Corri 10.000 volte.

Oltre ad essere veloci, ecco altri motivi per scegliere il cloner:

  1. esegue un clone profondo di qualsiasi object (anche quelli che non scrivi tu stesso)
  2. non devi mantenere aggiornato il tuo metodo clone () ogni volta che aggiungi un campo
  3. puoi clonare oggetti che non hanno un costruttore pubblico predefinito
  4. lavora con Spring
  5. (ottimizzazione) non clona oggetti immutabili noti (come Integer, String, ecc.)
  6. facile da usare. Esempio:

    cloner.deepClone (ANYOBJECT);

Sono il creatore del lib clonatore, quello presentato da Brad. Questa è una soluzione per clonare oggetti senza dover scrivere alcun codice aggiuntivo (non c’è bisogno di oggetti serializzabili o metodo impl clone ())

È abbastanza veloce come ha detto Brad, e di recente ho caricato una versione ancora più veloce. Nota che l’implementazione manuale di un metodo clone () sarà più veloce della clone lib, ma dovrai scrivere molto codice.

Cloner lib ha funzionato abbastanza bene per me dal momento che lo sto usando in un’implementazione cache per un sito con traffico molto intenso (~ 1 milione di richieste al giorno). La cache dovrebbe clonare circa 10 oggetti per richiesta. È abbastanza affidabile e stabile. Ma tieni presente che la clonazione non è senza rischi. La lib può essere configurata per stampare su ogni istanza di class che clona durante lo sviluppo. In questo modo puoi verificare se clona ciò che pensi debba essere clonato – i grafici degli oggetti possono essere molto profondi e possono contenere riferimenti a una quantità sorprendentemente ampia di oggetti. Con la clone lib, puoi istruirlo a non clonare gli oggetti che non vuoi, cioè i singleton.

Un modo generale per clonare in profondità una raccolta arbitraria è serializzarlo su un stream, quindi rileggerlo in una nuova raccolta. Potrai reidratare completamente nuovi oggetti che non hanno alcuna relazione con quelli vecchi, oltre ad essere copie identiche.

Controlla la risposta di Bruno per un link alle classi di utilità di serializzazione Apache Commons , che sarà molto utile se questa è la strada che decidi di intraprendere.

Una possibilità è usare la serializzazione :

Apache Commons fornisce SerializationUtils

Ho usato questa libreria di clonazione e l’ho trovata abbastanza utile. Dato che aveva alcune limitazioni (avevo bisogno di un controllo più dettagliato sul processo di clonazione: quale campo, in quale contesto e quanto profondamente dovrebbe essere clonato, ecc.), Ne ho creato una versione estesa. Puoi controllare la clonazione dei campi annotandoli nella class quadro.

Solo per dargli un assaggio, ecco una class di esempio:

 public class CloneMePlease { @Clone(Skip.class) String id3 = UUID.randomUUID().toString(); @Clone(Null.class) String id4 = UUID.randomUUID().toString(); @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class) String id5 = UUID.randomUUID().toString(); @Clone.List({ @Clone(groups=CustomActivationGroup2.class, value=Skip.class), @Clone(groups=CustomActivationGroup3.class, value=Copy.class)}) Object activationGroupOrderTest = new Object(); @Clone(LongIncrement.class) long version = 1l; @PostClone private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service){ //do stuff with the original source object in the context of the cloned object //you can inject whatewer service you want, from spring/guice to perform custom logic here } } 

Maggiori dettagli qui: https://github.com/mnorbi/fluidi-cloning

C’è anche un’estensione specifica di ibernazione nel caso ne abbiate bisogno.

Utilizzare la serializzazione e quindi la deserializzazione, ma tenere presente che questo approccio funziona solo con classi Serializable senza campi transitori. Inoltre, i tuoi singleton non saranno più single.