go 切片

2019-03-27  本文已影响0人  StevenQin

基本入门

先看一个需求:我们需要一个数组来保存学生的成绩,但是学生的个数是不确定的。解决方案:用到切片(可以理解为动态的数组)

var 切片名 []类型
比如:var a []int
func main() {
    //定义一个数组
    var intArr [5]int = [...]int{1, 22, 33, 66, 99}
    //定义一个切片
    //slice是一个切片
    //引用了intArr数组的,起始下标为1,终止下标为3(但是不包含3)
    slice := intArr[1:3]
    fmt.Println("intArr数组元素是 ", intArr)
    fmt.Println("slice切片的元素是 ", slice)

    fmt.Println("slice切片的长度是 ", len(slice))
    fmt.Println("slice切片的容量是 ", cap(slice)) //容量 cap
    //打印intArr[1]的内存中的地址  %p 格式化为0x开头的十六进制
    fmt.Printf("intArr[1]的地址=%p\n", &intArr[1])
    //打印slice的地址
    fmt.Printf("slice的地址=%p\n", &slice)
    slice[1] = 34
    fmt.Println()
    fmt.Println()

    fmt.Println("intArr数组元素是 ", intArr)
    fmt.Println("slice切片的元素是 ", slice)
}

切片中的内存布局(对上面代码的内存图解

type slice struct{
  ptr *[2]int  //指针
  len int  //长度
  cap int  //容量
}

切片使用(3种方式)

1、方式一

    var arr [5]int = [...]int{1, 2, 3, 4, 5}
    slice := arr[1:3]

2、方式二

语法: var 切片名 [ ]type = make([ ]type , len , [cap])
参数说明:type 数据类型 len 长度 cap 容量

func main() {
    var slice []int = make([]int, 4, 10)
    //默认值
    fmt.Println("默认值为:", slice)
    // 长度
    fmt.Printf("slice的长度为:%v\n", len(slice))
    // 容量
    fmt.Printf("slice的容量为:%v\n", cap(slice))
    slice[0] = 100
    slice[2] = 200
    fmt.Println(slice)
}
var slice []int = make([]int, 5, 10)
    //默认值
    fmt.Println("默认值为:", slice)
    // 长度
    fmt.Printf("slice的长度为:%v\n", len(slice))
    // 容量
    fmt.Printf("slice的容量为:%v\n", cap(slice))
    slice[1] = 10
    slice[3] = 20
    fmt.Println(slice)

小结

1、通过make方式可以指定切片的长度和容量
2、如果没有给切片的各个元素赋值,就会使用默认值[int,float => 0 ,string => "" ,bool => false]
3、通过make方式创建的切片,默认数组只能由make底层维护,对外不可见,可能通过slice[下标]来访问。

3、方式三

定义一个切片,直接指定一个数组,使用原理类似make方式。
var slice []int = []int{1, 3, 5}
var strSlice []string = []string{"tom", "jack", "mary"}

切片的遍历

func main() {
    var arr [5]int = [...]int{10, 20, 30, 40, 50}
    slice := arr[1:4] //20,30.40
    for i := 0; i < len(slice); i++ {
        fmt.Printf("i=%v v=%v ", i, slice[i])
    }
    fmt.Println()
    for i, v := range slice {
        fmt.Printf("i=%v v=%v \n", i, v)
    }
}

切片的注意事项

1、切片初始化时 var slice = arr[startIndex,endIndex]

说明:从arr数组下标为startIndex,取到下标为endIndex的元素(不含arr[endIndex]
2、切片初始化时,仍然不能越界

    var arr [5]int = [...]int{10, 20, 30, 40, 50}
    // slice := arr[:]    //全部
    slice := arr[1:4]    //20,30.40
    slice2 := slice[1:2] //30
    slice2[0] = 100
    fmt.Println("arr=", arr)
    fmt.Println("slice=", slice)
    fmt.Println("slice2=", slice2)

6、用append内置函数来给切片动态增加

    var slice []int = []int{100, 200, 300}
    fmt.Println("slice=", slice)
    //通过append直接给slice追加具体的元素。元素类型要一致

    slice = append(slice, 400, 500, 600)
    fmt.Println("slice=", slice)
    //切片追加切片
    slice = append(slice, slice...)
    fmt.Println("slice=", slice)

append操作的底层原理分析:
6.1、切片append操作的本质就是对数组扩容
6.2、go底层会创建一下新的数组newArr(按扩容后大小)
6.3、将slice原来包含的元素拷贝到新的数组newArr
6.4、slice重新引用到newArr
6.5、注意newArr是在底层来维护的。程序员不可见。

7、切片的copy(拷贝)

    var slice1 []int = []int{1, 2, 3, 4, 5}
    var slice2 = make([]int, 10)
    copy(slice2, slice1)
    fmt.Println("slice1=", slice1)
    fmt.Println("slice2=", slice2)
    slice1[2] = 100
    fmt.Println("修改后的slice1=", slice1)
    fmt.Println("最后slice2=", slice2)

copy的时候要求数据类型都是切片
按上面代码,slice1slice2的数据空间是独立的,相互不影响。

string 和slice

1、string底层是一个byte数组,因此string也可以进行切片处理。

    str := "hello@atguigu"
    //使用切片来获取数组
    slice := str[6:]
    fmt.Println("slice=", slice)

2、string和切片在内存的形式,以'abcd'画出内存示意图


3、string是不可变的,也就说不能通过str[0] = 'z'方式来修改字符串

4、如果需要修改字符串,可以先将string -> []byte /或者 []rune -> 修改 -> 重写转成string

    str := "hello@atguigu"
    //将字符串转为byte数组
    arr1 := []byte(str)
    arr1[0] = 'z'
    //将切片转为字符串
    str = string(arr1)
    fmt.Println("str = ", str)

    //byte类型只能处理英文和数字,不能处理中文
    //原因是英文和数字占一个字节,而中文会占用3个字节
    //可以通过[]rune 来处理
    arr2 := []rune(str)
    arr2[0] = '北'
    str = string(arr2)
    fmt.Println("str = ", str)

byte类型只能处理英文和数字,不能处理中文
原因是英文和数字占一个字节,而中文会占用3个字节
可以通过[]rune来处理

切片练习

arr[0] = 1; arr[1] = 1; arr[2] = 2; arr[3] = 3; arr[4] = 5 ; arr[5] = 8

package main

import (
    "fmt"
)

func fbn(n int) []uint64 {
    //声明一个切片 大小是 n
    fbnSlice := make([]uint64, n)
    fbnSlice[0] = 1
    fbnSlice[1] = 1
    //for循环来存放 斐波那契数
    for i := 2; i < n; i++ {
        fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
    }
    return fbnSlice
}

func main() {
    //思数:
    //1、定义一个大小n的切片
    //2、下标为0和1的切片元素值都为1
    //3、剩下for循环 return 回切片
    fbnSlice := fbn(30)
    fmt.Println("fbnSlice=", fbnSlice)
}
上一篇 下一篇

猜你喜欢

热点阅读