slice 数据结构
type slice struct {
array unsafe.Pointer //指向底层数组的指针
len int //切片中元素个数
cap int //切片总容量
}
-
基于数组或者slice生成一个slice的时候,新的slice和原来数组/slice 的底层数组是同一个
-
基于数组或者slice生成slice的cap=原来对应的数组长度-现在slice的第一个元素对应的索引
-
slice 作为参数传递的是 副本,但是对应的数组的指针不变
package main
import "fmt"
var arr = []int{1,2,3,4,5}
func testSliceAsParam1(s1 []int) {
s1[0] = 5 //扩容前修改,影响传入slice,因为s1指向底层数组没变,但是底层数组元素值变了
return
}
func testSliceAsParam2(s1 []int) {
s1 = append(s1, 6) //扩容后修改,不影响传入slice,因为扩容后产生了新的底层数组,函数体内的
//副本s1,其指向底层数组的指针已经改变,指向新生成的底层数组,此时修改只
//是修改新的底层数组,原s1指向的底层数组元素值没变
s1[0] = 9
return
}
func main() {
s1 := arr[:5]
fmt.Println("before1: ")
fmt.Println(s1)// [1 2 3 4 5]
testSliceAsParam1(s1)
fmt.Println("after1: ")
fmt.Println(s1)// [5 2 3 4 5]
testSliceAsParam2(s1)
fmt.Println("after2: ")
fmt.Println(s1) //[5 2 3 4 5]
}
- 扩容规则:
在一般的情况下,你可以简单地认为新切片的容量(以下简称新容量)将会是原切片容量(以下简称原容量)的 2 倍。
但是,当原切片的长度(以下简称原长度)大于或等于1024时,Go 语言将会以原容量的1.25倍作为新容量的基准(以下新容量基准)
slice 数组指针什么情况下会发生变化?
确切地说,一个切片的底层数组永远不会被替换。为什么?虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它是把新的切片作为了新底层数组的窗口,而没有对原切片及其底层数组做任何改动。
测试:
append扩容后,确实产生了新的底层数组,也生成了新的切片;但当你用原切片作为append的接收者,因为‘=’是值传递,你看到切片的地址是没有改变的。
package main
import "fmt"
var arr = []int{1,2,3,4,5}
func main() {
s1 := arr[:5]
fmt.Printf("%p", &s1) //0xc0420023e0
fmt.Println()
s2 := append(s1, 6) //确实产生了新的底层数组和新的切片
fmt.Printf("%p", &s2) //0xc042002420
fmt.Println()
s1 = s2 //值传递,这里等价于s1 = append(s1,6)
fmt.Printf("%p", &s1) //0xc0420023e0
}
slice删除元素
- 切片的一大好处是可以让我们通过窗口快速地定位并获取,或者修改底层数组中的元素
- 不过,当我们想删除切片中元素的时候就没那么简单了。元素复制一般是免不了的,就算只删除一个元素,有时也会造成大量元素的移动
package main
import "fmt"
var arr = []int{1,2,3,4,5,6,7,8,9}
func main() {
s1 := append(arr[:2], arr[3:]...)
fmt.Println(s1) //[1 2 4 5 6 7 8 9]
fmt.Println(arr) //[1 2 4 5 6 7 8 9 9] 有大量元素移动
}
for range 遍历切片
type student struct {
Name string
Age int
}
func pase_student1() {
m := make(map[string]*student)
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
for _, stu := range stus {
//stu是副本,&stu是一个定值,
//而里边存的东西会变,
//最终存储切片最后一个值
m[stu.Name] = &stu
}
for k, v := range m {
fmt.Println(k, v)
}
}
func pase_student2() {
m := make(map[string]student)
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
for _, stu := range stus {
m[stu.Name] = stu
}
for k, v := range m {
fmt.Println(k, v)
}
}
func main() {
pase_student1()
pase_student2()
}
slice练习题
package main
import "fmt"
func main(){
s1 := []int{1,2} // s1 len是2,cap也是2
s2 := s1
s2 = append(s2,3) // s2有扩容,len是3,cap是4,此时s2指向的底层数组指针已经和s1不一样
Test1(s1) // s1形参传递后,Test1函数中的s是新变量,但是其底层数组是一个,Test1中append后,s指向的底层数组指针变了,所以对s的修改不会影响s1
Test1(s2) // s2再Test1中append后,没有扩容,所以s2指向的底层数据和s一样,Test1中对s的修改,生效在了底层数组,然后影响到了s2,但s2和s是不同的变量,所以s2还是只有数组前三个元素
fmt.Println(s1,s2)
}
func Test1(s []int){
s = append(s,0)
for i:= range s{
s[i]++
}
}
// 选项
A:[2,3] [2,3,4]
B:[1,2] [1,2,3]
C:[1,2] [2,3,4]
D:[2,3,1] [2,3,4,1]
答案是c