【Let's Go!】 开发小技巧

2. 开发小技巧

2.1. 字符串拼接

字符串的拼接建议使用strings.Builder

// A Builder is used to efficiently build a string using Write methods.

字符串高效拼接

下图是我自己电脑跑出来的结果

image-20210910234314914

可以看出预先分配的strings.Builder最为快速且分配次数最少

func builderPreConcat(n int, str string) string {
    var builder strings.Builder
    builder.Grow(n * len(str))
    for i := 0; i < n; i++ {
      builder.WriteString(str)
    }
    return builder.String()
}

2.2. 切片的使用

切片的本质是对底层数组的抽象

类型展示

func Slice() {
	a := [3]int{1, 2, 3}   // 长度为3的数组
	b := [2]int{1, 2}      // 长度为2的数组
	c := [...]int{1, 2, 3} // 自动计算长度的数组
	d := []int{1, 2, 3, 4} // 切片

	a = b // 长度不一致的数组,属于不同类型,无法实现赋值
}

切片操作并不会复制底层数组,而是会创建一个新的切片复用底层数组

func Slice() {
	nums := make([]int, 0, 8)
	nums = append(nums, 1, 2, 3, 4, 5, 6, 7, 8)
	nums2 := nums[2:4]

	fmt.Println(nums)  // [1 2 3 4 5 6 7 8]
	fmt.Println(nums2) // [3 4]

	nums2 = append(nums2, 50, 60) // 修改了底层数组!!!!

	fmt.Println(nums)  // [1 2 3 4 50 60 7 8]
	fmt.Println(nums2) // [3 4 50 60]

}

切片操作指南

Go Slice Tricks Cheat Sheet

如果在某些场景中,只用到了底层数组的小部分元素,那么可以尝试使用copy的方式替代re-slice操作,因为re-slice操作不会新建底层数组,导致长时间引用原底层数组,GC无法释放该内存,而copy会新建底层数组,使得原底层数组内存得到释放。

2.3. 空结构体节省内存

空结构体不占据内存空间,因此被广泛作为各种场景下的占位符使用。一是节省资源,二是空结构体本身就具备很强的语义,即这里不需要任何值,仅作为占位符。

func EmptyStruct() {
		fmt.Println(unsafe.Sizeof(struct{}{}))
}
// 0

可以使用map[string]struct{}实现setstruct{}仅作占位符

type Set map[string]struct{}

func DoSet() {
    s := make(Set)
    s["123"] = struct{}{}
    for k, v := range s {
      fmt.Println(k,v)
		}
}

// 123 {}

也可以作为不发送数据的信道(channel)

Go 空结构体 struct{} 的使用

2.4. for和range的性能对比

range返回的是对象的拷贝

func Loop() {
    data := []struct {
      Num int
    }{
      {
        Num: 1,
      },
      {
        Num: 2,
      },
    }

    for i := 0; i < len(data); i++ {
      data[i].Num = data[i].Num + 10
    }

    fmt.Println(data)

    for _, v := range data {
      v.Num = v.Num + 1
    }

    fmt.Println(data)
}
// [{11} {12}]
// [{11} {12}]

range 在迭代过程中返回的是迭代值的拷贝,如果每次迭代的元素的内存占用很低,那么 for 和 range 的性能几乎是一样,例如 []int。但是如果迭代的元素内存占用较高,例如一个包含很多属性的 struct 结构体,那么 for 的性能将显著地高于 range,有时候甚至会有上千倍的性能差异。对于这种场景,建议使用 for,如果使用 range,建议只迭代下标,通过下标访问迭代值,这种使用方式和 for 就没有区别了。如果想使用 range 同时迭代下标和值,则需要将切片/数组的元素改为指针,才能不影响性能。——《Go 语言高性能编程》for 和 range 的性能比较

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×