目录
什么是切片
创建切片
1. 通过底层数组 (需要指定上界下界)
2. 直接创建 (直接创建切片, 会在内存中创建一个底层数组, 切片就是对底层数组的引用, 语法糖)
3. 通过make函数创建
切片的长度和容量
切片默认值(零值)
append 给切片追加元素
切片的扩容规则
总结
什么是切片
创建切片切片就是底层数组的一部分, 修改切片底层数组也会相应改变, 共享一个底层数据的不同切片都会更改, 而且切片的长度灵活多变, 切片是golang中复杂数据类型的一种, 具有长度和容量,分别通过len()和cap()来获取
1. 通过底层数组 (需要指定上界下界)创建切片有三种方式, 一种通过底层数据创建, 一种直接创建切片, 一种是通过make函数
- 切片的上下界有默认值, 上界为0 , 下界为数组或切片的长度
package main
import (
"fmt"
)
func main() {
array := [5]int{1,2,3,4,5}
slice := array[1:4]
fmt.Printf("array值%v\n", array)
fmt.Printf("slice值%v\n", slice)
}
2. 直接创建 (直接创建切片, 会在内存中创建一个底层数组, 切片就是对底层数组的引用, 语法糖)
package main
import (
"fmt"
)
func main() {
slice := []int{1,2,3}
var slice2 []int = []int{1,2,3,4}
var slice3 = []int{1,2,3,4,5}
fmt.Printf("切片的值是 %v, 类型是 %T\n",slice, slice)
fmt.Printf("切片的值是 %v, 类型是 %T\n",slice2, slice2)
fmt.Printf("切片的值是 %v, 类型是 %T",slice3, slice3)
}
3. 通过make函数创建
用make函数创建切片, 需要三个参数, 类型, 长度, 容量, 创建出来的切片成员默认都是零值, 容量可以不传, 不传默认就是长度的值, 但是长度必传
package main
import (
"fmt"
)
func main() {
slice := make([]int, 5,5)
printSlice(slice)
for i,_ := range slice {
slice[i] = i
}
printSlice(slice)
}
func printSlice(slice []int) {
fmt.Println("切片的长度和容量分别是: ", len(slice), cap(slice), "值为", slice)
}
切片的长度和容量
package main
import (
"fmt"
)
func main() {
slice := []int{1,2,3,4,5,6,7,8,9}
printSlice(slice) // 长度9 容量9
slice2 := slice[:3] // === slice[0:3]
printSlice(slice2) // 长度3 容量9
slice3 := slice[1:6]
printSlice(slice3) // 长度5 容量8
}
func printSlice(slice []int) {
fmt.Println("切片的长度和容量分别是: ", len(slice), cap(slice))
}
切片默认值(零值)
切片的默认值是nil, slice之间不能直接进行比较, 唯一可以进行比较的是nil零值, 零值的切片没有底层数组, 判断一个切片空切片应该使用len(slice) == 0 来进行判断而不是slice == nil来判断, 因为空切片不等于nil
package main
import (
"fmt"
)
func main() {
var slice []string // 声明未初始化 slice为零值nil
if (slice == nil) {
fmt.Println("slice = ", nil)
}
slice2 := []string{} // 空切片 != nil
if slice2 == nil {
fmt.Println("slice2 = ", nil)
}
if len(slice) == 0 && len(slice2) == 0 {
fmt.Println("slice2, slice 都是空切片 ")
}
}
append 给切片追加元素
append函数可以给切片增加元素
语法: append(slice, v1,v2,v3....) slice为需要添加元素的切片 v1 v2 v3 为需要添加的值
官方例子:
package main
import "fmt"
func main() {
var s []int
printSlice(s)
// 添加一个空切片
s = append(s, 0)
printSlice(s)
// 这个切片会按需增长
s = append(s, 1)
printSlice(s)
// 可以一次性添加多个元素
s = append(s, 2,3,4,5,6)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
其实前三行打印很好理解, 问题就在于第四行, 为什么我向切片中追加了7个元素, 而切片的容量却是8呢? 其实这是由于切片的扩容规则导致的, 下文会讲
切片的扩容规则当切片的容量超过超过底层数组的长度时, 就会创建一个新的数组, 将原有的值拷贝到新数组中, 分配更大的容量给切片, 俗称扩容.
扩容分为三种情况 :
- 所需容量大于旧容量的2倍时且所需容量是复数, 则分配新容量为所需容量, 若是奇数则+1, 换做计算公式: newcap >= 2oldcap / 2 === 0 ? newcap : newcap+1
- 所需容量小于旧容量的2倍并且旧容量小于1024时, 则分配新容量为2倍的旧容量
- 所需容量大于旧容量并且旧容量大于1024时, 则分配新容量为旧容量*1.25
以下部分源码
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.cap < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
有些时候会遇到不符合上述的规则的情况, 这可能是由于类型导致的
package main
import "fmt"
func main() {
var s []int32
printSlice(s)
s = append(s, 0)
printSlice(s)
s = append(s, 1,2)
printSlice(s)
}
func printSlice(s []int32) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
package main
import "fmt"
func main() {
var s []int
printSlice(s)
s = append(s, 0)
printSlice(s)
s = append(s, 1,2)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
可以看到类型的不同确实对容量大小有影响
文章部分借鉴于 Go slice扩容深度分析 - 掘金本文主要是对go slice的扩容机制进行了一些分析。环境,64位centos的docker镜像+go1.12.1。 网上很多博客也有提到,slice扩容,cap不够1024的,直接翻倍;cap超过1024的,新cap变为老cap的1.25倍。 应该是4个8?基于翻倍的思路,c…https://juejin.cn/post/6844903812331732999
总结
- 切片是复杂数组类型
- 切片之间不能进行比较, 但是可以和零值nil进行比较
- 判断空切片应用 len(slice) == 0 进行判断 而不是 slice == nil
- 切片是对底层数组的引用
- 切片扩容会创建新的底层数组, 将原有数组copy过去
- 切片有相应的扩容规则, 但受类型的影响
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)