本章讲解了3方面的内容
1. 数组
2. 切片
3. map
一、数组
1. 数组的定义方式
var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} var grid [4][5]int fmt.Println(arr1, arr2, arr3) fmt.Println(grid)
输出结果:
[0 0 0 0 0] [1 3 5] [2 4 6 8 10] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
2. 数组遍历的方式:
for i := 0; i < len(arr2); i++ { fmt.Println(arr2[i]) }
for i, v := range arr3 { fmt.Println(i, v) }
rang方式遍历的三种
// 第一种: 只获取数组的下标 for i := range arr3 { fmt.Println(arr3[i]) } // 第二种: 获取数组的下标和值 for i, v := range arr3 { fmt.Println(i, v) } // 第三种: 只获取值 for _, v := range arr3 { fmt.Println(v) }
3. arr[5] 和 arr[3]是不同的类型
func printArray(arr [5]int) { arr[0] = 100 for _, v := range arr { fmt.Println(v) } } func main() { var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} var grid [4][5]int fmt.Println(arr1, arr2, arr3) fmt.Println(grid) printArray(arr1) printArray(arr3) // 下面这个打印会报错, 因为arr2是3个容量的数组 // printArray(arr2) }
这里传递arr1 和arr3过去的时候, 可以正常打印数组,但是传递arr2过去的时候, 会报异常.
cannot use arr2 (type [3]int) as type [5]int in argument to printArray
原因是: [3]int 和[5]int是不同的类型
4. 数组的传递是值拷贝类型.
func printArray(arr [5]int) { arr[0] = 100 for _, v := range arr { fmt.Println(v) } } func main() { //定义数组的三种方法 var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} // 证明数组是值拷贝类型 fmt.Println("证明数组是值拷贝类型") printArray(arr1) printArray(arr3) fmt.Println(arr1, arr3) }
我们在函数printArray中修改数组第一个元素的值是100. 然后打印. 在打印原数组. 结果如下:
证明数组是值拷贝类型 100 0 0 0 0 100 4 6 8 10 [0 0 0 0 0] [2 4 6 8 10]
5. 如何实现数组的地址传递呢? 使用指针
func printArray1(arr *[5]int) { arr[0] = 100 for _, v := range arr { fmt.Println(v) } } func main() { //定义数组的三种方法 var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} // 证明数组是值拷贝类型 fmt.Println("如何让数组实现值拷贝呢") printArray(&arr1) fmt.Println(arr1) }
结果
如何让数组实现地址拷贝呢? 100 0 0 0 0 [100 0 0 0 0]
注意:
a. 在方法printArray中参数接收是一个地址类型. 并且传递参数的时候也传递一个地址类型
b. 在arr[0] = 100赋值的时候, 无需获取地址的值. 直接给指针类型的数组赋值即可
二、切片slice
1. 什么是slice
func main() { // 定义一个数组 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} fmt.Println("arr1[2:6] = ", arr1[2:6]) fmt.Println("arr1[2:] = ", arr1[2:]) fmt.Println("arr1[:6] = ", arr1[:6]) fmt.Println("arr1[:] = ", arr1[:] ) }
结果
arr1[2:6] = [2 3 4 5] arr1[2:] = [2 3 4 5 6 7] arr1[:6] = [0 1 2 3 4 5] arr1[:] = [0 1 2 3 4 5 6 7]
a. 通过arr[a:b]方式获取的值就是slice
b. slice是数组的一个视图. slice不是值传递的. slice内存储的是数组的地址
2. 验证slice不是值传递
func updateSlice(s []int) { s[0] =100 } func main() { // 定义一个数组 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s1 := arr1[2:] s2 := arr1[:] fmt.Println("after updateSlice(s1)") updateSlice(s1) fmt.Println(s1) updateSlice(s2) fmt.Println(s2) fmt.Println(arr1) }
结果:
after updateSlice(s1) s1 = [100 3 4 5 6 7] s2 = [100 1 100 3 4 5 6 7] arr1 = [100 1 100 3 4 5 6 7]
可以看到很有趣的现象. s1 的第一个元素被改100. 同时影响了s2的第3个元素. s2的第一个元素修改后,s1, s2同时都影响了数组arr1.
由此可见: s1, s2 都是指向的数组的地址
将s传递给函数printSlice, 在打印原来的s. 发现s的值变化了. 说明, 切片传递是地址传递, 而不是值传递.
可是上一章讲指针的时候,不是说go中只有值拷贝一种类型么? 那么为什么slice不是值拷贝呢? 因为slice是数组的一个视图. (可以理解为, 他取的是数组中指定元素的地址. 所以, slice不是值拷贝, 他的元素是地址)
思考: 在上面将数组的第5个问题, 数组如何作为一个地址拷贝的方式作为参数传递到方法里面呢? 我们的做法是: 将数组作为一个指针传过去. 学习了slice,我们可以换一种方法.
func printArray1(arr []int) { arr[0] = 100 for _, v := range arr { fmt.Println(v) } } func main() { //定义数组的三种方法 var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} fmt.Println(arr1, arr2, arr3) // 如何让数组实现地址拷贝呢? fmt.Println("如何让数组实现地址拷贝呢?方法2") printArray1(arr3[:]) fmt.Println(arr3) }
结果:
[0 0 0 0 0] [1 3 5] [2 4 6 8 10] 如何让数组实现地址拷贝呢?方法2 100 4 6 8 10 [100 4 6 8 10]
1. printArray1(arr []int)接收参数的时候, 使用arr []int就是切片. 如果是arr [5]int就是数组.
2.如何将一个数组传给接收切片的方法呢? 如下:printArray1(arr3[:])
是不是很巧妙. 这种方法就不用使用& *了
3. reslice
我们对数组可以进行slice操作, 对slice还可以继续进行slice操作, 就是reslice
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func main() { // 定义一个数组 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s2 := arr1[:] fmt.Println("reslice操作") fmt.Println("slice 后: ", s2) s2 = s2[:5] fmt.Println("reslice 后: ", s2) s2 = s2[2:] fmt.Println("再次reslice 后: ", s2) updateSlice(s2) fmt.Println("再次reslice 然后修改第一个元素的值 后: ", s2) fmt.Println("原数组:", arr1) }
输出结果:
reslice操作 slice 后: [0 1 2 3 4 5 6 7] reslice 后: [0 1 2 3 4] 再次reslice 后: [2 3 4] 再次reslice 然后修改第一个元素的值 后: [100 3 4] 原数组: [0 1 100 3 4 5 6 7]
我们看到对slice再次进行slice, 就和对数组slice是类似的. 最终指向的都是数组中的地址
updateSlice(s2): 这个操作就比较有意思了. 我们看到, 对二次reslice后的数组,修改他的第一个元素的值. 然后在打印原数组,发现, 原数组的值也修改了. 再次证明, slice是数组的一个视图,最终存储的是数组的地址. 一旦被修改,会影响原数组.
4. slice扩展
看下面这个例子:
func main() { // 定义一个数组 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s1 := arr1[2:6] s2 := s1[3:5] fmt.Println("s1 = ", s1, ", s2 = ", s2) }
结果:
s1 = [2 3 4 5] , s2 = [5 6]
很奇怪: s1取的数组的4个元素. s2想要取s1的3-5个元素,可s1只有4个元素呀, s2还成功取出来了. 这是怎么回事呢?
为什么会这样呢?我们来分析一下
最开始, 数组中的值是0, 1, 2, 3, 4, 5, 6, 7
当我们取s1切片的时候, s1是底层数组的一个视图, 他取值是从2-6, 虽然,他只取了4个元素,但是因为他是对底层的一个视图,所以他是可以看到底层后面的元素的.
然后取s2切片. 我们发现他去的是s1下标的3-5个元素. 虽然s1只有4个元素, 因为他是可以看到底层其他后面的元素的, 所以, s2能够把数组中第5个元素取出来.
那么取出来对应的元素值时多少呢?对应到底层数组, 他的值就是5和6
那么s1只有4个元素, 为什么能够看到第五和第六个元素呢?
原来slice的底层结构是这么定义的
slice定义了3个对象.
ptr是一个指针, 记录的是切片的第一个地址(下面的方格代表数组),
len是切片中元素的个数, 如果获取超过元素个数的值, 会报下标越界
cap: 是capacity, 容量. 他存储的是从ptr指向的数组的元素开始, 一直到数组结束的元素.
所以,s1[3:5]获取的是cap中的数据. 正常返回.
总结: 1. slice 可以向后扩展, 但不能向前扩展. slice中cap的起始元素是从ptr开始
2. s[i]不可以超越len(s), 向后扩展可以超越len(s),但不可以超过底层数组cap(t)
5. 向slice添加元素
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func main() { // 定义一个数组--为什么这样定义就能够被识别为一个数组呢 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s1 := arr1[2:6] // 2, 3, 4, 5 s2 := s1[3:5] // 4, 5 fmt.Println("s2 = ", s2) fmt.Println("arr1 = ", arr1) s3 := append(s2, 10) // 4, 5, 10 fmt.Println("s3 = ", s3) fmt.Println("arr1 = ", arr1) s4 := append(s3, 11) // 4, 5, 10 ,11 fmt.Println("s4 = ", s4) fmt.Println("arr1 = ", arr1) s5 := append(s4, 12) // 4, 5, 10 ,11, 12 fmt.Println("s5 = ", s5) fmt.Println("arr1 = ", arr1) s6 := s5[2:5] fmt.Println("s6 = ", s6) updateSlice(s6) fmt.Println("arr1 = ", arr1) }
结果
s2 = [5 6] , arr1 = [0 1 2 3 4 5 6 7] s3 = [5 6 10] , arr1 = [0 1 2 3 4 5 6 10] s4 = [5 6 10 11] , arr1 = [0 1 2 3 4 5 6 10] s5 = [5 6 10 11 12] , arr1 = [0 1 2 3 4 5 6 10] s5 = [100 6 10 11 12] , arr1 = [0 1 2 3 4 5 6 10]
添加元素是append. 那么我们来分析下这段代码:
s2的值是5 ,6 原数组:[0 1 2 3 4 5 6 7]
s3在s2基础上增加了元素10 , 我们发现原数组变为:[0 1 2 3 4 5 6 10]. 这说明, append也是对slice的cap进行处理的
s4在s3基础上增加了元素11, 我们发现原数组较上一个没变化:[0 1 2 3 4 5 6 10]. 原因是什么呢?s4 增加的数组已经超过了cap范围, 这时会重新开辟一块新的空间. 因为这块空间已经保存不下这个数据了.
s5的变化同s4, 虽增加了一个元素,但是, 原数组无变化
s5第二次打印可以证明, 数组已经重新开辟了一块新的空间. 因为我对s5的第一个元素值进行了修改, 但修改后数组的值没有变化, 表明此时s5的切片指向的地址已经不是原来数组的地址了.
下面来看看这几个元素的len 和cap是如何变化的
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func main() { // 定义一个数组--为什么这样定义就能够被识别为一个数组呢 arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s1 := arr1[2:6] // 2, 3, 4, 5 s2 := s1[3:5] // 4, 5 fmt.Println("s2 = ", s2, ", arr1 = ", arr1, ", len(s2)", len(s2), ", cap(s2)", cap(s2)) s3 := append(s2, 10) // 4, 5, 10 fmt.Println("s3 = ", s3, ", arr1 = ", arr1, ", len(s3)", len(s3), ", cap(s3)", cap(s3)) s4 := append(s3, 11) // 4, 5, 10 ,11 fmt.Println("s4 = ", s4, ", arr1 = ", arr1, ", len(s4)", len(s4), ", cap(s4)", cap(s4)) s5 := append(s4, 12) // 4, 5, 10 ,11, 12 fmt.Println("s5 = ", s5, ", arr1 = ", arr1, ", len(s5)", len(s5), ", cap(s5)", cap(s5)) updateSlice(s5) fmt.Println("s5 = ", s5, ",arr1 = ", arr1, ", len(s5)", len(s5), ", cap(s5)", cap(s5)) }
打印结果:
s2 = [5 6] , arr1 = [0 1 2 3 4 5 6 7] ,len(s2) 2 ,cap(s2) 3 s3 = [5 6 10] , arr1 = [0 1 2 3 4 5 6 10] ,len(s3) 3 ,cap(s3) 3 s4 = [5 6 10 11] , arr1 = [0 1 2 3 4 5 6 10] ,len(s4) 4 ,cap(s4) 6 s5 = [5 6 10 11 12] , arr1 = [0 1 2 3 4 5 6 10] ,len(s5) 5 ,cap(s5) 6 s5 = [100 6 10 11 12] , arr1 = [0 1 2 3 4 5 6 10] ,len(s5) 5 ,cap(s5) 6
我们打印出了每一次变化后len的值和cap的值. 从s4开始, 增加了一个元素, 原来的地址已经容纳不了这么多数据了, 于是新开辟了一块空间. 元素的len是4, cap是当前数组的2倍. 已经不是原来的数组了
总结:
1. 添加元素时,如果超越capacity, 那么会重新开辟一块更大的底层数组, 把slice切片值copy过去. 原来的数组, 如果有人用就依然存在, 如果没人用就会被垃圾回收掉.
2. 由于是值传递, 所以append必须要有一个返回值接收. 原因是: 当append的容量超过原来数组的时候, 会新开辟一块空间, 新开辟的空间需要有新的参数来接收.
7.slice的copy
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func printSlice(s []int) { fmt.Printf("len=%d, cap=%d ", len(s), cap(s)) } func main() { var sli []int // 这样定义就是一个zero value, 他的值时nil // 给sli赋值. 因为是zero value, 所以,可以直接复制 for i := 0; i < 200 ;i ++ { printSlice(sli) sli = append(sli, 2*i+1) } }
结果
len=0, cap=0 len=1, cap=1 len=2, cap=2 len=3, cap=4 len=4, cap=4 len=5, cap=8 len=6, cap=8 len=7, cap=8 len=8, cap=8 len=9, cap=16 len=10, cap=16 len=11, cap=16 len=12, cap=16 len=13, cap=16 len=14, cap=16 len=15, cap=16 len=16, cap=16 len=17, cap=32 len=18, cap=32 len=19, cap=32 len=20, cap=32 len=21, cap=32 len=22, cap=32 len=23, cap=32 len=24, cap=32 len=25, cap=32 len=26, cap=32 len=27, cap=32 len=28, cap=32 len=29, cap=32 len=30, cap=32 len=31, cap=32 len=32, cap=32 len=33, cap=64 len=34, cap=64 len=35, cap=64 len=36, cap=64 len=37, cap=64 len=38, cap=64 len=39, cap=64 len=40, cap=64 len=41, cap=64 len=42, cap=64 len=43, cap=64 len=44, cap=64 len=45, cap=64 len=46, cap=64 len=47, cap=64 len=48, cap=64 len=49, cap=64 len=50, cap=64 len=51, cap=64 len=52, cap=64 len=53, cap=64 len=54, cap=64 len=55, cap=64 len=56, cap=64 len=57, cap=64 len=58, cap=64 len=59, cap=64 len=60, cap=64 len=61, cap=64 len=62, cap=64 len=63, cap=64 len=64, cap=64 len=65, cap=128 len=66, cap=128 len=67, cap=128 len=68, cap=128 len=69, cap=128 len=70, cap=128 len=71, cap=128 len=72, cap=128 len=73, cap=128 len=74, cap=128 len=75, cap=128 len=76, cap=128 len=77, cap=128 len=78, cap=128 len=79, cap=128 len=80, cap=128 len=81, cap=128 len=82, cap=128 len=83, cap=128 len=84, cap=128 len=85, cap=128 len=86, cap=128 len=87, cap=128 len=88, cap=128 len=89, cap=128 len=90, cap=128 len=91, cap=128 len=92, cap=128 len=93, cap=128 len=94, cap=128 len=95, cap=128 len=96, cap=128 len=97, cap=128 len=98, cap=128 len=99, cap=128 len=100, cap=128 len=101, cap=128 len=102, cap=128 len=103, cap=128 len=104, cap=128 len=105, cap=128 len=106, cap=128 len=107, cap=128 len=108, cap=128 len=109, cap=128 len=110, cap=128 len=111, cap=128 len=112, cap=128 len=113, cap=128 len=114, cap=128 len=115, cap=128 len=116, cap=128 len=117, cap=128 len=118, cap=128 len=119, cap=128 len=120, cap=128 len=121, cap=128 len=122, cap=128 len=123, cap=128 len=124, cap=128 len=125, cap=128 len=126, cap=128 len=127, cap=128 len=128, cap=128 len=129, cap=256 len=130, cap=256 len=131, cap=256 len=132, cap=256 len=133, cap=256 len=134, cap=256 len=135, cap=256 len=136, cap=256 len=137, cap=256 len=138, cap=256 len=139, cap=256 len=140, cap=256 len=141, cap=256 len=142, cap=256 len=143, cap=256 len=144, cap=256 len=145, cap=256 len=146, cap=256 len=147, cap=256 len=148, cap=256 len=149, cap=256 len=150, cap=256 len=151, cap=256 len=152, cap=256 len=153, cap=256 len=154, cap=256 len=155, cap=256 len=156, cap=256 len=157, cap=256 len=158, cap=256 len=159, cap=256 len=160, cap=256 len=161, cap=256 len=162, cap=256 len=163, cap=256 len=164, cap=256 len=165, cap=256 len=166, cap=256 len=167, cap=256 len=168, cap=256 len=169, cap=256 len=170, cap=256 len=171, cap=256 len=172, cap=256 len=173, cap=256 len=174, cap=256 len=175, cap=256 len=176, cap=256 len=177, cap=256 len=178, cap=256 len=179, cap=256 len=180, cap=256 len=181, cap=256 len=182, cap=256 len=183, cap=256 len=184, cap=256 len=185, cap=256 len=186, cap=256 len=187, cap=256 len=188, cap=256 len=189, cap=256 len=190, cap=256 len=191, cap=256 len=192, cap=256 len=193, cap=256 len=194, cap=256 len=195, cap=256 len=196, cap=256 len=197, cap=256 len=198, cap=256 len=199, cap=256 Process finished with exit code 0
从打印结果可以看出. 1) 没有个切片赋初始值, 他的默认长度是0 , 因此给切片append不会报错. 2) 每次cap容量不够的时候, 增长是按照当前元素值的2倍增长的.
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func printSlice(s []int) { fmt.Printf("%v, len=%d, cap=%d ", s, len(s), cap(s)) } func main() { s1 := []int{1, 2, 3, 4} s2 := make([]int, 16) s3 := make([]int, 2) printSlice(s1) printSlice(s2) printSlice(s3) copy(s2, s1) printSlice(s2)
copy(s3, s1)
printSlice(s3)
}
结果:
[1 2 3 4], len=4, cap=4 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16 [0 0], len=2, cap=2 [1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16 [1 2], len=2, cap=2
1) 定义切片可以使用的方法
- var s1 []int. 初始值是nil, len是0 cap是0 ,可直接append
- var s2 = []int{1, 2, 3, 4},有4个元素, len是4, cap是4
- var s3 = make([]int, 16) , 有16个元素,初始值都是0 , len是16, cap是16.
2) copy是将s1 copy给s2.
3) 如果, 目标容量小于原容量, 则只copy目标容量个数的值
8. slice的delete
slice不能直接delete, 也就是没有提供delete方法,那么如果想要删除中间的某一个元素, 怎么办呢?
package main import "fmt" func updateSlice(s []int) { s[0] =100 } func printSlice(s []int) { fmt.Printf("%v, len=%d, cap=%d ", s, len(s), cap(s)) } func main() { s1 := []int{1, 2, 3, 4} s2 := make([]int, 16) s3 := make([]int, 2) printSlice(s1) printSlice(s2) printSlice(s3) copy(s2, s1) printSlice(s2) fmt.Println("删除元素值4") s4 := append(s2[:3], s2[4:]...) printSlice(s4) }
结果
[1 2 3 4], len=4, cap=4 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16 [0 0], len=2, cap=2 [1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16 删除s2的元素值4 [1 2 3 0 0 0 0 0 0 0 0 0 0 0 0], len=15, cap=16
s2的值是[1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16
删除中间的元素4, 怎么操作呢? 使用s4 := append(s2[:3], s2[4:]...)
注意:的一点是append的第二个参数是一个数组,而不是切片, 那么如何将切片转换为数组呢? 在切片后面加三个点...
三、map
map定义的三种方式
// 第一种 m := map[string]string { "aaa":"111", "bbb":"222", "ccc":"333", "ddd":"444", } // 第二种 m1 := make(map[string]int) // 使用make,创建了一个empty map // 第三种 var m2 map[string]int // 使用var, 创建了一个nil. nil和java中的null不同的是:nil可以参与运算.对null对象进行运算报异常 fmt.Println(m, m1, m2)
重点看一下第二种和第三种方式有何不同.
方式二: 创建了一个empty map
方式三: 创建了一个nil 的map
虽然如此,二者都可以参与运算. go中的nil和java中的null不同的是: nil可以参与运算. 对null对象进行运算报异常
二.循环遍历使用range
//循环遍历 for k, v := range m { fmt.Println(k, v) // map是无需的,每次输出可能不一样 }
三.判断key是否存在
// 判断key是否存在 if v, ok := m["aaa"]; ok { fmt.Println(v, ok) } else { fmt.Println(ok) }
v, ok := m["aaa"]用来判断key是否存在
四. 删除一个元素
delete(m, "aaa")