2012-10-31 33 views
10

Come posso copiare in profondità una mappa delle mappe in Groovy? Le chiavi della mappa sono stringhe o int. I valori sono stringhe, oggetti primitivi o altre mappe, in modo ricorsivo.Copia profonda Mappa in Groovy

risposta

21

Un modo semplice è questa:

// standard deep copy implementation 
def deepcopy(orig) { 
    bos = new ByteArrayOutputStream() 
    oos = new ObjectOutputStream(bos) 
    oos.writeObject(orig); oos.flush() 
    bin = new ByteArrayInputStream(bos.toByteArray()) 
    ois = new ObjectInputStream(bin) 
    return ois.readObject() 
} 
+0

Superbo. Sto usando questo per copiare in profondità un ConfigObject groovy come clone() fa solo una copia superficiale. La copia superficiale non è sufficiente su un ConfigObject per le mie esigenze. Grazie. – noumenon

+0

Questo è abbastanza sorprendente –

+0

Devo ricordarlo! –

2

Ho paura di doverlo fare nel modo clone. Si potrebbe dare Apache Commons Lang SerializationUtils una prova

+0

Sono abbastanza nuovo per Groovy. Potrei sbagliarmi. –

+0

Il clone sembra funzionare bene per le mappe. Ma non appena porti oggetti più complicati sulla scena, non funziona altrettanto bene, perché è una copia poco profonda. La copia profonda di Ayman è veloce e funziona per gli oggetti che ho provato (incluso ConfigObject). – noumenon

4

ho appena colpito questo problema come bene, e ho appena trovato:

deepCopy = evaluate(original.inspect()) 

Anche se io Ho scritto codice in Groovy per meno di 12 ore, mi chiedo se ci potrebbero essere alcuni problemi di fiducia con l'utilizzo di evaluate. Inoltre, quanto sopra non gestisce i backslash. Questo:

deepCopy = evaluate(original.inspect().replace('\\','\\\\')) 

fa.

+0

Grazie per aver menzionato l'ispezione. Ho cercato per anni un equivalente Groovy della repr di Python! Pensare che fosse sempre lì ... – Tobia

+1

Ha funzionato per me quando modificai docker-compose.yml con groovy e jenkins. Grazie! (ha aggiunto questo commento per facilitare la ricerca su google) –

3

Per eseguire una copia completa di ciascun membro in una classe, esiste il newInstance() per gli oggetti di classe. Ad esempio,

foo = ["foo": 1, "bar": 2] 
bar = foo.getClass().newInstance(foo) 
foo["foo"] = 3 
assert(bar["foo"] == 1) 
assert(foo["foo"] == 3) 

Vedi http://groovy-lang.org/gdk.html e passare a java.lang, classe, e, infine, il metodo sovraccarichi newInstance.

UPDATE:

L'esempio che ho sopra è in ultima analisi, un esempio di una copia, ma quello che veramente dire è che, in generale, si ha quasi sempre per definire la propria logica copia completa affidabile, con forse usando il metodo newInstance(), se il metodo clone() non è abbastanza. Ecco diversi modi come andare a tale proposito:

import groovy.transform.Canonical 
import groovy.transform.AutoClone 
import static groovy.transform.AutoCloneStyle.* 

// in @AutoClone, generally the semantics are 
// 1. clone() is called if property implements Cloneable else, 
// 2. initialize property with assignment, IOW copy by reference 
// 
// @AutoClone default is to call super.clone() then clone() on each property. 
// 
// @AutoClone(style=COPY_CONSTRUCTOR) which will call the copy ctor in a 
// clone() method. Use if you have final members. 
// 
// @AutoClone(style=SIMPLE) will call no arg ctor then set the properties 
// 
// @AutoClone(style=SERIALIZATION) class must implement Serializable or 
// Externalizable. Fields cannot be final. Immutable classes are cloned. 
// Generally slower. 
// 
// if you need reliable deep copying, define your own clone() method 

def assert_diffs(a, b) { 
    assert a == b // equal objects 
    assert ! a.is(b) // not the same reference/identity 
    assert ! a.s.is(b.s) // String deep copy 
    assert ! a.i.is(b.i) // Integer deep copy 
    assert ! a.l.is(b.l) // non-identical list member 
    assert ! a.l[0].is(b.l[0]) // list element deep copy 
    assert ! a.m.is(b.m) // non-identical map member 
    assert ! a.m['mu'].is(b.m['mu']) // map element deep copy 
} 

// deep copy using serialization with @AutoClone 
@Canonical 
@AutoClone(style=SERIALIZATION) 
class Bar implements Serializable { 
    String s 
    Integer i 
    def l = [] 
    def m = [:] 

    // if you need special serialization/deserialization logic override 
    // writeObject() and/or readObject() in class implementing Serializable: 
    // 
    // private void writeObject(ObjectOutputStream oos) throws IOException { 
    // oos.writeObject(s) 
    // oos.writeObject(i) 
    // oos.writeObject(l) 
    // oos.writeObject(m) 
    // } 
    // 
    // private void readObject(ObjectInputStream ois) 
    // throws IOException, ClassNotFoundException { 
    // s = ois.readObject() 
    // i = ois.readObject() 
    // l = ois.readObject() 
    // m = ois.readObject() 
    // } 
} 

// deep copy by using default @AutoClone semantics and overriding 
// clone() method 
@Canonical 
@AutoClone 
class Baz { 
    String s 
    Integer i 
    def l = [] 
    def m = [:] 

    def clone() { 
     def cp = super.clone() 
     cp.s = s.class.newInstance(s) 
     cp.i = i.class.newInstance(i) 
     cp.l = cp.l.collect { it.getClass().newInstance(it) } 
     cp.m = cp.m.collectEntries { k, v -> 
     [k.getClass().newInstance(k), v.getClass().newInstance(v)] 
     } 
     cp 
    } 
} 

// assert differences 
def a = new Bar("foo", 10, ['bar', 'baz'], [mu: 1, qux: 2]) 
def b = a.clone() 
assert_diffs(a, b) 

a = new Baz("foo", 10, ['bar', 'baz'], [mu: 1, qux: 2]) 
b = a.clone() 
assert_diffs(a, b) 

ho usato @Canonical per le equals() metodo e tuple ctor. Vedi groovy doc Chapter 3.4.2, Code Generation Transformations.

Un altro modo per eseguire la copia profonda è l'utilizzo di mix. Diciamo che si voleva una classe esistente per avere funzionalità copia profonda:

class LinkedHashMapDeepCopy { 
    def deep_copy() { 
     collectEntries { k, v -> 
     [k.getClass().newInstance(k), v.getClass().newInstance(v)] 
     } 
    } 
} 

class ArrayListDeepCopy { 
    def deep_copy() { 
     collect { it.getClass().newInstance(it) } 
    } 
} 

LinkedHashMap.mixin(LinkedHashMapDeepCopy) 
ArrayList.mixin(ArrayListDeepCopy) 

def foo = [foo: 1, bar: 2] 
def bar = foo.deep_copy() 
assert foo == bar 
assert ! foo.is(bar) 
assert ! foo['foo'].is(bar['foo']) 

foo = ['foo', 'bar'] 
bar = foo.deep_copy() 
assert foo == bar 
assert ! foo.is(bar) 
assert ! foo[0].is(bar[0]) 

o delle categorie (di nuovo vedere la groovy doc) se si voleva la semantica di copia profonde basata su una sorta di contesto di esecuzione:

import groovy.lang.Category 

@Category(ArrayList) 
class ArrayListDeepCopy { 
    def clone() { 
     collect { it.getClass().newInstance(it) } 
    } 
} 

use(ArrayListDeepCopy) { 
    def foo = ['foo', 'bar'] 
    def bar = foo.clone() 
    assert foo == bar 
    assert ! foo.is(bar) 
    assert ! foo[0].is(bar[0]) // deep copying semantics 
} 

def foo = ['foo', 'bar'] 
def bar = foo.clone() 
assert foo == bar 
assert ! foo.is(bar) 
assert foo[0].is(bar[0]) // back to shallow clone