Golang中如何一行代码实现map的拷贝

Golang中如何一行代码实现map的拷贝 我有一个非常大的映射。我不想通过迭代来将其复制到另一个映射中。有没有其他解决方案?

我能想到的一个方法是将映射数据存入数据库,然后再重新读取,但这会增加一次额外的查询操作,代价较高。

那么可行的解决方案应该是什么呢?

13 回复

包含数据

更多关于Golang中如何一行代码实现map的拷贝的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我想进行拷贝,因为映射是按引用传递的,而我不想传递那个映射。

一个 Stack Overflow 回答建议在循环中复制映射:https://stackoverflow.com/a/23058707/9455968

使用 for 循环并复制数据可能是最快且最容易理解的解决方案。如果您坚持尝试其他方法,可以尝试转换为 JSON 再转换回来,但我不建议这样做。

是的,我尝试使用了gob,但我必须注册所有类型,这很繁琐,而且我不知道我的映射(来自前端的过滤器)可能有多复杂。那么,我是否应该找到所有类型并在gob中注册呢?

如果它这么大,为什么不找其他解决方案而不用复制呢?我的意思是,复制是绝对必要的吗?否则,正如上面所说,由于这是引用类型,除了在循环中复制每个元素之外别无他法。

我遇到了这个问题。
https://play.golang.org/p/MRpurSATQuV

可以看到只进行了一层复制。在内部我们可以替换内容,但它仍然会引用原始映射。

func main() {
    fmt.Println("hello world")
}

不理解您的使用场景。但如果您要复制它,为什么不从一开始就维护重复的映射,就像这样:

package main

import "fmt"

func main() {
    m1 := map[string]int{"a": 1, "b": 2}
    m2 := map[string]int{}
    for k, v := range m1 {
        m2[k] = v
    }
    fmt.Println(m2)
}

最快的序列化方法是使用 “encoding/gob” 对映射进行编码/解码。

为什么不创建一个包含此映射的结构体接收器(不导出该映射)并在其上实现查找或获取方法(只读)?类似这样:

type MyMap struct {
	myHugeMap map[string]string
}

func (m *MyMap) get(i string) (string, error) {
	e, ok := m.myHugeMap[i]
	if !ok {
		return "", fmt.Errorf("element %s not found", i)
	}
	return e, nil
}

当然,这应该放在单独的包中,并且需要传递该结构体的指针。

如果你有很多新类型的值,那么你需要注册它们,但如果只是少数新类型,考虑到 encoding/gob 相对于 encoding/json 的速度优势,这并不算太麻烦。

但如果你是从未知数据创建初始映射,那么我假设它是一个标准的 map[string]interface{} 值。如果是这样,就不需要类型注册。

这里:https://play.golang.org/p/it0mLk1vd8f

注意:如果你是基于结构体定义进行 gob 编码/解码,则无需注册:https://play.golang.org/p/Pv0tPAcU74v

感谢,JSON 序列化和反序列化功能正常。我还转向了一种更简单的迭代和复制方案,但还需要测试负载情况。

func copy(originalMap map[string]interface{}) map[string]interface{} {
	newMap := make(map[string]interface{})
	for key, value := range originalMap {
		newMap[key] = value
	}
	return newMap
}
func  appendWith(toAppend bson.M, filter map[string]interface{}) error {
	value := filter["cond"]
	marshallValue, err := json.Marshal(value)
	if err != nil {
		return errors.Wrap(err, "json marshall")
	}
	var unmarshallValue []interface{}
	err = json.Unmarshal(marshallValue, &unmarshallValue)
	if err != nil {
		return errors.Wrap(err, "json unmarshall")
	}
	var res []interface{}
	res = append(res, unmarshallValue...)
	_ = append(res, toAppend)
	return nil
}

老实说,如果没有更多背景信息,我不确定自己或其他人能给你更多帮助。我们都知道你想复制一个映射,担心内部代码会修改映射中的数据,而且你显然在使用 map[string]interface{}(这本身可能被视为代码异味),但这些信息并不能完整描绘你正在做什么以及为什么这么做,因此很难给出合理的建议。

这就好比你要我推荐适合你汽车的轮胎,而我却不知道你是住在常年下雪的地方、季节性下雪的地区还是永远阳光明媚的地方。又或者你打算把车开上赛道,需要完全不同的轮胎类型。关键是,我可以尝试给你一些模糊的通用建议,但最终这些都只是可能不太有用的泛泛之谈。

如果你坚持每次都要克隆映射,可以将其编码为 JSON 然后重新解码到新的映射变量中,这样每次都能实现深度克隆(据我所知),但速度会比较慢。可能比反复查询数据库快一些,但依然算不上高速。

如果你想获得更好的建议,我认为我们需要更深入了解你的具体场景,这样才能帮你找到可行的解决方案——无论这个方案是否包含克隆 map[string]interface{}

在Go语言中,可以使用一行代码实现map的拷贝,无需显式迭代。具体方法是利用Go语言内置的maps包(从Go 1.21版本开始提供)的Clone函数。这个函数会创建一个新的map,并复制原始map中的所有键值对。

示例代码:

package main

import (
    "fmt"
    "maps"
)

func main() {
    originalMap := map[string]int{"a": 1, "b": 2, "c": 3}
    copiedMap := maps.Clone(originalMap) // 一行代码实现map拷贝
    fmt.Println("Original:", originalMap)
    fmt.Println("Copied:", copiedMap)
}

输出:

Original: map[a:1 b:2 c:3]
Copied: map[a:1 b:2 c:3]

如果使用的是Go 1.21之前的版本,可以自定义一个函数来实现类似功能,但需要多行代码。例如:

func copyMap[K comparable, V any](m map[K]V) map[K]V {
    result := make(map[K]V, len(m))
    for k, v := range m {
        result[k] = v
    }
    return result
}

使用示例:

copiedMap := copyMap(originalMap)

这种方法避免了数据库操作的开销,直接在内存在完成拷贝,效率更高。

回到顶部