【golang】2、深拷贝-浅拷贝-方式与性能对比:Reflect、GobEncode、JsonMarshal


在这里插入图片描述

概念

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

浅拷贝

  • 拷贝的只是数据的地址. 新老对象共享内存
  • 当改新对象时, 老对象也会被改变. 当释放新对象时, 老对象也会被释放.
  • 浅拷贝的数据类型, 即引用类型数据: 有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
}
  • 初始化数据
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"},
}

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

浅拷贝

func BenchmarkShallowCopy(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.ShallowCopy()
	}
}

结果为BenchmarkShallowCopy 0.7702 ns/op

Reflect

func BenchmarkReflect(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyReflect()
	}
}

结果为BenchmarkReflect 4515 ns/op

JsonMarshal

func BenchmarkJsonMarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyJsonMarshal()
	}
}

结果为BenchmarkJsonMarshal 11133 ns/op

GobEncode

func BenchmarkGobMarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		c.DeepCopyGobMarshal()
	}
}

结果为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))
}

结果如下可见, 确实只有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"
          }
        }

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

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

=== 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"
          }
        }

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

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

=== 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"
          }
        }

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

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

=== 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"
          }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值