当前位置:   article > 正文

【golang】2、深拷贝-浅拷贝-方式与性能对比:Reflect、GobEncode、JsonMarshal_golang值拷贝和地址拷贝

golang值拷贝和地址拷贝


在这里插入图片描述

概念

各编程语言都有深浅拷贝的概念, 它们的核心是拷贝的是地址(浅拷贝)还是(深拷贝), 今天我们就对其概念介绍, 并做性能对比和特性对比

浅拷贝

  • 拷贝的只是数据的地址. 新老对象共享内存
  • 当改新对象时, 老对象也会被改变. 当释放新对象时, 老对象也会被释放.
  • 浅拷贝的数据类型, 即引用类型数据: 有Slice(无论原生类型或结构体slice), Map

深拷贝

  • 深拷贝的是数据的内容, 会创建新对象, 新老对象不共享内存
  • 当改变新对象时, 和老对象没关系; 当释放新对象时, 和老对象也没关系; 总之就是新老对象没任何关系
  • 深拷贝的数据类型, 即值类型数据: 有Struct, Array, Int, String, Float, Bool

用途

当我们需要拷贝一个新对象时, 怎么选择呢? 显然

  • 当需要节省内存时, 可选浅拷贝, 因为只是复制了地址
  • 当为了操作简便时, 可选浅拷贝, 直接a := b即可
  • 而当需要开辟新内存, 分别处理新老对象, 使两者互不干扰时, 则需要深拷贝
    • 例如数据流业务的多条支流间, 若均存在改写对象的操作, 则需深拷贝, 防止A逻辑分支改了a对象, 而后续B逻辑分支意外的使用了被别人改过的对象

在这里插入图片描述

性能对比

有Reflect, GobEncode, JsonMarshal几种方式, 我们做一个Benchmark试验如下

  • 定义数据类型
type InnerStruct struct {
	Int    int
	String string
}
type CType struct {
	Int         int
	String      string
	Struct      InnerStruct
	IntSlice    []int
	StringSlice []string
	StructSlice []InnerStruct
	IntArray    [2]int
	StringArray [2]string
	StructArray [2]InnerStruct
	Map         map[string]string
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 初始化数据
c := &CType{
	Int:         1,
	String:      "2",
	Struct:      InnerStruct{Int: 3, String: "4"},
	IntSlice:    []int{5, 6},
	StringSlice: []string{"7", "8"},
	StructSlice: []InnerStruct{{Int: 9, String: "10"}, {Int: 11, String: "12"}},
	IntArray:    [2]int{13, 14},
	StringArray: [2]string{"15", "16"},
	StructArray: [2]InnerStruct{{Int: 17, String: "18"}, {Int: 19, String: "20"}},
	Map:         map[string]string{"21": "22", "23": "24"},
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

测试电脑为cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz

浅拷贝

func BenchmarkShallowCopy(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.ShallowCopy()
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

结果为BenchmarkShallowCopy 0.7702 ns/op

Reflect

func BenchmarkReflect(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyReflect()
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

结果为BenchmarkReflect 4515 ns/op

JsonMarshal

func BenchmarkJsonMarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyJsonMarshal()
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

结果为BenchmarkJsonMarshal 11133 ns/op

GobEncode

func BenchmarkGobMarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyGobMarshal()
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

结果为BenchmarkGobMarshal 54298 ns/op
综上, 浅拷贝性能>Reflect性能>JsonMarshal性能>GobMarshal

在这里插入图片描述

特点对比

做完性能对别后, 我们还需对别深浅拷贝是否对新对象的写操作有影响

同样用上文性能对比的数据, 分别测试对拷贝后新对象的写操作, 是否影响老对象, 结果如下

浅拷贝

func TestShallowCopy(t *testing.T) {
	r := c.ShallowCopy()
	r.Int += 10000
	r.String += "changed"
	r.Struct.Int += 10000
	r.Struct.String += "changed"
	r.IntSlice[0] += 10000
	r.IntSlice[1] += 10000
	r.StringSlice[0] += "changed"
	r.StringSlice[1] += "changed"
	r.StructSlice[0].Int += 10000
	r.StructSlice[0].String += "changed"
	r.StructSlice[1].Int += 10000
	r.StructSlice[1].String += "changed"
	r.IntArray[0] += 10000
	r.IntArray[1] += 10000
	r.StringArray[0] += "changed"
	r.StringArray[1] += "changed"
	r.StructArray[0].Int += 10000
	r.StructArray[0].String += "changed"
	r.StructArray[1].Int += 10000
	r.StructArray[1].String += "changed"
	r.Map["21"] += "changed"
	r.Map["23"] += "changed"
	rByte, _ := json.MarshalIndent(r, "", "  ")
	t.Log(string(rByte))
}
  • 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

结果如下可见, 确实只有Slice(无论原生类型或结构体slice), Map有影响, 其他类型均无影响

=== RUN   TestShallowCopy
    main_test.go:117: {
          "Int": 1,
          "String": "2",
          "Struct": {
            "Int": 3,
            "String": "4"
          },
          "IntSlice": [
            10005,
            10006
          ],
          "StringSlice": [
            "7changed",
            "8changed"
          ],
          "StructSlice": [
            {
              "Int": 10009,
              "String": "10changed"
            },
            {
              "Int": 10011,
              "String": "12changed"
            }
          ],
          "IntArray": [
            13,
            14
          ],
          "StringArray": [
            "15",
            "16"
          ],
          "StructArray": [
            {
              "Int": 17,
              "String": "18"
            },
            {
              "Int": 19,
              "String": "20"
            }
          ],
          "Map": {
            "21": "22changed",
            "23": "24changed"
          }
        }
  • 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

Reflect

func TestDeepCopyReflect(t *testing.T) {
	r := c.DeepCopyReflect()
	r.Int += 10000
	r.String += "changed"
	r.Struct.Int += 10000
	r.Struct.String += "changed"
	r.IntSlice[0] += 10000
	r.IntSlice[1] += 10000
	r.StringSlice[0] += "changed"
	r.StringSlice[1] += "changed"
	r.StructSlice[0].Int += 10000
	r.StructSlice[0].String += "changed"
	r.StructSlice[1].Int += 10000
	r.StructSlice[1].String += "changed"
	r.IntArray[0] += 10000
	r.IntArray[1] += 10000
	r.StringArray[0] += "changed"
	r.StringArray[1] += "changed"
	r.StructArray[0].Int += 10000
	r.StructArray[0].String += "changed"
	r.StructArray[1].Int += 10000
	r.StructArray[1].String += "changed"
	r.Map["21"] += "changed"
	r.Map["23"] += "changed"
	rByte, _ := json.MarshalIndent(c, "", "  ")
	t.Log(string(rByte))
}
  • 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

结果如下, 确实新对象的任何改动, 都没影响老对象

=== RUN   TestDeepCopyReflect
    main_test.go:117: {
          "Int": 1,
          "String": "2",
          "Struct": {
            "Int": 3,
            "String": "4"
          },
          "IntSlice": [
            5,
            6
          ],
          "StringSlice": [
            "7",
            "8"
          ],
          "StructSlice": [
            {
              "Int": 9,
              "String": "10"
            },
            {
              "Int": 11,
              "String": "12"
            }
          ],
          "IntArray": [
            13,
            14
          ],
          "StringArray": [
            "15",
            "16"
          ],
          "StructArray": [
            {
              "Int": 17,
              "String": "18"
            },
            {
              "Int": 19,
              "String": "20"
            }
          ],
          "Map": {
            "21": "22",
            "23": "24"
          }
        }
  • 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

JsonMarshal

func TestDeepCopyJsonMarshal(t *testing.T) {
	r := c.DeepCopyJsonMarshal()
	r.Int += 10000
	r.String += "changed"
	r.Struct.Int += 10000
	r.Struct.String += "changed"
	r.IntSlice[0] += 10000
	r.IntSlice[1] += 10000
	r.StringSlice[0] += "changed"
	r.StringSlice[1] += "changed"
	r.StructSlice[0].Int += 10000
	r.StructSlice[0].String += "changed"
	r.StructSlice[1].Int += 10000
	r.StructSlice[1].String += "changed"
	r.IntArray[0] += 10000
	r.IntArray[1] += 10000
	r.StringArray[0] += "changed"
	r.StringArray[1] += "changed"
	r.StructArray[0].Int += 10000
	r.StructArray[0].String += "changed"
	r.StructArray[1].Int += 10000
	r.StructArray[1].String += "changed"
	r.Map["21"] += "changed"
	r.Map["23"] += "changed"
	rByte, _ := json.MarshalIndent(c, "", "  ")
	t.Log(string(rByte))
}
  • 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

结果如下, 确实新对象的任何改动, 都没影响老对象

=== RUN   TestDeepCopyJsonMarshal
    main_test.go:117: {
          "Int": 1,
          "String": "2",
          "Struct": {
            "Int": 3,
            "String": "4"
          },
          "IntSlice": [
            5,
            6
          ],
          "StringSlice": [
            "7",
            "8"
          ],
          "StructSlice": [
            {
              "Int": 9,
              "String": "10"
            },
            {
              "Int": 11,
              "String": "12"
            }
          ],
          "IntArray": [
            13,
            14
          ],
          "StringArray": [
            "15",
            "16"
          ],
          "StructArray": [
            {
              "Int": 17,
              "String": "18"
            },
            {
              "Int": 19,
              "String": "20"
            }
          ],
          "Map": {
            "21": "22",
            "23": "24"
          }
        }
  • 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

GobEncode

func TestDeepCopyGobMarshal(t *testing.T) {
	r := c.DeepCopyGobMarshal()
	r.Int += 10000
	r.String += "changed"
	r.Struct.Int += 10000
	r.Struct.String += "changed"
	r.IntSlice[0] += 10000
	r.IntSlice[1] += 10000
	r.StringSlice[0] += "changed"
	r.StringSlice[1] += "changed"
	r.StructSlice[0].Int += 10000
	r.StructSlice[0].String += "changed"
	r.StructSlice[1].Int += 10000
	r.StructSlice[1].String += "changed"
	r.IntArray[0] += 10000
	r.IntArray[1] += 10000
	r.StringArray[0] += "changed"
	r.StringArray[1] += "changed"
	r.StructArray[0].Int += 10000
	r.StructArray[0].String += "changed"
	r.StructArray[1].Int += 10000
	r.StructArray[1].String += "changed"
	r.Map["21"] += "changed"
	r.Map["23"] += "changed"
	rByte, _ := json.MarshalIndent(c, "", "  ")
	t.Log(string(rByte))
}
  • 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

结果如下, 确实新对象的任何改动, 都没影响老对象

=== RUN   TestDeepCopyGobMarshal
    main_test.go:117: {
          "Int": 1,
          "String": "2",
          "Struct": {
            "Int": 3,
            "String": "4"
          },
          "IntSlice": [
            5,
            6
          ],
          "StringSlice": [
            "7",
            "8"
          ],
          "StructSlice": [
            {
              "Int": 9,
              "String": "10"
            },
            {
              "Int": 11,
              "String": "12"
            }
          ],
          "IntArray": [
            13,
            14
          ],
          "StringArray": [
            "15",
            "16"
          ],
          "StructArray": [
            {
              "Int": 17,
              "String": "18"
            },
            {
              "Int": 19,
              "String": "20"
            }
          ],
          "Map": {
            "21": "22",
            "23": "24"
          }
        }
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/616624
推荐阅读
相关标签
  

闽ICP备14008679号