2022-4-27 实习Day25
1、bugFix4-26测试问题全部检验测试解决完毕。–3小时 100%
2、进行交易时,杭州银行、总行目标账户数量太少,手动添加。–3小时 100%
Golang编程学习(part 20)
1、切片的基本介绍
① 切片的英文是slice
② 切片是数组的一个引用, 因此切片是引用类型, 在进行传递时, 遵守引用传递的机制
③ 切片的使用和数组类似, 遍历切片、访问切片的元素和求切片长度 len(slice) 都一样
④ 切片 的长度是可以变化的, 因此切片是一个可以动态变化的数组
⑤ 切片定义的基本语法:var 切片名 []类型 var a []int
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main import "fmt" func main() { array := [...]int{1, 22, 33, 66, 99}
slice := array[1:3]
fmt.Println("array =", array) fmt.Printf("slice 的元素是=%v,类型是%T\n", slice, slice) fmt.Println("slice 的元素个数 =", len(slice)) fmt.Println("slice 的容量 =", cap(slice))
}
array = [1 22 33 66 99] slice 的元素是=[22 33],类型是[]int slice 的元素个数 = 2 slice 的容量 = 4
|
2、切片在内存中的形式(重要!!!)
1 2 3 4 5 6 7 8
| slice := array[1:3] --> [ 0x23545 | 2 | 4 ] ptr len cap | \|/ | 数组: array --> [ 1 | 22 | 33 | 66 | 99 ] 0x23545
|
对上图的分析总结
① slice的确是一个引用类型
② slice从底层来说, 其实就是一个数据结构(struct 结构体)
1 2 3 4 5 6 7
| type slice struct{ ptr *[2]int len int cap int }
slice可以理解为一个对象, 有三个属性, ptr存的是第一个元素的地址, len存长度, cap存切片的最大容量
|
3、切片的使用
① 第一种方式:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这样的
1 2 3 4 5 6
| slice := array[1:3]
fmt.Println("array =", array) fmt.Printf("slice 的元素是=%v,类型是%T\n", slice, slice) fmt.Println("slice 的元素个数 =", len(slice)) fmt.Println("slice 的容量 =", cap(slice))
|
② 第二种方式:通过make来创建切片
基本语法:var 切片名 []type = make([]type, len, cap)
参数说明:type:就是数据类型 len:实际大小 cap:指定切片容量【可选,如果写了要cap>len】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| package main
import "fmt"
func main() { var slice []float64 = make([]float64, 5, 10) slice[1] = 10 slice[3] = 20 fmt.Println(slice) fmt.Println("slice的size=", len(slice)) fmt.Println("slice的cap=", cap(slice)) fmt.Printf("slice的地址=%p\n\n", &slice[0])
slice = append(slice, 100) fmt.Println(slice) fmt.Println("slice的size=", len(slice)) fmt.Println("slice的cap=", cap(slice)) fmt.Printf("slice的地址=%p\n\n", &slice[0])
slice = append(slice, 200) slice = append(slice, 300) slice = append(slice, 400) fmt.Println(slice) fmt.Println("slice的size=", len(slice)) fmt.Println("slice的cap=", cap(slice)) fmt.Printf("slice的地址=%p\n\n", &slice[0])
slice = append(slice, 500) fmt.Println(slice) fmt.Println("slice的size=", len(slice)) fmt.Println("slice的cap=", cap(slice)) fmt.Printf("slice的地址=%p\n\n", &slice[0])
slice = append(slice, 600) fmt.Println(slice) fmt.Println("slice的size=", len(slice)) fmt.Println("slice的cap=", cap(slice)) fmt.Printf("slice的地址=%p\n\n", &slice[0]) }
[0 10 0 20 0] slice的size= 5 slice的cap= 10 slice的地址=0xc0000b00f0
[0 10 0 20 0 100] slice的size= 6 slice的cap= 10 slice的地址=0xc0000b00f0
[0 10 0 20 0 100 200 300 400] slice的size= 9 slice的cap= 10 slice的地址=0xc0000b00f0
[0 10 0 20 0 100 200 300 400 500] slice的size= 10 slice的cap= 10 slice的地址=0xc0000b00f0
[0 10 0 20 0 100 200 300 400 500 600] slice的size= 11 slice的cap= 20 slice的地址=0xc0000d8000
|
1 2 3 4 5 6 7 8 9 10 11
| 对上述代码详解 1、slice可以理解为一个结构体,里面有三个变量【结构体是值类型,赋值给其他变量会拷贝一份】 ptr len cap slice --> [ 0x2145 | 5 | 10 ] | | \|/ [ 0 | 10 | 0 | 20 |...] 2、slice结构体中的ptr指向了一个数组, 通过slice中的属性就可以控制这种数组
3、append()多次使用后一旦超过了cap容量大小,数组就会扩容(会发生数组中值的拷贝)
|
对上面代码小结
1)通过make方式创建切片可以指定切片的大小和容量
2) 如果没有给切片的各个元素赋值, 那么就会使用默认值 int, float =>0 string=>”” bool=>false
3)通过make方式创建的切片对应的数组是由make底层维护, 对外不可见, 即只能通过slice去访问各个元素
③ 第三种方式:定义一个切片, 直接就指定具体数组, 使用原理类似 make 方式
1 2 3 4 5 6 7 8 9 10 11
| var slice = []string{"tom", "jack", "mary"}
fmt.Println("slice=", slice) fmt.Println("slice size=", len(slice)) fmt.Println("slice cap=", cap(slice))
slice= [tom jack mary] slice size= 3 slice cap= 3
|
④ 方式1和2的区别(面试)
方式1是直接引用数组, 这个数组是事先存在的, 程序员是可见的
方式2是通过make来创建切片, make也会创建一个数组, 是由切片在底层进行维护, 程序员是看不见的。
4、切片的遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var arr = [...]int{10, 20, 30, 40, 50} slice := arr[1:4] for i:=0; i<len(slice); i++{ fmt.Printf("slice[%v]=%v", i, slice[i]) }
slice[0]=20 slice[1]=30 slice[2]=40
for i,v := range slice { fmt.Printf("i=%v v=%v \n", i, v) }
i=0 v=20 i=1 v=30 i=2 v=40
|
5、切片使用的注意事项和细节讨论
① 切片初始化时 var slice = arr[startIndex : endIndex]
说明:从arr数组下标为startIndex, 取到下标为endIndex的元素(不含 arr[endIndex])
② 切片初始化时,仍然不能越界。范围在 [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[ : ]
③ cap是一个内置函数, 用于统计切片的容量, 即最大可以存放多少个元素
④ 切片定义完后, 还不能使用, 因为本身是一个空的, 需要让其引用到一个数组, 或者make一个空间供切片来使用
⑤ 切片可以继续切片
1 2 3 4 5 6 7 8 9 10 11 12 13
| var arr = [...]int{10, 20, 30, 40, 50} slice := arr[1:4] slice2 := slice[1 : 2] fmt.Printf("%p\n", &slice[0]) fmt.Println(cap(slice)) fmt.Printf("%p\n", &slice2[0]) fmt.Println(cap(slice2))
0xc00000a3f8 4 0xc00000a400 3
|
⑥ 使用append内置函数, 可以对切片进行动态追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import "fmt"
func main() { var slice = []int{100, 200, 300} fmt.Println(len(slice), cap(slice)) fmt.Printf("切片地址=%p 切片第一个元素地址=%p \n", &slice, &slice[0])
slice = append(slice, 400, 500, 600) fmt.Printf("切片地址=%p 切片第一个元素地址=%p \n", &slice, &slice[0])
slice = append(slice, slice...) fmt.Printf("切片地址=%p 切片第一个元素地址=%p \n", &slice, &slice[0]) }
3 3 切片地址=0xc000004078 切片第一个元素地址=0xc00000c150 切片地址=0xc000004078 切片第一个元素地址=0xc00000a3f0 切片地址=0xc000004078 切片第一个元素地址=0xc000070120
|
⑦ 切片 append 操作的底层原理分析
1)切片 append操作的本质就是对数组扩容(只有在切片容量不够用时才扩容)
2)go底层会创建一下新的数组 newArr(扩容后大小)
3)将slice原来包含的元素拷贝到新的数组newArr,slice重新引用到newArr
4)注意newArr是在底层来维护的,程序员不可见
⑧ 切片的拷贝操作:切片使用copy内置函数完成拷贝,举例:
1 2 3 4 5 6 7 8 9 10 11
| var slice = []int{1, 2, 3, 4, 5, 6} var slice1 = make([]int, 10) copy(slice1, slice) fmt.Println("slice=", slice) fmt.Println("slice1=", slice1)
slice= [1 2 3 4 5 6] slice1= [1 2 3 4 5 6 0 0 0 0]
|
⑨ 分析下面代码
1 2 3 4 5
| var a []int = []int{1, 2, 3, 4, 5} var slice = make([]int, 1) fmt.Println(slice) copy(slice, a) fmt.Println(slice)
|