Go
1.1 数组的定义
var 数组名 [数组长度] 数据类型
[5]int[10]int
package main
import "fmt"
func main() {
var a [3]int
var b [4]int
fmt.Printf("a: %T b: %T\n", a, b) // a: [3]int b: [4]int
}
1.2 数组的初始化
数组的初始化也有很多方式
方式一
初始化数组时可以使用初始化列表来设置数组元素的值
var 数组名 = [数组长度]类型{初始化列表}
示例
package main
import "fmt"
func main() {
var a [3]int // 默认使用类型的零值初始化
var b = [4]int{1,2,3,4} // 使用1,2,3,4初始化数组
var cityArray = [3]string{"北京", "上海", "深圳"}
fmt.Println(a, b, cityArray) // [0 0 0] [1 2 3 4] [北京 上海 深圳]
a = [3]int{5, 6, 7}
fmt.Println(a) // [5 6 7]
}
方式二
根据初始值的个数自行推断数组的长度
var 数组名 = [...]类型{初始化列表}
示例
package main
import "fmt"
func main() {
var a [3]int // 默认使用类型的零值初始化
var b = [...]int{1,2,3,4} // 使用1,2,3,4初始化数组
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(a, b, cityArray) // [0 0 0] [1 2 3 4] [北京 上海 深圳]
a = [...]int{5, 6, 7}
fmt.Println(a) // [5 6 7]
}
方式三
指定索引值的方式来初始化数组
var 数组名 = [数组长度]类型{index: value}
示例
package main
import "fmt"
func main() {
var a [3]int // 默认使用类型的零值初始化
a = [3]int{0: 5, 2:7} // 指定索引初始化
fmt.Println(a) // [5 0 7]
}
1.3 数组的遍历
索引遍历
package main
import "fmt"
func main() {
cityArray := [...]string{"北京", "上海", "深圳"}
for i := 0; i < len(cityArray); i++ {
city := cityArray[i]
fmt.Println(city)
}
}
range遍历
package main
import (
"fmt"
)
func main() {
cityArray := [...]string{"北京", "上海", "深圳"}
for _, v := range cityArray {
fmt.Println(v)
}
}
1.4 多维数组
Go
1.4.1 二维数组声明
var 数组名 [外层长度][内层长度]数据类型
示例
package main
import (
"fmt"
)
func main() {
var a [3][2] int // 定义一个三行两列的二维数组. 一维数组中保持了三个只有2个元素一维数组
fmt.Println(a) // [[0 0] [0 0] [0 0]]
}
1.4.2 二维数组的初始化
方式一
使用初始化列表来设置数组元素的值
var 数组名 = [外层长度][内层长度]数据类型{数据列表}
示例
package main
import (
"fmt"
)
func main() {
var a [3][2] int // 定义一个三行两列的二维数组. 一维数组中保持了三个只有2个元素一维数组
fmt.Println(a) // [[0 0] [0 0] [0 0]]
a = [3][2]int{{1,2},{3,4},{5,6}}
fmt.Println(a) // [[1 2] [3 4] [5 6]]
}
方式二
根据初始值的个数自行推断数组的长度
var 数组名 = [...][内层长度]数据类型{数据列表}
- 仅支持外层自动推算元素个数
示例
package main
import (
"fmt"
)
func main() {
var a [3][2] int // 定义一个三行两列的二维数组. 一维数组中保持了三个只有2个元素一维数组
fmt.Println(a) // [[0 0] [0 0] [0 0]]
a = [...][2]int{{2,3},{4,5},{6,7}} // 仅支持外层自动推算元素个数,不支持内层自动推算元素个数
fmt.Println(a) // [[2 3] [4 5] [6 7]]
}
方式三
指定索引值的方式来初始化数组
var 数组名 = [外层长度][内层长度]数据类型{index: {内存数据列表}}
示例
package main
import (
"fmt"
)
func main() {
var a [3][2] int // 定义一个三行两列的二维数组. 一维数组中保持了三个只有2个元素一维数组
fmt.Println(a) // [[0 0] [0 0] [0 0]]
a = [3][2]int{0: {1,2}, 2: {5,6}}
fmt.Println(a) // [[1 2] [0 0] [5 6]]
}
1.4.3 二维数组遍历
package main
import (
"fmt"
)
func main() {
var a [3][2] int // 定义一个三行两列的二维数组. 一维数组中保持了三个只有2个元素一维数组
fmt.Println(a) // [[0 0] [0 0] [0 0]]
a = [3][2]int{{1,2},{3,4},{5,6}}
for i:=0; i < 3;i++ {
for j:=0; j < 2; j++ {
fmt.Println(a[i][j])
}
}
for _, v1 := range a {
for _, v := range v1{
fmt.Println(v)
}
}
}
1.5 数组是值类型
数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,
不会改变本身的值
package main
import (
"fmt"
)
func main() {
b := [...]int{1, 2, 3}
b1 := b
b1[0]=3
fmt.Println("b:", b) // b: [1 2 3]
fmt.Println("b1:", b1) // b1: [3 2 3]
}
注意
==!=[n]*T*[n]T
数组练习
package main
import "fmt"
func main() {
a1 := [...]int {1, 3, 5, 7, 8}
sum := 0
for _, v := range a1 {
sum += v
}
fmt.Printf("sum = %d\n", sum)
for i, v1 := range a1 {
for j:=i+1; j < len(a1); j++{
if v1 + a1[j] == 8{
fmt.Printf("(%d, %d)", i, j) // (0, 3)(1, 2)
}
}
}
}
二 切片(slice)
因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性
package main
func arraySum(x [3]int) int{
sum := 0
for _, v := range x{
sum = sum + v
}
return sum
}
x[3]int
另外
a := [3]int{1, 2, 3}
a
为此,go语言引入了切片来解决这些问题。
切片(Slice)是一个拥有相同类型元素的可变长度的序列。
它是基于数组类型做的一层封装。它非常灵活,支持自动扩容
地址长度容量
2.1 声明切片
声明切片类型的基本语法如下:
var 切片名 [] 数据类型
2.2 切片初始化
var 切片名 = []数据类型{初始化列表}
示例
package main
import "fmt"
func main() {
var slice = []int{1,2,3,4}
fmt.Println(slice) // [1 2 3 4]
var slice2 = []string{"北京", "上海", "沙河"}
fmt.Println(slice2) // [北京 上海 沙河]
}
2.3 引用类型
nil
golangnilpointerchannelfuncinterfacemapslicec
package main
import "fmt"
func main() {
var s1 [] int // 声明一个切片
fmt.Println(s1 == nil) // true
// 在golang中nil代表了pointer, channel, Function, interface, map 或者 slice 的零值,类似与c语言中的空指针.
var s2 = []int{1,2,3,4}
fmt.Println(s2 == nil) //false
}
2.4 切片长度和容量
切片拥有自己的长度和容量,
- 长度: 切片保存的数据的个数
- 容量: 底层数组的长度 - 切片第一个元素在底层数组中的位置
len()cap()
package main
import "fmt"
func main() {
var s1 [] int
fmt.Println(len(s1), cap(s1)) // 0 0
var s2 = []int{1,2,3,4}
fmt.Println(len(s2), cap(s2)) // 4 4
}
2.5 切片表达式
字符串数组指向数组或切片的指针lowhighlowhigh
简单切片表达式
切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片
lowhigha1<=索引值<4s长度=high-low
从数组得到切片
数组名[low:high]
示例
package main
import "fmt"
func main() {
// 由数组得到切片
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s1 := array[0:8]
fmt.Printf("s1:%v,type(s1): %T, len(s1): %d, cap(s1): %d", s1, s1, len(s1), cap(s1))// s1:[1 2 3 4 5 6 7 8],type(s1): []int, len(s1): 8, cap(s1): 10
}
low0high
package main
import "fmt"
func main() {
// 由数组得到切片
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s1 := array[0:8]
fmt.Printf("s1:%v,type(s1): %T, len(s1): %d, cap(s1): %d\n", s1, s1, len(s1), cap(s1))// s1:[1 2 3 4 5 6 7 8],type(s1): []int, len(s1): 8, cap(s1): 10
s2 := array[:5]
s3 := array[5:]
s4 := array[:]
fmt.Println(s2) // [1 2 3 4 5]
fmt.Println(s3) // [1 2 3 4 5]
fmt.Println(s4) // [1 2 3 4 5 6 7 8 9 10]
}
0 <= low <= high <= len(a)
从切片得到切片
切片名[low:high]
highcap(a)len(a)intlowhighlow <= highpanic
示例
package main
import "fmt"
func main() {
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
slice := array[:5]
s1 := slice[3: 5] // s1的容量是底层数组array的长度减去s1的
fmt.Printf("s1: %v, cap(s1): %d\n", s1, cap(s1)) // s1: [4 5], cap(s1): 7
slice = array[5:]
//s2 := slice[:6] // out of range
s2 := slice[:5]
fmt.Printf("s2: %v, cap(s2): %d", s2, cap(s2)) // s2: [6 7 8 9 10], cap(s2): 5
}
完整切片表达式
数组指向数组的指针切片
变量名[low:high:max]
变量名[low: high]max-low0
示例
package main
import "fmt"
func main() {
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s1 := array[:5:6]
fmt.Printf("s1: %v, cap(s1): %d\n", s1, cap(s1)) // s1: [1 2 3 4 5], cap(s1): 6
s2 := array[4:6:10]
fmt.Printf("s2: %v, cap(s2): %d", s2, cap(s2)) // s2: [5 6], cap(s2): 6
}
0 <= low <= high <= max <= cap(a)
2.6 make函数构造切片
如果需要动态的创建一个切片,我们就需要使用内置的make()函数,格式如下
make([]T, size, cap)
Tsizecapsize
示例
package main
import "fmt"
func main() {
slice := make([]int, 10, 20)
fmt.Printf("slice: %v, len(slice): %d, cap(slice):%d\n", slice, len(slice), cap(slice)) // slice: [0 0 0 0 0 0 0 0 0 0], len(slice): 10, cap(slice):20
}
slice2010len(slice)cap(slice)
2.7 切片本质
底层数组的指针切片的长度(len)切片的容量(cap)
a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}s1 := a[:5]s2 := a[3:6]
切片不能直接比较
==nilnilnil00nil
nilnil00nil
package main
import "fmt"
func main() {
var s1 []int //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
fmt.Printf("len(s1): %d, cap(s1): %d, s1 is nil: %t\n", len(s1), cap(s1), s1 == nil) // len(s1): 0, cap(s1): 0, s1 is nil: true
fmt.Printf("len(s2): %d, cap(s2): %d, s2 is nil: %t\n", len(s2), cap(s2), s2 == nil) // len(s2): 0, cap(s2): 0, s2 is nil: false
fmt.Printf("len(s3): %d, cap(s3): %d, s3 is nil: %t\n", len(s3), cap(s3), s3 == nil) // len(s3): 0, cap(s3): 0, s3 is nil: false
}
len(s) == 0s == nil
2.8 再谈引用类型
切片是一个引用类型数据,赋值和参数传递不会完整复制底层数据,会共用同一个底层数组
package main
import "fmt"
func main() {
s1 := make([]int, 3) //[0 0 0]
s2 := s1 // 将s1直接赋值给s2,s1和s2共用一个底层数组
s2[0] = 100
fmt.Println(s1) // [100 0 0]
fmt.Println(s2) // [100 0 0]
}
切片遍历
切片的遍历方式和数组是一致的,支持索引遍历和for range遍历
package main
import "fmt"
func main() {
s := []int{1, 3, 5}
for i := 0; i < len(s); i++ {
fmt.Println(i, s[i])
}
for index, value := range s {
fmt.Println(index, value)
}
}
2.9 append函数
append
Goappend()...
package main
import "fmt"
func main() {
var s []int
s = append(s, 1) // 添加一个元素
fmt.Printf("s: %v, len(s): %d, cap(s): %d\n", s, len(s), cap(s))
s = append(s, 2, 3, 4) // 添加三个元素
fmt.Printf("s: %v, len(s): %d, cap(s): %d\n", s, len(s), cap(s))
s2 := []int{5, 6, 7}
s = append(s, s2...) // 添加另一个切片的元素
fmt.Printf("s: %v, len(s): %d, cap(s): %d\n", s, len(s), cap(s))
}
varappend()append()append
切片自动扩容扩容
package main
import "fmt"
func main() {
var numSlice []int
for i := 0; i < 10; i++ {
numSlice = append(numSlice, i)
fmt.Printf("%v len:%d cap:%d ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0jZMbBRv-1619921849366)(.img/切片扩容.png)]
append()numSlice1,2,4,8,162
扩容策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-02U7LCIq-1619921849370)(.img/切片扩容源码.png)]
cap2old.capnewcapcap1024newcapold.capnewcap=doublecap1024newcapold.cap1/4newcap=old.capfor {newcap += newcap/4}newcapcapnewcap >= capcapcapcap
intstring
2.10 copy复制
copy
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
b := a
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(b) //[1 2 3 4 5]
b[0] = 1000
fmt.Println(a) //[1000 2 3 4 5]
fmt.Println(b) //[1000 2 3 4 5]
}
ababab
Gocopy()copy()
copy(destSlice, srcSlice []T)
srcSlicedestSlice
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
c := make([]int, 5, 5)
copy(c, a) //使用copy()函数将切片a中的元素复制到切片c
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1 2 3 4 5]
c[0] = 1000
fmt.Println(a) //[1 2 3 4 5]
fmt.Println(c) //[1000 2 3 4 5]
}
2.11 删除切片中的元素
Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素
a = append(a[:index], a[index+1:]...)
aindex
package main
import "fmt"
func main() {
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
fmt.Println(a) // [30 31 32 33 34 35 36 37]
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) // [30 31 33 34 35 36 37]
}
再谈切片本质
package main
import "fmt"
func main() {
a := [...]int{30, 31, 32, 33, 34, 35, 36, 37}
s := a[:]
fmt.Printf("%p ", s) // 0xc0000c4040
fmt.Println(s, len(s), cap(s)) // [30 31 32 33 34 35 36 37] 8 8
s = append(s[:2], s[3:]...)
fmt.Printf("%p ", s) // 0xc0000c4040
fmt.Println(s, len(s), cap(s)) // [30 31 33 34 35 36 37] 7 8
fmt.Println(a) // [30 31 33 34 35 36 37 37]
}
指针、长度、容量
练习
package main
import "fmt"
func main() {
var a = make([]int, 5, 10)
for i := 0; i < 10; i++ {
a = append(a, i)
}
fmt.Println(a) // [0 0 0 0 0 0 1 2 3 4 5 6 7 8 9]
}
排序
a := []int{3,1,7,5,0}
sort.Ints(a)
fmt.Println(a) // [0 1 3 5 7]
三 map
maphash
map的声明与使用
Gomap
var map变量 map[KeyType]ValueType
KeyTypeValueType
mapnilmake()map
map变量 = make(map[KeyType]ValueType, [cap])
capmapmap
获取map中保存的值
value, bool_value := map变量[key]
keymapvaluekeymapbool_valuekeytruefalse
map示例
package main
import "fmt"
func main() {
var m map[string]int
m = make(map[string]int, 2)
m["age"] = 18
m["money"] = 200
fmt.Printf("%#v\n", m) // map[string]int{"age":18, "money":200}
value, exists := m["hello"]
if exists {
fmt.Println(value, exists) // value默认为对应类型的零值
} else {
fmt.Println("该key不在map中")
}
}
map的遍历
mapfor range
package main
import "fmt"
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
// 遍历key-value
for k, v := range scoreMap {
fmt.Println(k, v)
}
// 遍历key
for k := range scoreMap {
fmt.Println(k)
}
}
删除键值
delete()map
delete(map, key)
mapmapkeykey
package main
import "fmt"
func main() {
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
delete(scoreMap, "小明")
for k, v := range scoreMap {
fmt.Println(k, v)
}
delete(scoreMap, "hello") // hello不存在,delete不操作
}
- key不存在,delete函数不做任何操作
map的有序遍历
go
元素为map类型的切片
切片中的元素为map类型,定义格式
var slice [] map[keyType]valueType
- 该方式声明了一个元素为map的切片
通过初始化列表初始化
slice = []map[keyType]valueType{{key: value}, {key: value}}
通过make函数初始化
slice = make([] map[keyType]valueType, len, cap) // 初始化切片
slice[i] = make(map[keyType]valueType, cap) // 初始化map
示例
package main
import "fmt"
func main() {
var slice1 []map[string]int
slice1 = []map[string]int{{"dyp": 19}, {"dyy": 12}} // 初始化列表初始化
fmt.Println(slice1)
var slice2 []map[string]int
slice2 = make([]map[string]int, 3) // 初始化切片
slice2[0] = make(map[string]int, 3) // 初始化map
slice2[0]["dyp"] = 19
slice2[0]["dyy"] = 12
fmt.Println(slice2)
}
元素为切片的map
mapkeykeyvalue
map中值为切片类型声明
var sliceMap1 map[keyType][]Type
keyTypemap[]Typevalue
通过初始化列表初始化
sliceMap1 = map[keyType][]Type{"key": {value1, value2, ..., value_n}}
通过make初始化
sliceMap1 = make(map[keyType][]Type, cap) // 先初始化map
sliceMap1["key"] = make([]Type, len, cap) // 在初始化切片
示例
package main
import "fmt"
func main() {
var sliceMap1 map[string][]int
sliceMap1 = map[string][]int{"北京": {1, 2, 3}, "上海": {4, 5, 6}} // 初始化列表初始化
fmt.Println(sliceMap1)
var sliceMap2 map[string][]int
sliceMap2 = make(map[string][]int, 3) // 先初始化map
sliceMap2["北京"] = make([]int, 0, 3) // 在初始化切片
sliceMap2["北京"] = append(sliceMap2["北京"], 1, 2, 3) // 向切片添加值
fmt.Println(sliceMap2)
}