Go:数组,切片和字典
目录:
数组Array
- 定义数组的格式:
var <varName>[n]<type>, n>=0
- 数组的长度也是类型的一部分
- 注意区分指向数组的指针和指针数组
- 数组在Go中为值类型
- 数组之间可以使用
==
或!=
进行比较,但是不能用<
和>
- 可以使用new来创建数组,此方法返回一个指向数组的指针
- Go支持多维数组
定义数组
定义数组的格式:var <varName>[n]<type>, n>=0
数组零值
package main
import (
"fmt"
)
func main() {
a := [2]int{}
fmt.Println(a)
}
或
package main
import (
"fmt"
)
func main() {
a := [2]int{1, }
fmt.Println(a)
}
第二个打印结果为[1 0]
,定义了数组,如果没有赋值,但是没有赋值的为零值,对于int类型的零值为0,对于string就是空字符串
为数组特定元素赋值
给第20个元素赋值为1
package main
import (
"fmt"
)
func main() {
a := [20]int{19: 1}
fmt.Println(a)
}
输出结果为[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
不指定数组长度
可以不指定个数
package main
import (
"fmt"
)
func main() {
a := [...]int{1, 2, 3, 4, 5}
fmt.Println(a)
}
会根据创建数组的数量进行创建
package main
import (
"fmt"
)
func main() {
a := [...]int{19: 1}
fmt.Println(a)
}
这样自动创建一个长度为20的数组
使用new创建数组
package main
import (
"fmt"
)
func main() {
a := new([10]int)
fmt.Println(a)
}
返回了&[0 0 0 0 0 0 0 0 0 0]
,返回了一个数组指针
指向数组的指针和指针数组
指向数组的指针
和别的语言不同,数组是一个值类型,而在其他语言中是引用类型
在传递到方法或者函数的时候,会将整个数组进行拷贝而不是传递一个地址给方法或者函数,如果想使用引用类型的数组,可以通过slice实现。
package main
import (
"fmt"
)
func main() {
a := [...]*int{19: 1}
var p *[100]int = &a
fmt.Println(p)
}
这样取到的p是指向a的数组的指针
指针数组
package main
import (
"fmt"
)
func main() {
x, y := 1, 2
a := [...]*int{&x, &y}
fmt.Println(a)
}
执行会打印[0xc420014098 0xc4200140b0]
,数组内对应元素的内存地址
数组比较
数组的比较要求数组长度和数组内值都一样为Ture
package main
import (
"fmt"
)
func main() {
a := [2]int{1, 2}
b := [2]int{1, 3}
fmt.Println(a == b)
}
数组的赋值
package main
import (
"fmt"
)
func main() {
a := new([10]int)
a[1] = 2
fmt.Println(a)
a := [10]int{}
a[1] = 2
fmt.Println(a)
}
多维数组
package main
import (
"fmt"
)
func main() {
a := [2][3]int{
{1, 2, 3},
{2, 3, 4}}
fmt.Println(a)
}
输出结果
[[1 2 3] [2 3 4]]
通过数组进行冒泡排序
package main
import (
"fmt"
)
func main() {
a := [...]int{88, 9, 26, 67, 56}
num := len(a)
for i := 0; i < num; i++ {
for j := i + 1; j < num; j++ {
if a[i] < a[j] {
temp := a[i]
a[i] = a[j]
a[j] = temp
}
}
}
fmt.Println
}
输出结果
[88 67 56 26 9]
Slice
- 本身不是数组,它指向底层的数组
- 作为变长数组的替代方案,可以关联底层数组的局部或全部
- 是引用类型
- 可以直接创建或者从底层数组获取生成
- 使用len()获取元素个数,cap()获取容量
- 一般使用make关键字创建
- 如果多个slice指向相同的底部数组,其中一个的值改变会影响全部
- make([]T, len, cap)
- 其中cap关键字可以省略,和len的值相同
- len表示存数的元素个数,cap表示容量
从数组获取Slice
package main
import (
"fmt"
)
func main() {
a := [10]int{}
fmt.Println(a)
s := a[5:10] //a[5, 6, 7, 8, 9]
fmt.Println(s)
}
这里a[5:10]表示从第六个索引开始,到第10个为止,可以理解为一个前开后闭的区间
[root@why 16:21:15 go_code]#go run slice_test1.go
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0]
这里创建的时候默认会赋予零值,int类型的零值就为0
可用获取索引的方式
package main
import (
"fmt"
)
func main() {
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(a)
s1 := a[5:10] //a[5, 6, 7, 8, 9]
s2 := a[5:len(a)] //a[5, 6, 7, 8, 9]
s3 := a[5:] //a[5, 6, 7, 8, 9]
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
}
执行结果
[root@why 16:24:27 go_code]#go run slice_test1.go
[1 2 3 4 5 6 7 8 9 10]
[6 7 8 9 10]
[6 7 8 9 10]
[6 7 8 9 10]
当然,从头取到尾可以用[:]
Slice容量
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 3, 10)
fmt.Println(len(s1), cap(s1))
s2 := make([]int, 3)
fmt.Println(len(s2), cap(s2))
}
执行结果
[root@why 16:28:37 go_code]#go run slice_test2.go
3 10
3 3
如果不指定cap,cap的默认值为创建的slice长度。
Slient的cap
package main
import (
"fmt"
)
func main() {
a := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}
s1 := a[2:5]
fmt.Println(string(s1), cap(s1))
}
执行结果
[root@why 17:02:58 go_code]#go run slice_test3.go
cde 9
容量为9,最大容量就是从切片位置开始,并到内存结尾。
数组是连续的内存,对于超出容量,容量就会翻倍。
Reslice
- Reslice时索引是以被slice的切片为准
- 索引不可超过被slice的切片
- 索引越界不会导致底层数组重新分配,而是引发错误
Append
- 可以在slice尾部追加元素
- 可以将一个slice追加在另一个slice的尾部,如果最终长度未超过追加到slice容量则会返回原始slice,如果超过追加到的slice容量则会重新分配数组并拷贝原始数组
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 3, 6)
fmt.Printf("%p\n", s1)
s1 = append(s1, 1, 2, 3)
fmt.Printf("%v %p\n", s1, s1)
s1 = append(s1, 1, 2, 3)
fmt.Printf("%v %p\n", s1, s1)
}
执行结果
[root@why 23:07:51 go_code]#go run slice_test4.go
0xc42001a0c0
[0 0 0 1 2 3] 0xc42001a0c0
[0 0 0 1 2 3 1 2 3] 0xc4200180c0
检验一下slice是数组的拷贝还是数组的引用
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5, 6}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2)
s1[0] = 9
fmt.Println(s1, s2)
}
执行结果
[root@why 23:13:53 go_code]#go run slice_test4.go
[3 4 5] [2 3]
[9 4 5] [2 9]
这边我修改了s1[0],对于数组a来说就是a[2],因为获取的是底层数组的引用,所以对底层数组进行了修改,获取引用的s2也就修改了
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5, 6}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2)
s2 = append(s2, 1, 1, 1, 1, 1, 1, 1)
s1[0] = 9
fmt.Println(s1, s2)
}
执行结果
[root@why 23:19:55 go_code]#go run slice_test4.go
[3 4 5] [2 3]
[9 4 5] [2 3 1 1 1 1 1 1 1]
这是因为s2在append后超过了底层数组的长度,重新分配的数组
copy
package main
import (
"fmt"
)
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9}
copy(s2, s1)
fmt.Println(s2)
}
执行结果
[root@why 23:27:49 go_code]#go run slice_test5.go
[1 2 3]
是因为s2的数组长度为3,所以只拷贝了三个元素
map
- 类似其他语言中的字典或者hash表,以key-value的方式存储
- key必须是支持==和!=比较运算的类型,不可以是函数,map或者slice
- map查找比线性搜索快,但是比索引访问要慢100倍
- map使用make()创建
- make([keyType]valueType, cap),cap可以省略,这里必须指定类型,就感觉蛋疼
- 超出容量自动扩容,但尽量提供一个合理的初始值
- 使用len()获取元素个数
- 键值对不存在时自动添加,使用delete()删除键值对
- 使用for, range对map和slice进行迭代操作
创建map
package main
import (
"fmt"
)
func main() {
var m1 map[int]string
m1 = map[int]string{}
fmt.Println(m1)
var m2 map[int]string
m1 = make(map[int]string)
fmt.Println(m2)
var m3 map[int]string = make(map[int]string)
fmt.Println(m3)
m4 := make(map[int]string)
fmt.Println(m4)
}
执行结果
[root@why 23:59:49 go_code]#go run map_test1.go
map[]
map[]
map[]
map[]
赋值和删除
package main
import (
"fmt"
)
func main() {
m := make(map[int]string)
m[1] = "why"
a := m[1]
fmt.Println(a)
delete(m, 1)
a = m[1]
fmt.Println(a)
}
执行结果
[root@why 00:10:57 go_code]#go run map_test1.go
why
嵌套的map
package main
import (
"fmt"
)
func main() {
m := make(map[int]map[int]string)
m[1] = make(map[int]string)
m[1][2] = "why"
a := m[1][2]
fmt.Println(a)
}
执行结果
[root@why 00:15:26 go_code]#go run map_test2.go
why
注意需要对map[1]定义才能使用,定义的是map[int]map[int]string
后边的map[int]string
对于零值,可能是真的是零值,也可能是被定义为零值,可以通过接收返回值的第二个参数
package main
import (
"fmt"
)
func main() {
m := make(map[int]map[int]string)
a, ok := m[1][2]
fmt.Println(a, ok)
}
执行结果
[root@why 00:27:10 go_code]#go run map_test2.go
false
嵌套map需要单独初始化,这个在编译的过程中不会被发现的,所以可以对第二个返回值进行一下判断
循环map
package main
import (
"fmt"
)
func main() {
m := make([]map[int]string, 5)
for i := range m {
m[i] = make(map[int]string, 1)
m[i][1] = "why"
fmt.Println(m[i])
}
fmt.Println(m)
}
执行结果
[root@why 00:53:44 go_code]#go run map_test2.go
map[1:why]
map[1:why]
map[1:why]
map[1:why]
map[1:why]
[map[1:why] map[1:why] map[1:why] map[1:why] map[1:why]]
这里要通过key来获取并赋值,如果使用value,value是拷贝的,所以赋值不生效
map无序
package main
import (
"fmt"
)
func main() {
m := map[int]string{1 : "a", 2 : "b", 3 : "c", 4 : "d", 5 : "e"}
s := make([]int, len(m))
i := 0
for k,_ := range m {
s[i] = k
i++
}
fmt.Println(s)
}
执行结果
[root@why 00:59:11 go_code]#go run !$
go run map_test3.go
[1 2 3 4 5]
[root@why 00:59:18 go_code]#go run map_test3.go
[4 5 1 2 3]
[root@why 00:59:19 go_code]#go run map_test3.go
[2 3 4 5 1]
[root@why 00:59:20 go_code]#go run map_test3.go
[3 4 5 1 2]
可以看到每次都是无序的。