1.1.1. 一、切片的介绍。

1、声明切片的语法:

var 切片名 []类型

例如:var a [] int 表示声明一个切片[]int类型的变量a。

2、切片的长度是可以变化的,因此切片是一个可以动态变化的数组。

3、切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。

4、切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样。

示例如下:

func main() {
    intArr := [...]int{11,21,30,50}
    /**
     1、声明一个切片。名字是slice1
     2、intArr[1:3] 表示slice1 引用intArr数组下标从1到3的元素。包含1不包含3。
     */
    slice1 := intArr[1:3]
    //intArr= [11 21 30 50]
    fmt.Println("intArr=", intArr)
    //slice1的元素= [21 30]
    fmt.Println("slice1的元素=",slice1)
    //slice1的元素个数= 2
    fmt.Println("slice1的元素个数=",len(slice1))
    //slice1的容量= 3
    fmt.Println("slice1的容量=", cap(slice1)) //切片的容量可以动态变化
}

示例2如下:数组的intArr[1]地址等于slice1[0]的地址。表示创建切片的时候只是把数组 intArr 元素的地址复制给了切片slice1。改变intArr[1]、slice1[0]其中一个值,另一个值会一起变化。

func main() {
    intArr := [...]int{11,21,30,50}
    slice1 := intArr[1:3]
    // intArr[1]的地址= 0xc0000600c8
    fmt.Println("intArr[1]的地址=", &intArr[1])
    // slice1[0]的地址= 0xc0000600c8
    fmt.Println("slice1[0]的地址=",&slice1[0])
}

5、切片的底层结构。

5.1、slice 是一个引用类型。

5.2、slice 从底层来说,其实就是一个数据结构(struct 结构体)。其中指针指向起始位置;len表示元素的个数;cap表示切片的容量。

type slice struct { 
    ptr *[2]int 
    len int 
    cap int 
}

1.1.2. 二、创建切片的3种方式。

方式1:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的。

    intArr := [...]int{11,21,30,50}
    slice1 := intArr[1:3]

方式2:通过 make 来创建切片。

基本语法:var 切片名 []type = make([]type, len, [cap])

参数说明:type:就是数据类型; len:大小; cap :指定切片容量(可选), 如果你分配了 cap,则要求 cap>=len。

    var slice2 []float64 = make([]float64, 3, 5)
    slice2[0] = 20 //给切片的元素赋值
    // slice2= [20 0 0]
    fmt.Println("slice2=", slice2)
  • 通过 make 方式创建切片可以指定切片的大小和容量 。虽然容量为5,但是只初始化3个元素。
  • 如果没有给切片的各个元素赋值,那么就会使用元素的数据类型的零值。
  • 通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去访问各个元素。

方式3:定义一个切片,直接就指定具体数据。

    var strSlice = []string{"tom","jack"}
    //strSlice= [tom jack]
    fmt.Println("strSlice=", strSlice)
    //strSlice 长度= 2
    fmt.Println("strSlice 长度=", len(strSlice))
    // strSlice 容量= 2
    fmt.Println("strSlice 容量=", cap(strSlice))

1.1.3. 三、切片的遍历(和数组一样)。

切片的遍历和数组一样,也有两种方式 :

  • 1、 for 循环常规方式遍历 。
  • 2、for-range 方式遍历。
    var strSlice = []string{"tom","jack"}
    //普通遍历
    for i := 0; i < len(strSlice); i++ {
        fmt.Printf("strSlice[%v]=%v \n",i,strSlice[i])
    }
    // for-range遍历
    for i,v:= range strSlice {
        fmt.Printf("strSlice[%v]=%v \n",i,v)
    }

1.1.4. 四、 切片的使用的注意事项。

1、切片初始化时var slice = arr[startIndex:endIndex]说明:从 arr 数组下标为 startIndex,取到 下标为 endIndex 的元素(不含 arr[endIndex])。

2、切片初始化时,仍然不能越界。范围在 [0-len(arr)] 之间,但是可以动态增长。

  • var slice = arr[0:end] 可以简写 var slice = arr[:end]

  • var slice = arr[start:len(arr)] 可以简写: var slice = arr[start:]

  • var slice = arr[0:len(arr)] 可以简写: var slice = arr[:]

3、cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。

4、切片可以继续切片。

例子如下:slice2是slice1继续切片之后的结果。arr、slice1、slice2指向的元素地址相同。改变其中一个另一个会改变。

    var arr  = [...]int{11,2,3,54}
    var slice1 = arr[1:4] // 切片的元素是arr下标从1到4的元素
    fmt.Println(slice1) // [2 3 54]

    //切片继续切片
    var slice2  =  slice1[1:] // 切片slice2的元素是slice1下标从1开始之后的元素
    fmt.Println(slice2) // [3 54]

5、 用 append 内置函数,可以对切片进行动态追加。

示例如下:使用append给slice1 增加了3个元素。原来的slice1 不变,返回的slice2是追加元素之后的数组

      var slice1 = []int{6,13}
    slice2 := append(slice1, 4, 8, 90)
    fmt.Println(slice1) // [6 13]
    fmt.Println(slice2) // [6 13 4 8 90]

    // slice1... 表示把slice1的元素遍历放在这里做参数。
    //把slice1的元素追加给slice1。
    slice3 := append(slice1, slice1...)
    fmt.Println(slice3) // [6 13 6 13]

append函数的底层原理分析:

切片 append 操作的本质就是对数组扩容

go 底层会创建一下新的数组 newArr(安装扩容后大小)

将 slice1 原来包含的元素拷贝到新的数组 newArr,并且返回新的数组。

可以把切片的元素追加给另一个切片:slice3 := append(slice1, slice1...) 。第二个参数slice1... 必须是切片,不能是数组。

6、切片的拷贝操作。

切片使用 copy 内置函数完成拷贝,例子如下:

    var slice1 = []int{6,13}
    //slice2 := make([]int,3)
    slice2 := []int{1,5,9}

    //把 slice1 的元素拷贝给slice2。
    // slice1 的元素会按照下标覆盖 slice2的元素
    copy(slice2,slice1)

    fmt.Println("slice2=",slice2) //slice2= [6 13 9]
    fmt.Println("slice1=",slice1) //slice1= [6 13]

说明:

  • copy(slice2,slice1) 函数的2个参数都必须是切片。
  • copy(slice2,slice1) slice1 的元素会按照下标覆盖 slice2的元素。
  • copy(slice2,slice1) 拷贝完成后,slice1和slice2的数据空间相互独立。改变slice2的元素值不影响slice1的元素值。同理改变slice1也不影响slice2。

拷贝数组的时候。如果接收方的数组长度不够。那只拷贝一部分。

例子如下:slice2只有一个元素,slice1有2个元素。把slice1拷贝给slice2。slice2只接受一个元素。

    var slice1 = []int{6,13}
    slice2 := make([]int,1)
    copy(slice2,slice1)

    fmt.Println("slice2=",slice2) //slice2= [6]
    fmt.Println("slice1=",slice1) //slice1= [6 13]

7、切片是引用类型,所以在函数传参、变量赋值时,遵守引用传递机制。

8、切片元素的删除。

Go 语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素。示例代码如下:

    seq := []string{"a", "b", "c", "d", "e"}
    // 指定要删除元素的位置,就是"c"
    index := 2
    // 查看删除位置之前的元素和之后的元素
    fmt.Println(seq[:index], seq[index+1:])// [a b] [d e]
    // 将删除点前后的元素连接起来
    seq = append(seq[:index], seq[index+1:]...) 
    fmt.Println(seq) // [a b d e]

results matching ""

    No results matching ""