Go:数组,切片和字典

时间:Aug. 2, 2018 分类:

目录:

数组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]

可以看到每次都是无序的。