切片(slice)

slice 数据结构

type slice struct {
    array unsafe.Pointer  //指向底层数组的指针
    len   int //切片中元素个数
    cap   int //切片总容量
}

golang 源码

  • 基于数组或者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

转载slice删除元素的性能对比

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值