zoukankan      html  css  js  c++  java
  • golang学习笔记 ---slice

    Go 语言中的slice类型可以理解为是数组array类型的描述符,包含了三个因素:
    1. 指向底层数组的指针 
    2. slice目前使用到的底层数组的元素个数,即长度
    3. 底层数组的最大长度,即容量

    因此当我们定义一个切片变量,s := make([]int, 5, 10),即为指向了一个最大长度为10的底层数组,目前切片s使用到的长度为5。 

    基于slice的定义,在使用slice时,有以下几点注意事项:

    1.对slice进行切分操作
    对slice进行切分操作会生成一个新的slice变量,新slice和原来的slice指向同一个底层数组,只不过指向的起始位置可能不同,长度及容量可能也不相同。
    • 当从左边界有截断时,会改变新切片容量大小
    • 左边界默认0,最小为0;右边界默认slice的长度,最大为slice的容量
    • 当然,因为指向同一个底层数组,对新slice的操作会影响到原来的slice
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    
    	a := make([]int, 5, 10)
    
    	b := a[:3]
    	//b  len: 3 cap: 10
    	fmt.Println("b-- ", "len:", len(b), " ,cap:", cap(b))
    
    	//当从左边界有截断时 会改变新切片容量大小
    	c := a[2:4]
    	//c  len: 2 cap: 8
    	fmt.Println("c-- ", "len:", len(c), " ,cap:", cap(c))
    
    	//左边界默认0 最小为0 | 右边界默认slice的长度 最大为slice的容量
    	d := a[:cap(a)]
    	//d  len: 10 cap: 10
    	fmt.Println("d-- ", "len:", len(d), " ,cap:", cap(d))
    }
    

      输出:

    b--  len: 3  ,cap: 10
    c--  len: 2  ,cap: 8
    d--  len: 10  ,cap: 10
    

      

    2.slice的赋值及函数间传递

    a := []int{1, 2, 3, 4, 5}
    b := a
    

     

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    
    	a := []int{1, 2, 3, 4, 5}
    	b := a
    
    	//对b进行操作会影响到a
    	b[0] = 10
    	// 10 2 3 4 5
    	fmt.Println(a)
    }
    

      输出:

    [10 2 3 4 5]
    

    如上所示,则a, b指向同一个底层数组,且长度及容量因素相同,对b进行的操作会影响到a。 

    切片作为函数参数示例:

    package main
    
    import (
    	"fmt"
    )
    
    func modifySlice(s []int) {
    	s[0] = 10
    }
    
    func main() {
    	a := []int{1, 2, 3, 4, 5}
    	modifySlice(a)
    	//[10 2 3 4 5]
    	fmt.Println(a)
    }
    

      输出:

    [10 2 3 4 5]
    

      如上所示,将slice作为参数在函数间传递的时候是值传递,产生了一个新的slice,只不过新的slice仍然指向原来的底层数组,所以通过新的slice也能改变原来的slice的值 

    package main
    
    import (
    	"fmt"
    )
    
    func modifySlice(s []int) {
    	s = []int{10, 20, 30, 40, 50}
         s[0] = -1 } func main() { a := []int{1, 2, 3, 4, 5} modifySlice(a) //[1 2 3 4 5] fmt.Println(a) }

      如上所示,在调用函数的内部,将s整体赋值一个新的slice,并不会改变a的值,因为modifySlice函数内对s重新的整体赋值,让s指向了一个新的底层数组,而不是传递进来之前的a指向的那个数组,之后s的任何操作都不会影响到a了。

    3.slice的append操作

    append操作最容易踩坑,下面详细说明一下。
     
    append函数定义:  
     
     func append(s []T, x ...T) []T 
     
    Append基本原则:对于一个slice变量,若slice容量足够,append直接在原来的底层数组上追加元素到slice上;如果容量不足,append操作会生成一个新的更大容量的底层数组。 
     
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := make([]int, 2, 4)
    	fmt.Println("initialize.....")
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//通常append操作都是将返回值赋值给自己,
    	//此处为了方便说明,没有这样做
    
    	//改变b的前2个元素值 会影响到a
    	a[0] = 99
    	fmt.Println("赋值之后.....")
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    	fmt.Println("Append之后.....")
    	b := append(a, 2)
    	b[0] = 1
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    
    	a = append(a, 3)
    	fmt.Println("a Append之后.....")
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    
    	a = append(a, 4)
    	a = append(a, 5)
    
    	fmt.Println("a 再次Append之后.....")
    	//a 扩容了,指向不同的底层数组
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    	//a的扩容没有影响到b,b还是指向原来的底层数组
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    }
    

      输出:

    initialize.....
    a: [0 0]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    赋值之后.....
    a: [99 0]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    Append之后.....
    a: [1 0]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    b: [1 0 2]  &b[0]: 0xc0000105a0  len: 3  cap: 4
    a Append之后.....
    a: [1 0 3]  &a[0]: 0xc0000105a0  len: 3  cap: 4
    b: [1 0 3]  &b[0]: 0xc0000105a0  len: 3  cap: 4
    a 再次Append之后.....
    a: [1 0 3 4 5]  &a[0]: 0xc000014240  len: 5  cap: 8
    b: [1 0 3]  &b[0]: 0xc0000105a0  len: 3  cap: 4
    
    a 再次Append之后,a 扩容了,指向不同的底层数组,a的扩容没有影响到b,b还是指向原来的底层数组

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := make([]int, 2, 4)
    	a[0] = 10
    	a[1] = 20
    	//a: [10 20]  &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//进行append操作
    	b := append(a[:1], 1)
    
    	//a: [10 1]   &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//b: [10 1]   &b[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    }
    

      输出:

    a: [10 20]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    a: [10 1]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    b: [10 1]  &b[0]: 0xc0000105a0  len: 2  cap: 4
    

     

    如上所示,若append后的b的实际元素个数没有超过原来的a指向的底层数组的容量,那么a,b指向同一个底层数组。
    注意此时append的操作对象是:对a进行切分之后的切片,只取了a的第一个值,相当于一个新切片,长度为1,和a指向同一个底层数组,我们称这个切分后的新切片为c吧,那么就相当于b其实是基于c切片进行append的,直接在长度1之后追加元素,所以append之后a的第二个元素变成了1。【所以切分操作和append操作放一起的时候,一定要小心】 

     

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := make([]int, 2, 4)
    	a[0] = 10
    	a[1] = 20
    	//a: [10 20]  &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//进行append操作
    	//b := append(a[:1], 1)
    	c := a[:1]
    	fmt.Println("c:", c, " &c[0]:", &c[0], " len:", len(c), " cap:", cap(c))
    	b := append(c, 1)
    
    	//a: [10 1]   &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//b: [10 1]   &b[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    }
    

      输出:

    a: [10 20]  &a[0]: 0xc000072140  len: 2  cap: 4
    c: [10]  &c[0]: 0xc000072140  len: 1  cap: 4
    a: [10 1]  &a[0]: 0xc000072140  len: 2  cap: 4
    b: [10 1]  &b[0]: 0xc000072140  len: 2  cap: 4
    

      

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    
    	a := make([]int, 2, 4)
    	a[0] = 10
    	a[1] = 20
    	//a: [10 20]       &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//进行append操作
    	//append是在第一个元素后开始追加,所以要超过容量,至少要追加4个,而不是之前例子的3个
    	b := append(a[:1], 1, 2, 3, 4)
    
    	//a: [10 20]       &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	//b: [10 1 2 3 4]  &b[0]: 0x10454020  len: 5  cap: 8
    	fmt.Println("b:", b, " &b[0]:", &b[0], " len:", len(b), " cap:", cap(b))
    }
    

      如果append的元素数较多,超过了原来的容量,直接采用了新的底层数组,也就不会影响到a了。 

    输出:

    a: [10 20]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    a: [10 20]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    b: [10 1 2 3 4]  &b[0]: 0xc000014240  len: 5  cap: 8
    

      

    package main
    
    import (
    	"fmt"
    )
    
    func testAppend(s []int) {
    	//进行append操作
    	s = append(s, 1)
    	//s: [10 1]  &s[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("s:", s, " &s[0]:", &s[0], " len:", len(s), " cap:", cap(s))
    }
    func main() {
    
    	a := make([]int, 2, 4)
    	a[0] = 10
    	a[1] = 20
    	//a: [10 20]  &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	testAppend(a[:1])
    
    	//a: [10 1]   &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    }
    

      输出:

    a: [10 20]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    s: [10 1]  &s[0]: 0xc0000105a0  len: 2  cap: 4
    a: [10 1]  &a[0]: 0xc0000105a0  len: 2  cap: 4
    

      

    import (
    	"fmt"
    )
    
    func testAppend(s []int) {
    	//进行append操作
    	s = append(s, 1, 2, 3, 4)
    	//s: [10 1]  &s[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("s:", s, " &s[0]:", &s[0], " len:", len(s), " cap:", cap(s))
    }
    func main() {
    
    	a := make([]int, 2, 4)
    	a[0] = 10
    	a[1] = 20
    	//a: [10 20]  &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    	testAppend(a[:1])
    
    	//a: [10 1]   &a[0]: 0x10410020  len: 2  cap: 4
    	fmt.Println("a:", a, " &a[0]:", &a[0], " len:", len(a), " cap:", cap(a))
    
    }
    

      输出

    a: [10 20] &a[0]: 0xc0000105a0 len: 2 cap: 4
    
    s: [10 1 2 3 4] &s[0]: 0xc000014240 len: 5 cap: 8
    
    a: [10 20] &a[0]: 0xc0000105a0 len: 2 cap: 4
    

      

    参考:

    https://blog.golang.org/go-slices-usage-and-internals

     

  • 相关阅读:
    Latex文件如何拆分进行独立编译?
    梯度下降优化算法
    Google大脑科学家贾杨清(Caffe缔造者)-微信讲座
    Jeecg 如何执行批量insert或者update操作,高效率
    【JEECG_3.7.1】列表多表头的设计
    t:formvalid中定义callback函数
    Excel导入的时候日期格式会变成double式的String数据处理
    sql 跨表修改的方式
    js判断字符串str是否包含字符串substr
    帆软报表和jeecg的进一步整合--ajax给后台传递map类型的参数
  • 原文地址:https://www.cnblogs.com/saryli/p/11660473.html
Copyright © 2011-2022 走看看