Deep copy Map in Groovy

对着背影说爱祢 提交于 2019-11-30 07:04:18

问题


How can I deep copy a map of maps in Groovy? The map keys are Strings or Ints. The values are Strings, Primitive Objects or other maps, in a recursive way.


回答1:


An easy way is this:

// 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()
}



回答2:


I've just hit this issue as well, and I just found:

deepCopy = evaluate(original.inspect())

Although I've been coding in Groovy for less than 12 hours, I wonder if there might be some trust issues with using evaluate. Also, the above doesn't handle backslashes. This:

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

does.




回答3:


To go about deep copying each member in a class, the newInstance() exists for Class objects. For example,

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

See http://groovy-lang.org/gdk.html and navigate to java.lang, Class, and finally the newInstance method overloads.

UPDATE:

The example I have above is ultimately an example of a shallow copy, but what I really meant was that in general, you almost always have to define your own reliable deep copy logic, with perhaps using the newInstance() method, if the clone() method is not enough. Here's several ways how to go about that:

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)

I used @Canonical for the equals() method and tuple ctor. See groovy doc Chapter 3.4.2, Code Generation Transformations.

Another way to go about deep copying is using mixins. Let's say you wanted an existing class to have deep copy functionality:

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])

Or categories (again see the groovy doc) if you wanted deep copying semantics based on some sort of runtime context:

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



回答4:


I am afraid you have to do it the clone way. You could give Apache Commons Lang SerializationUtils a try




回答5:


For Json (LazyMap) this wokred for me

copyOfMap = new HashMap<>()
originalMap.each { k, v -> copyOfMap.put(k, v) }
copyOfMap = new JsonSlurper().parseText(JsonOutput.toJson(copyOfMap))

EDIT: Simplification by: Ed Randall

copyOfMap = new JsonSlurper().parseText(JsonOutput.toJson(originalMap))


来源:https://stackoverflow.com/questions/13155127/deep-copy-map-in-groovy

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!