切片的扩容和缩容
Go 语言中的切片是一种动态数组,它可以自动扩容和缩容以适应不同的数据量。在实际开发中,了解切片的扩容和缩容机制对于优化内存使用和提高程序性能至关重要。
切片的扩容
当向切片中添加元素时,如果切片的容量不足,就会触发切片的扩容机制。切片的扩容会分配一个新的数组,并将原来的元素复制到新数组中。新数组的长度通常是原数组长度的两倍,但是如果原数组长度小于 1024,新数组的长度会比原数组长度大 1 倍;如果原数组长度大于等于 1024,新数组的长度会比原数组长度大 1/4 倍。
切片的扩容可以通过内置函数 append
来实现。当切片容量不足时,调用 append
函数会触发切片的扩容。以下是一个示例代码,它演示了如何向切片中添加元素:
package main
import "fmt"
func main() {
s := make([]int, 3, 5)
fmt.Println(len(s)) // 输出:3
fmt.Println(cap(s)) // 输出:5
s = append(s, 1, 2, 3)
fmt.Println(len(s)) // 输出:6
fmt.Println(cap(s)) // 输出:10
}
在上面的示例中,首先使用 make
函数创建了一个长度为 3,容量为 5 的切片 s
。然后向切片 s
中添加了三个元素,此时切片的长度变为 6,容量变为 10。注意,添加元素后,切片 s
的底层数组已经发生了变化,原来的元素已经被复制到新数组中。
切片的缩容
当从切片中删除元素时,如果切片的长度小于容量的 1/4,就会触发切片的缩容机制。切片的缩容会释放底层数组中未使用的空间。新数组的长度通常是原数组长度的一半,但是如果原数组长度小于 1024,新数组的长度等于原数组长度;如果原数组长度大于等于 1024,新数组的长度会比原数组长度小 1/2 倍。
切片的缩容不需要显式调用任何函数,Go 语言的垃圾回收机制会自动进行。以下是一个示例代码,它演示了如何从切片中删除元素:
代码语言:javascript复制package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
fmt.Println(len(s)) // 输出:5
fmt.Println(cap(s)) // 输出:5
s = s[:3]
fmt.Println(len(s)) // 输出:3
fmt.Println(cap(s)) // 输出:5
s = s[:2]
fmt.Println(len(s)) // 输出:2
fmt.Println(cap(s)) // 输出:5
s = s[:1]
fmt.Println(len(s)) // 输出:1
fmt.Println(cap(s)) // 输出:5
s = s[:0]
fmt.Println(len(s)) // 输出:0
fmt.Println(cap(s)) // 输出:5
s = nil
fmt.Println(len(s)) // 输出:0
fmt.Println(cap(s)) // 输出:0
}
在上面的示例中,首先创建了一个长度为 5 的切片 s
,长度和容量都为 5。然后通过 s[:3]
的方式从切片中删除了最后两个元素,此时切片的长度变为 3,容量不变。接着通过 s[:2]
的方式从切片中删除了最后一个元素,此时切片的长度变为 2,容量不变。然后通过 s[:1]
的方式从切片中删除了最后一个元素,此时切片的长度变为 1,容量不变。最后通过 s[:0]
的方式从切片中删除了所有元素,此时切片的长度变为 0,容量不变。最后将切片 s
赋值为 nil
,此时切片的长度和容量都变为 0。
需要注意的是,虽然切片的缩容会释放底层数组中未使用的空间,但是并不一定能立即回收内存,具体的回收时机由 Go 语言的垃圾回收机制决定。在实际开发中,可以通过将不再使用的切片赋值为 nil
的方式来帮助垃圾回收机制及时回收内存。