GO-复合类型2-切片

切片概述

  • 切片可以理解为长度可以动态变化的数组
  • 切片和数组的相同点1:通过下标来访问元素
  • 切片和数组的相同点2:通过下标或range方式遍历元素
  • 不同点是切片的长度不是固定的,你可以动态地向切片中追加新的元素
  • 切片中可以容纳的元素个数成为容量,容量>=长度
  • 你可以通过len(slice)和cap(slice)分别获取切片的长度和容量
  • 通过make(type,len,cap)的方式你可以创建出自定义初始长度和容量的切片
  • 在追加元素的过程中,如果容量不够用时,就存在动态扩容的问题
  • 动态扩容采用的是倍增策略,即:新容量=2*旧容量
  • 扩容后的切片会得到一片新的连续内存地址,所有元素的地址都会随之发生改变

创建切片

func demob1() {
    //有类无类冒等三种方式创建出类型、值、长度、容量都相同的切片
    var s1 []int = []int{0,1,2}
    var s2 = []int{0,1,2}
    s3 := []int{0,1,2}

    //这里的初始长度和容量是相等的
    //type=[]int,value=[0 1 2],len=3,cap=3
    fmt.Printf("type=%T,value=%v,len=%d,cap=%d\n",s1,s1,len(s1),cap(s1))
    fmt.Printf("type=%T,value=%v,len=%d,cap=%d\n",s2,s2,len(s2),cap(s2))
    fmt.Printf("type=%T,value=%v,len=%d,cap=%d\n",s3,s3,len(s3),cap(s3))

    //创建长度和容量都为3的切片
    s4 := make([]int, 3)
    fmt.Printf("type=%T,value=%v,len=%d,cap=%d\n", s4, s4, len(s4), cap(s4))

    //创建3长度、4容量的切片
    s5 := make([]int, 3, 4)
    fmt.Printf("type=%T,value=%v,len=%d,cap=%d\n", s5, s5, len(s5), cap(s5))
}

向切片中追加新的元素

  • 容量每突破一次就翻倍
  • 每翻倍一次,切片就获得一片新的堆地址,每一个元素的地址也都随之发生变化
  • 切片名称的栈地址是不会变的
func demob2() {
    s := make([]int, 0)
    s = append(s, 1)
    //value=[1],len=1,cap=1,saddr=0xc04205c3e0,elemaddr=0xc042062080
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 2)
    //value=[1 2],len=2,cap=2,saddr=0xc04205c3e0,elemaddr=0xc0420620d0
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 3)
    //value=[1 2 3],len=3,cap=4,saddr=0xc04205c3e0,elemaddr=0xc0420600e0
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 4)
    //value=[1 2 3 4],len=4,cap=4,saddr=0xc04205c3e0,elemaddr=0xc0420600e0
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 5)
    //value=[1 2 3 4 5],len=5,cap=8,saddr=0xc04205c3e0,elemaddr=0xc042088100
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 6, 7, 8)
    //value=[1 2 3 4 5 6 7 8],len=8,cap=8,saddr=0xc04205c3e0,elemaddr=0xc042088100
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
    s = append(s, 9)
    //value=[1 2 3 4 5 6 7 8 9],len=9,cap=16,saddr=0xc04205c3e0,elemaddr=0xc042092080
    fmt.Printf("value=%v,len=%d,cap=%d,saddr=%p,elemaddr=%p\n", s, len(s), cap(s),&s,&s[0])
}

 

兼并其它切片

func demob3() {
    s := make([]int, 0)
    s = append(s, 1, 2, 3)
    s2 := []int{4, 5, 6}
    //追加s2中所有元素到s,注意这里的写法
    s = append(s, s2...)
    fmt.Println("s=",s)
    fmt.Println("s2=",s2)
}

截取数组或切片,获得切片

  • 截取方式:son := father[start:end]
  • start不写默认从开头截取
  • end不写默认截取到末尾
func demob4() {
    var a [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    //在数组的基础上截取,得到切片
    as1 := a[2:8]
    fmt.Printf("type=%T,value=%v\n", as1, as1)
    //截取的方式[头下标:尾下标],含头不含尾,不写头默认头=0,不写尾默截取到最后一位
    as2 := a[:8]
    as3 := a[2:]
    as4 := a[:]
    fmt.Println(as2, as3, as4)
}

切片和底层数组/切片的关系

  • 一开始切片引用底层数组的元素地址——切片和数组内容的修改会相互影响
  • 切片扩容到突破原有容量时,就拷贝内容到新的地址——此时就已经完全脱离了底层数组
func demob5() {
    var a [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    //切片不是值拷贝,而是直接底层数组地址
    as1 := a[:5]
    as2 := as1[:]

    //[0 1 2 3 4] [0 1 2 3 4]
    fmt.Println(as1, as2)

    //0xc0420140a0,0xc0420140a0,0xc0420140a0
    fmt.Printf("%p,%p,%p\n", &a[0], &as1[0], &as2[0])

    //切片不是值拷贝,而是直接底层数组地址
    as1[0] = 123
    as2[1] = 38

    //[123 38 2 3 4 5 6 7 8 9] [123 38 2 3 4] [123 38 2 3 4]
    fmt.Println(a, as1, as2)

    //扩容突破了容量(cap)的上限,切片拷贝得到新的元素地址,脱离了原先的底层数组
    //before:cap=10,addr=0xc04205c3e0,elemaddr=0xc042090000
    fmt.Printf("before:cap=%d,addr=%p,elemaddr=%p\n", cap(as2), &as2, &as2[0])
    as2 = append(as2, 666, 777, 888,999,1000,1100,1100)
    //after:cap=20,addr=0xc04205c3e0,elem addr=0xc042094000
    fmt.Printf("after:cap=%d,addr=%p,elemaddr=%p\n", cap(as2), &as2, &as2[0])

    as2[0] = 90000
    //打印的结果就是:as2脱离了a和as1,自成一家
    fmt.Println(a, as1, as2)
    a[0] = 0
    fmt.Println(a, as1, as2)
}

拷贝src切片,覆盖dst切片

func demob6() {
    //var dst = []int{1,2,3,4}
    //var src = []int{10,20,30,40}
    //var dst = []int{1,2,3,4}
    //var src = []int{10,20,30,40,50}
    var dst = []int{1, 2, 3, 4, 5, 6}
    var src = []int{10, 20, 30, 40}
    //src中的元素从头覆盖dst中的元素,返回受影响的元素个数
    n := copy(dst, src)
    fmt.Println(dst, n)
}

遍历访问切片元素

func demob7() {
    s := make([]int, 0)
    s = append(s, 10, 20, 30, 40, 50)
    for index, value := range s {
        fmt.Println(index, value)
    }
}
阅读更多
换一批

没有更多推荐了,返回首页