《Go小技巧&易错点100例》第二十篇

2024-08-13 22:41:18 浏览数 (2)

本次内容:

  • 使用slice和map的内置函数
  • 避免不必要的类型转换
  • 优雅的字符串拼接方式

正文:

使用slice和map的内置函数

在Go语言中,append(), copy(), len(), cap() 函数是处理切片(slice)时非常常用的内置函数,但它们并不直接涉及 delete(),因为 delete() 是用于处理映射(map)的内置函数,用于删除映射中的键值对。下面是每个函数的作用说明:

1)append(slice []Type, elems ...Type) []Type :用于向切片末尾追加一个或多个元素,并返回一个新的切片(可能是原切片的扩展,也可能是新分配的切片)。如果切片的空间不足以存储追加的元素,append 会分配一个更大的切片,并将原切片的内容复制到新切片中,然后追加新的元素。

2)copy(dst, src []Type) int :用于将源切片(src)的元素复制到目标切片(dst)中。它会返回复制的元素数量。复制操作会同时考虑dstsrc的长度,以两者中较短的一个为准。如果dst有足够的空间存储src的所有元素,则复制所有元素;否则,只复制能放入dst中的元素数量。

3)len(v Type) int :返回其参数的长度。对于切片,它返回切片中元素的数量。

4)cap(v Type) int :返回其参数的容量。对于切片,它返回切片底层数组的总大小。切片的容量是切片能够增长到的最大长度,而不需要重新分配内存。

5)delete(m map[Type]Type1, key Type) :用于从映射中删除指定的键值对。如果映射中存在该键,则删除它;如果不存在,则不做任何操作。

总的来说,append(), copy(), len(), cap() 是处理切片时的重要函数,而 delete() 是专门用于处理映射的。

示例

代码语言:go复制
func TestInnerFunc(t *testing.T) {
	// append
	var nums []int
	for i := 0; i < 10; i   {
		nums = append(nums, i)
	}
	fmt.Println("nums = ", nums)

	// delete
	m := make(map[string]string)
	m["a"] = "A"
	m["b"] = "B"
	m["c"] = "C"
	fmt.Println("before delete = ", m)
	delete(m, "b")
	fmt.Println("after delete = ", m)

	// copy
	var numsc []int
	copy(nums, numsc)
	fmt.Println("numsc = ", nums)

	numsn := make([]int, 10)
	numsn[0] = 1

	//cap
	fmt.Println("nums cap = ", cap(nums))
	fmt.Println("numsn cap = ", cap(numsn))

	//len
	fmt.Println("nums len = ", len(nums))
	fmt.Println("numsn len = ", len(numsn))
}

输出:

代码语言:shell复制
nums =  [0 1 2 3 4 5 6 7 8 9]
before delete =  map[a:A b:B c:C]
after delete =  map[a:A c:C]
numsc =  [0 1 2 3 4 5 6 7 8 9]
nums cap =  16
numsn cap =  10
nums len =  10
numsn len =  10

因为cap()函数是获取数组的容量,因此Go的扩容机制也需要了解下:

Go语言的slice扩容策略在不同的版本和slice容量大小下有所不同,但总体思路是相似的,即创建一个更大的底层数组,并将原始数据复制到新数组中。

Go 1.7及之前版本,如果当前容量小于1024,每次扩容后的容量都会翻倍。如果当前容量大于等于1024,扩容后的容量会按照增长因子(默认为1.25)来计算,即新的容量为原容量的1.25倍。

Go 1.8及之后版本,如果当前容量小于256,每次扩容后的容量都会翻倍。如果当前容量大于等于256且小于4096,扩容后的容量会按照增长因子(这里为1.5)来计算。如果当前容量大于等于4096,扩容后的容量会按照增长因子(默认为1.25)来计算。

避免不必要的类型转换

类型转换是昂贵的操作,应该避免在性能敏感的代码中进行不必要的类型转换。

示例:避免在循环中进行类型转换

代码语言:go复制
// 不推荐
var nums []interface{} = [...]interface{}{1, 2, 3, 4}
for _, num := range nums {
    if v, ok := num.(int); ok {
        fmt.Println(v)
    }
}

// 推荐(如果类型已知)
var nums []int = [...]int{1, 2, 3, 4}
for _, num := range nums {
    fmt.Println(num)
}

优雅的字符串拼接方式

对于大量的字符串拼接操作,使用strings.Builder或(在Go 1.10及以后)编译器优化的 操作符通常比使用fmt.Sprintf更高效。

示例

代码语言:go复制
const (
	hello = "Hello "
	world = "World"
	sign  = "!"
)

// 推荐
func userBuilder() string {
	builder := strings.Builder{}
	builder.WriteString(hello)
	builder.WriteString(world)
	builder.WriteString(sign)
	return builder.String()
}

// 推荐
func userPlus() string {
	return hello   world   sign
}

// 不推荐
func userFmt() string {
	return fmt.Sprintf("%s%s%s", hello, world, sign)
}

本篇结束~

0 人点赞