Go语言中所有赋值操作都是值传递,如果结构中不含指针,则直接赋值就是深度拷贝;如果结构中含有指针(包括自定义指针,以及切片,map等使用了指针的内置类型),则数据源和拷贝之间对应指针会共同指向同一块内存,这时深度拷贝需要特别处理。目前,有三种方法,一是用gob序列化成字节序列再反序列化生成克隆对象;二是先转换成json字节序列,再解析字节序列生成克隆对象;三是针对具体情况,定制化拷贝。前两种方法虽然比较通用但是因为使用了reflex反射,性能比定制化拷贝要低出2个数量级,所以在性能要求较高的情况下应该尽量避免使用前两者。
结论数据:
gob time:454μs
json time:170μs
custom time:2μs
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | package main import ( "bytes" "encoding/gob" "encoding/json" "fmt" "time" ) type AuthorInfo struct { Name string `json:name` Age int `json:age` Country *int `json:country` } type Book struct { Title string `json:title` Author AuthorInfo `json:author` Year int `json:year` Category []string `json:category` Price map[string]string `json:price` } func DeepCopyByGob(dst, src interface{}) error { var buffer bytes.Buffer if err := gob.NewEncoder(&buffer).Encode(src); err != nil { return err } return gob.NewDecoder(&buffer).Decode(dst) } func DeepCopyByJson(src []Book) (*[]Book, error) { var dst = new([]Book) b, err := json.Marshal(src) if err != nil { return nil, err } err = json.Unmarshal(b, dst) return dst, err } func DeepCopyByCustom(src []Book) []Book { dst := make([]Book, len(src)) for i, book := range src { tmpbook := Book{} tmpbook.Title = book.Title tmpbook.Year = book.Year tmpbook.Author = AuthorInfo{} tmpbook.Author.Name = book.Author.Name tmpbook.Author.Age = book.Author.Age tmpbook.Author.Country = new(int) *tmpbook.Author.Country = *book.Author.Country tmpbook.Category = make([]string, len(book.Category)) for index, category := range book.Category { tmpbook.Category[index] = category } tmpbook.Price = make(map[string]string) for k, v := range book.Price { tmpbook.Price[k] = v } dst[i] = tmpbook } return dst } func check(err error){ if err != nil{ panic(err) } } func print(name string, books []Book){ for index,book := range books{ fmt.Printf("%s[%d]=%v country=%d\n", name, index, book, *book.Author.Country) } } func main() { //初始化源Book切片 books := make([]Book, 1) country := 1156 author := AuthorInfo{"David", 38, &country} price := make(map[string]string) price["Europe"] = "$56" books[0] = Book{"Tutorial", author, 2020, []string{"math", "art"}, price} print("books",books) var err error var start time.Time //Gob拷贝 start = time.Now() booksCpy := make([]Book, 1) err = DeepCopyByGob(&booksCpy, books) fmt.Printf("\ngob time:%v\n", time.Now().Sub(start)) check(err) *booksCpy[0].Author.Country = 1134 booksCpy[0].Category[0] = "literature" booksCpy[0].Price["America"] = "$250" print("booksCpy",booksCpy) print("books",books) //JSON拷贝 start = time.Now() booksCpy2, err_json := DeepCopyByJson(books) fmt.Printf("\njson time:%v\n", time.Now().Sub(start)) check(err_json) *(*booksCpy2)[0].Author.Country = 1135 (*booksCpy2)[0].Category[0] = "science" (*booksCpy2)[0].Price["Canada"] = "$150" print("(*booksCpy2)",*booksCpy2) print("books",books) //定制拷贝 start = time.Now() booksCpy3 := DeepCopyByCustom(books) fmt.Printf("\ncustom time:%v\n", time.Now().Sub(start)) *booksCpy3[0].Author.Country = 1136 booksCpy3[0].Category[0] = "geometry" booksCpy3[0].Price["Africa"] = "$34" print("booksCpy3",booksCpy3) print("books",books) } |
运行输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156 gob time:454.117μs booksCpy[0]={Tutorial {David 38 0xc0000165d8} 2020 [literature art] map[America:$250 Europe:$56]} country=1134 books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156 json time:170.338μs (*booksCpy2)[0]={Tutorial {David 38 0xc000016878} 2020 [science art] map[Canada:$150 Europe:$56]} country=1135 books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156 custom time:2.165μs booksCpy3[0]={Tutorial {David 38 0xc0000168c8} 2020 [geometry art] map[Africa:$34 Europe:$56]} country=1136 books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156 |