如何深度复制地图,然后清除原件?

我尝试将映射(amap)的内容复制到另一个映射(aSuperMap)中,然后清除 amap,这样它就可以在下一次迭代/循环中获取新值。 问题是您不能清除超级映射中的映射 而没有清除其参考。 下面是一些伪代码。

for something := range fruits{
aMap := make(map[string]aStruct)
aSuperMap := make(map[string]map[string]aStruct)


for x := range something{
aMap[x] = aData
aSuperMap[y] = aMap
delete(aMap, x)
}
//save aSuperMap
saveASuperMap(something)


}

我还尝试了一些动态的东西,但显然它抛出了一个错误(不能赋值为 nil)

aSuperMap[y][x] = aData

问题是如何创建联想映射?在 PHP 中,我只使用 aSuperMap [ y ][ x ] = aData。看来哥朗没有什么明显的方法。如果我删除 delete(aMap, x)它的引用从超级地图也被删除。如果我不删除它,超级地图最终会得到重复的数据。基本上,在每个循环中,它得到带有新值和所有旧值的 aMap

168842 次浏览

You are not copying the map, but the reference to the map. Your delete thus modifies the values in both your original map and the super map. To copy a map, you have to use a for loop like this:

for k,v := range originalMap {
newMap[k] = v
}

Here's an example from the now-retired SO documentation:

// Create the original map
originalMap := make(map[string]int)
originalMap["one"] = 1
originalMap["two"] = 2


// Create the target map
targetMap := make(map[string]int)


// Copy from the original map to the target map
for key, value := range originalMap {
targetMap[key] = value
}

Excerpted from Maps - Copy a Map. The original author was JepZ. Attribution details can be found on the contributor page. The source is licenced under CC BY-SA 3.0 and may be found in the Documentation archive. Reference topic ID: 732 and example ID: 9834.

Individual element copy, it seems to work for me with just a simple example.

maps := map[string]int {
"alice":12,
"jimmy":15,
}


maps2 := make(map[string]int)
for k2,v2 := range maps {
maps2[k2] = v2
}


maps2["miki"]=rand.Intn(100)


fmt.Println("maps: ",maps," vs. ","maps2: ",maps2)

I'd use recursion just in case so you can deep copy the map and avoid bad surprises in case you were to change a map element that is a map itself.

Here's an example in a utils.go:

package utils


func CopyMap(m map[string]interface{}) map[string]interface{} {
cp := make(map[string]interface{})
for k, v := range m {
vm, ok := v.(map[string]interface{})
if ok {
cp[k] = CopyMap(vm)
} else {
cp[k] = v
}
}


return cp
}

And its test file (i.e. utils_test.go):

package utils


import (
"testing"


"github.com/stretchr/testify/require"
)


func TestCopyMap(t *testing.T) {
m1 := map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
}


m2 := CopyMap(m1)


m1["a"] = "zzz"
delete(m1, "b")


require.Equal(t, map[string]interface{}{"a": "zzz"}, m1)
require.Equal(t, map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
}, m2)
}

It should easy enough to adapt if you need the map key to be something else instead of a string.

You have to manually copy each key/value pair to a new map. This is a loop that people have to reprogram any time they want a deep copy of a map.

You can automatically generate the function for this by installing mapper from the maps package using

go get -u github.com/drgrib/maps/cmd/mapper

and running

mapper -types string:aStruct

which will generate the file map_float_astruct.go containing not only a (deep) Copy for your map but also other "missing" map functions ContainsKey, ContainsValue, GetKeys, and GetValues:

func ContainsKeyStringAStruct(m map[string]aStruct, k string) bool {
_, ok := m[k]
return ok
}


func ContainsValueStringAStruct(m map[string]aStruct, v aStruct) bool {
for _, mValue := range m {
if mValue == v {
return true
}
}


return false
}


func GetKeysStringAStruct(m map[string]aStruct) []string {
keys := []string{}


for k, _ := range m {
keys = append(keys, k)
}


return keys
}


func GetValuesStringAStruct(m map[string]aStruct) []aStruct {
values := []aStruct{}


for _, v := range m {
values = append(values, v)
}


return values
}


func CopyStringAStruct(m map[string]aStruct) map[string]aStruct {
copyMap := map[string]aStruct{}


for k, v := range m {
copyMap[k] = v
}


return copyMap
}

Full disclosure: I am the creator of this tool. I created it and its containing package because I found myself constantly rewriting these algorithms for the Go map for different type combinations.

As stated in seong's comment:

Also see http://golang.org/doc/effective_go.html#maps. The important part is really the "reference to underlying data structure". This also applies to slices.

However, none of the solutions here seem to offer a solution for a proper deep copy that also covers slices.

I've slightly altered Francesco Casula's answer to accommodate for both maps and slices.


This should cover both copying your map itself, as well as copying any child maps or slices. Both of which are affected by the same "underlying data structure" issue. It also includes a utility function for performing the same type of Deep Copy on a slice directly.

Keep in mind that the slices in the resulting map will be of type []interface{}, so when using them, you will need to use type assertion to retrieve the value in the expected type.

Example Usage

Maps (Will automatically deep-copy slices as well)

copy := CopyableMap(originalMap).DeepCopy()

Slices (Will automatically deep-copy maps as well)

copy := CopyableSlice(originalSlice).DeepCopy()

Source

deepcopy.go

package utils


type CopyableMap   map[string]interface{}
type CopyableSlice []interface{}


// DeepCopy will create a deep copy of this map. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (m CopyableMap) DeepCopy() map[string]interface{} {
result := map[string]interface{}{}


for k,v := range m {
// Handle maps
mapvalue,isMap := v.(map[string]interface{})
if isMap {
result[k] = CopyableMap(mapvalue).DeepCopy()
continue
}


// Handle slices
slicevalue,isSlice := v.([]interface{})
if isSlice {
result[k] = CopyableSlice(slicevalue).DeepCopy()
continue
}


result[k] = v
}


return result
}


// DeepCopy will create a deep copy of this slice. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (s CopyableSlice) DeepCopy() []interface{} {
result := []interface{}{}


for _,v := range s {
// Handle maps
mapvalue,isMap := v.(map[string]interface{})
if isMap {
result = append(result, CopyableMap(mapvalue).DeepCopy())
continue
}


// Handle slices
slicevalue,isSlice := v.([]interface{})
if isSlice {
result = append(result, CopyableSlice(slicevalue).DeepCopy())
continue
}


result = append(result, v)
}


return result
}

Test File (deepcopy_tests.go)

package utils


import (
"testing"


"github.com/stretchr/testify/require"
)


func TestCopyMap(t *testing.T) {
m1 := map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
"c": []interface{} {
"d", "e", map[string]interface{} {
"f": "g",
},
},
}


m2 := CopyableMap(m1).DeepCopy()


m1["a"] = "zzz"
delete(m1, "b")
m1["c"].([]interface{})[1] = "x"
m1["c"].([]interface{})[2].(map[string]interface{})["f"] = "h"


require.Equal(t, map[string]interface{}{
"a": "zzz",
"c": []interface{} {
"d", "x", map[string]interface{} {
"f": "h",
},
},
}, m1)
require.Equal(t, map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
"c": []interface{} {
"d", "e", map[string]interface{} {
"f": "g",
},
},
}, m2)
}

You can roundtrip it through encoding/gob:

package main


import (
"bytes"
"encoding/gob"
)


func copyMap(in, out interface{}) {
buf := new(bytes.Buffer)
gob.NewEncoder(buf).Encode(in)
gob.NewDecoder(buf).Decode(out)
}


func main() {
a := map[string]int{"month": 12, "day": 31}
b := make(map[string]int)
copyMap(a, &b)
}

This works with other packages too. Here is an example with net/url:

package main


import (
"fmt"
"net/url"
)


func copyVal(v url.Values) url.Values {
u := url.URL{
RawQuery: v.Encode(),
}
return u.Query()
}


func main() {
a := url.Values{
"west": {"left"}, "east": {"right"},
}
b := copyVal(a)
fmt.Println(b)
}