常用包管理命令
构建包:go build 包名
导入包:import "包名"
如何使用包中定义的函数?
导入需要的包,利用包名进行调用。
包中所有的函数都能被外界调用吗?
- 大写开头的函数能才能被调用。
- 小写开头的函数只能在函数里被调用。
构建包有什么用?
构建包后可以得到同名的可执行文件,可以直接运行这个可执行文件。效果和go run pkg.go一样。
数组的创建以及作为参数传递时的方式
- 可以var、短变量的方式创建
- 值传递的方式,也就是会进行拷贝,大数组拷贝会损失性能
package main
import "fmt"
func main() {
arr1 := [5]int{1, 2, 3, 4, 5} // 短变量初始化方式
var arr2 [5]int = [5]int{6, 7, 8, 9} // 指定元素个数
var arr3 = [...]int{1, 2, 3, 4, 5, 6} // 不指定个数,自己判断元素个数
var arr4 = [...]string{2: "i", 3: "am", 5: "jack"} // 通过冒号指定元素初始化
fmt.Println(arr1, arr2, arr3, arr4)
}
数组类型的缺点
值传递,会进行只拷贝,对于大数组会损失性能 解决办法:使用切片
1. 切片的创建和数组创建的区别
切片的创建不用指定数值大小。 创建方式:
-
var或者
:= -
make方式
-
为什么切片的容量为8?
- 好像是跟append有关系
2. 切片类型如果不初始化,声明后直接使用会怎么样?
如果不初始化,切片变量就没有分配内存空间,容量为0。此时的切片变量等于nil空。
如果不初始化直接使用,会报数组访问越界的panic。
如何避免切片未初始化的风险?
检查切片变量是否为空,为空则进行初始化。 两种方式:
- 直接make初始化。
- 使用append添加元素,会对切片进行扩容初始化
3. 切片的本质
切片底层指向的是一个数组,使用切片其实就是数组的引用。 切片底层维护着三个属性:
- 指针(执行一个数组)
- 长度len
- 容量cap
对切片进行切片化的本质
对切片化得到的不同切片,底层其实指向的是同一个数组,只是指针指向的数组位置不同。 从以下代码中可以看出两个切片其实指向同一个数组。
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3, 4, 5}
fmt.Println(slice1, len(slice1), cap(slice1))
slice2 := slice1[1:3] // 切片化
fmt.Println(slice2, len(slice2), cap(slice2))
slice2[0] = 100 // 更改元素是否会影响到slice1
fmt.Println(slice1, len(slice1), cap(slice1))
fmt.Println(slice2, len(slice2), cap(slice2))
}
/*
[1 2 3 4 5] 5 5
[2 3] 2 4
[1 100 3 4 5] 5 5
[100 3] 2 4
*/
4. 对切片进行动态扩容为什么会改变底层原数组
动态扩容后会发生什么?
- 容量翻倍增长
- 更改底层数组(底层数组替换为新的数组)
package main
import "fmt"
func main() {
var slice1 []int // 定义一个切片,但不初始化
fmt.Println(slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 1)
fmt.Println(slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 2)
fmt.Println(slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 3)
fmt.Println(slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 4)
fmt.Println(slice1, len(slice1), cap(slice1))
slice1 = append(slice1, 5)
fmt.Println(slice1, len(slice1), cap(slice1))
}
/*
[] 0 0
[1] 1 1
[1 2] 2 2
[1 2 3] 3 4
[1 2 3 4] 4 4
[1 2 3 4 5] 5 8
*/
可以看到容量是翻倍扩容的。
package main
import "fmt"
func main() {
var slice1 []int
slice1 = append(slice1, 1)
slice1 = append(slice1, 2)
slice1 = append(slice1, 3)
slice1 = append(slice1, 4)
fmt.Println(slice1, len(slice1), cap(slice1))
slice2 := slice1[1:3]
slice2[0] = 100
fmt.Println(slice1, len(slice1), cap(slice1))
fmt.Println(slice2, len(slice2), cap(slice2))
slice1 = append(slice1, 5) // 此时slice1容量扩充,是否会改变底层数组呢?
slice2[0] = 99
fmt.Println(slice1, len(slice1), cap(slice1))
fmt.Println(slice2, len(slice2), cap(slice2))
}
/*
[1 2 3 4] 4 4
[1 100 3 4] 4 4
[100 3] 2 3
[1 100 3 4 5] 5 8
[99 3] 2 3
*/
可以看到扩容后,改变slice2元素,slice1不受影响,说明底层这两个切片已经不指向同一个数组了。
5. 切片在实际应用中,怎么用更好,为什么?
- 函数参数用切片,而不要用数组。
- 切片其实就是引用传值,不涉及拷贝,性能更好。
- 如果不知道切片长度,初始化时尽量给定一个容量:
make([]int, 0, 20)- 避免频繁扩容影响性能。
- 如果知道切片长度,初始化可以只指定长度:
make([]int, 20)- 指定len后,cap和len相同。
如何初始化一个n×n的多维数组?
make([][]int, n)
arr := make([][]int, n)
for i := 0;i < n;i ++ {
arr[i] = make([]int, n)
}
map类型的变量为什么必须初始化才能使用?
因为不初始化就没有分配内存空间,此时的map变量就为nil空,无法对空变量进行操作。
如何进行初始化?
- make初始化
- 字面量初始化
func main() {
mp1 := make(map[int]string)
mp1[1] = "bob"
mp1[2] = "jack"
mp1[3] = "lisa"
fmt.Println(mp1)
var mp2 = map[int]int{
1: 10,
2: 19,
3: 20,
}
fmt.Println(mp2)
var mp3 map[string]int
// mp3["app"] = 2 // 报错,未初始化的map无法使用
if mp3 == nil {
mp3 = make(map[string]int)
}
fmt.Println(len(mp3))
}
/*
map[1:bob 2:jack 3:lisa]
map[1:10 2:19 3:20]
0
*/
go中的map是无序的
因此每次打印map变量,键值对的顺序可能不一样。
go中的map是引用类型,这意味着什么?
意味着将map作为函数类型传递时,传递的是变量的引用,而不是值拷贝。