golang中map,slice(切片)是常用的类型,?slice是对数组进行封装,可以避免一些坑
package main
import (
"fmt"
"strconv"
)
func testLenCap() {
strs := make([]string,5,10) //可以只有一个数字参数,那么cap=len,make([]string,5) 等价于 make([]string,5)
fmt.Printf("value=%v,is nil=%vn",strs,strs == nil)
//strs := []string{"0","1","2","3","4"} //当然这种方式是直接赋值了,忽略赋值等价于 make([]string,5)
for i := 0; i < len(strs); i++ {
strs[i] = strconv.Itoa(i)
}
fmt.Printf("len=%v,cap=%v,strsAddress=%p,valueAddress=%pn",len(strs),cap(strs),&strs,strs) //%p内存地址
for i := len(strs); i < cap(strs); i++ {
//strs[i] = strconv.Itoa(i) //panic
strs = append(strs,strconv.Itoa(i)) //使用append向数组追加数据
}
fmt.Printf("len=%v,strs) //内存地址不变
strs = append(strs,"10")
fmt.Printf("len=%v,strs) //内存地址改变
//strs2自身的地址与strs不一样,但实际数组的内存地址不变,改变strs,strs2其中任意一个变量,另一个随之改变
strs2 := strs[:]
fmt.Printf("len=%v,len(strs2),cap(strs2),&strs2,strs2)
strs2[5] = "1000"
fmt.Printf("strs[5]=%sn",strs[5])
//strs3自身的地址与strs不一样,实际数组的内存地址也不一样,互不干扰.
startIndex := 5
strs3 := strs[startIndex:7]
fmt.Printf("len=%v,len(strs3),cap(strs3),&strs3,strs3)
fmt.Printf("cap(strs3) == cap(strs)-startIndex : %v",cap(strs3) == cap(strs)-startIndex)
}
func main() {
testLenCap()
}
?
控制台打印结果:
value=[ ],is nil=false
len=5,cap=10,strsAddress=0xc0420023e0,valueAddress=0xc04203e0a0
len=10,valueAddress=0xc04203e0a0
len=11,cap=20,valueAddress=0xc042040140
len=11,strsAddress=0xc0420024a0,valueAddress=0xc042040140
strs[5]=1000
len=2,cap=15,strsAddress=0xc0420024e0,valueAddress=0xc042040190
cap(strs3) == cap(strs)-startIndex : true
?
可以看到:
len函数是实际数据存长度;?
cap是最大容量,可以避免反复分配内存;
扩容机制是翻倍,所以go的扩容很快,尤其是基数很大的情况下.? 但如果能预先分配cap,即使再快也是无畏的消耗
使用原切片创建新切片时,应注意每个切片的值,避免出现与预想不一样的情况