Go语言学习(四)| 数组、切片、集合

mervyn 2018年6月30日12:40:37编程语言 GoGo语言学习(四)| 数组、切片、集合已关闭评论61

array

声明数组

array由 [n]<type> 定义,n为array的长度,标示希望存储的内容的类型。例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

var arr [10]int     //声明一个int类型的数组
arr[0] = 42         //数组下标是从0开始的
arr[1] = 13         //赋值操作

数组的索引从 0 开始到 length - 1 结束。数组中的所有元素都被自动赋值为数组类型的零值。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

NOTE 数组的大小是类型的一部分.文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

由于长度也是数组类型的一部分,因此 [3]int 与 [4]int 是不同的类型,数组也就不能改变长度。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

Go 中的数组是值类型而不是引用类型。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。
如果要使用指针,那么就需要用到后面介绍的 slice 类型了。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

初始化数组

a := [3]int{1, 2, 3}

初始化数组中 {} 中的元素个数不能大于 [] 中的数字。
如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

a := [...]int{1, 2, 3}

初始化数组时,可以只初始化部分数组的值,例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

b := [10]int{1, 2, 3} //声明了一个长度为10的int数组,其中前3个元素初始化为1、2、3,其他默认为0。

二维数组

package main

import "fmt"

func main() {
    a := [2][2]int{ [2]int{1, 2}, [2]int{3, 4} }
    fmt.Println(a)

    b := [2][3] int { {1, 2, 3}, {2, 2, 3}}
    fmt.Println(b)  //[[1 2 3] [2 2 3]]

    c := [2][2]int{[...]int{1, 2}, [...]int{3, 4} }
    fmt.Println(c)
}

slice

slice 与 array 接近,但是在新的元素加入的时候可以增加长度.slice 是一个指向 array 的指针,这是其与 array 不同的地方;
slice 是引用类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array.文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

创建切片

基于数组创建

例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main

import "fmt"

func main() {
    //定义一个 5 个元素的 array,序号从 0 到 4
    a := [...]int{1, 2, 3, 4, 5}
    fmt.Println("array a is:", a)

    //从序号 2 至 3 创建 slice,它包含元素 3, 4 , s1 := a[0:6]; 编译时会报错。因为超出了数组范围
    s1 := a[2:4]
    fmt.Println("slice s1 is:", s1)

    //从序号 1 至 4 创建,它包含元素 2, 3, 4, 5
    s2 := a[1:5]
    fmt.Println("slice s2 is:", s2)

    //用 array 中的所有元素创建 slice,这是 a[0:len(a)] 的简化写法
    s3 := a[:]
    fmt.Println("slice s3 is:", s3)

    //从序号 0 至 3 创建,这是 a[0:4] 的简化写法,得到 1, 2, 3, 4
    s4 := a[:4]
    fmt.Println("slice s4 is:", s4)

    //从 slice s2 创建 slice,注意 s5 仍然指向 array a。a的值变了,slice的值也随之改变
    s5 := s2[:]
    fmt.Println("slice s5 is:", s5)

    fmt.Println("-----------------")
    s2[0] = 1
    fmt.Println("array a is:", a)
    fmt.Println("slice s1 is:", s1)
    fmt.Println("slice s2 is:", s2)
    fmt.Println("slice s3 is:", s3)
    fmt.Println("slice s4 is:", s4)
    fmt.Println("slice s5 is:", s5)

}

上述执行输出结果为:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

array a is: [1 2 3 4 5]
slice s1 is: [3 4]
slice s2 is: [2 3 4 5]
slice s3 is: [1 2 3 4 5]
slice s4 is: [1 2 3 4]
slice s5 is: [2 3 4 5]
-----------------
array a is: [1 1 3 4 5]
slice s1 is: [3 4]
slice s2 is: [1 3 4 5]
slice s3 is: [1 1 3 4 5]
slice s4 is: [1 1 3 4]
slice s5 is: [1 3 4 5]

直接创建

  • 创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
  • 创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
  • 直接创建并初始化包含5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}

slice 和 array 在声明时的区别

  • 声明数组时,方括号内写明了数组的长度或使用 ... 自动计算长度
  • 声明 slice 时,方括号内没有任何字符。
  • slice 可以从一个数组或一个已经存在的 slice 中再次声明。slice 通过array[i:j]来获取,其中 i 是数组的开始位置,j 是结束位置,但不包含 array[j] ,它的长度是 j - i .
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

// 声明两个含有byte的slice
var a, b []byte

// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]

// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]

slice的一些简便操作

  • slice 的默认开始位置是 0,ar[:n] 等价于 ar[0:n]
  • slice 的第二个序列默认是数组的长度,ar[n:] 等价于 ar[n:len(ar)]
  • 如果从一个数组里面直接获取 slice,可以这样 ar[:],因为默认第一个序列是 0,第二个是数组的长度,即等价于 ar[0:len(ar)]

操作 slice 几个有用的内置函数

  • len 获取 slice 的长度。
  • cap 获取slice的最大容量。
  • append 向 slice 里面追加一个或者多个元素,然后返回一个和 slice 一样类型的 slice。
  • copy 函数 copy 从源 slice 的 src 中复制元素到目标 dst,并且返回复制的元素的个数。

函数 copy 从源 slice src 复制元素到目标 dst,并且返回复制的元素的个数。源和目标可能重叠。复制的数量是 len(src) 和 len(dst) 中的最小值。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html


append 函数会改变 slice 所引用的数组的内容,从而影响到引用同一数组的其它 slice。 但当 slice 中没有剩余空间(即(cap - len) == 0)时,此时将动态分配新的数组空间。返回的 slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的 slice 则不受影响。文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main
import "fmt"
func main(){
    arr := [10]int{1, 2, 3, 4, 5}
    s1 := arr[0:2]
    fmt.Println(len(s1))    //输出2
    fmt.Println(cap(s1))    //输出10
}

map

声明 map

一般定义 map 的方法是:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

map[<from type>] <to type>

例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

var numbers map[string] int

numbers := make(map[string] int)

当只需要声明一个 map 的时候,使用 make 的形式:monthdays := make(map[string]int)
例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main
import "fmt"
func main(){
    monthdays := map[string]int{
        "Jan": 31, "Feb": 28, "Mar": 31,
        "Apr": 30, "May": 31, "Jun": 30,
        "Jul": 31, "Aug": 31, "Sep": 30,
        "Oct": 31, "Nov": 30, "Dec": 31,//此处的逗号是必须的
    }

    for key, val := range monthdays {
        fmt.Printf("key = %s, val = %d\n", key, val)
    }
}

当在 map 中索引(搜索)时,使用方括号。
例如打印出 12 月的天数:fmt.Printf("%d\n", monthdays["Dec"])
这个map就像我们平常看到的表格一样,左边列是 key ,右边列是值文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

使用map过程中需要注意的几点:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

  • map 是无序的,所以上述例子每次打印出来的结果顺序都不一样都会不一样,它不能通过 index 获取,而必须通过 key 获取
  • map 的长度是不固定的,也就是和 slice 一样,也是一种引用类型
  • 内置的 len 函数同样适用于 map,返回 map 拥有的 key 的数量
  • map 的值可以很方便的修改,通过 monthdays["Feb"] = 20 可以很容易的把 key 为 Feb 的字典值改为 20
  • map 和其他基本类别不同,它不是 Thread-safe ,在多个 goroutine 存取时,必须使用 mutex lock 机制。 Go1.9 以后可用 sync.Map 解决这个问题
  • map 的初始化可以通过 key:val 的方式初始化值,同时 map 内置有判断是否存在 key 的方式

通过 delete 删除 map 的元素

例:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main

func main() {
    monthdays := map[string]int{
        "Jan": 31, "Feb": 28, "Mar": 31,
        "Apr": 30, "May": 31, "Jun": 30,
        "Jul": 31, "Aug": 31, "Sep": 30,
        "Oct": 31, "Nov": 30, "Dec": 31,
    }

    //删除map中Feb元素
    delete(monthdays, "Feb")

    //map 有两个返回值,第一个返回值为该 key 的值;
    // 第二个返回值,如果不存在 key,那么 ok 为 false,如果存在 ok 为 true
    val, ok := monthdays["Feb"]

    //判断monthdays["Feb"]是否存在
    if ok {
        println("Feb is in the map and its value is", val)
    } else {
        println("Feb isn't in the map")
    }
}

输出结果:
Feb isn't in the map文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

例2:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var ms = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

func main() {
    fmt.Println(ms)
}

map 也是一种引用类型,如果两个 map 同时指向一个底层,那么一个改变,另一个也相应的改变:文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

文章源自编程技术分享-https://mervyn.life/d9deb1b5.html

package main

import "fmt"

func main() {
    m := make(map[string]string)
    fmt.Println("map m is: ", m)

    m["Hello"] = "Bonjour"
    m["World"] = "XXXXX"
    fmt.Println("map m is: ", m)

    m1 := m
    fmt.Println("map m1 is: ", m1)

    m1["Hello"] = "Salut"  // 现在m["hello"]的值已经是Salut了
    fmt.Println("map m is: ", m)
    fmt.Println("map m1 is: ", m1)
}
文章源自编程技术分享-https://mervyn.life/d9deb1b5.html
weinxin
我的微信公众号
微信扫一扫
mervyn
Go 方法指针接收者和值接收者 编程语言

Go 方法指针接收者和值接收者

Go 语言可以给自定义的类型添加一个方法。这里的方法其实也是函数,跟函数的区别在于在 func 关键字和函数名中间增加了一个参数,可以认为该类型也作为了参数传递入了函数中,例: package mai...
Go语言学习(十一)| 通道 编程语言

Go语言学习(十一)| 通道

通道类型的值本身就是并发安全的,这也是 Go 语言自带的、唯一一个可以满足并发安全性的类型。 通道声明及初始化 通道相当于一个先进先出的队列。在声明一个通道类型变量的时候,我们首先要确定该通道类型的元...
Go语言学习(十)| module 使用 编程语言

Go语言学习(十)| module 使用

requirement go module 是在go 1.11版本才开始有的,需要将环境变量 GO111MODULE 设置为 on 才能正常使用 例: export GO111MODULE=on 初次...
Go语言学习(二)| 变量 编程语言

Go语言学习(二)| 变量

Go变量的类型在变量名的后面。 声明单个变量 使用语法为: var var_name type , 例: var age int var age int = 10 //声明单个变量并初始化 声明多个变...
Go语言学习(一)| 环境搭建 编程语言

Go语言学习(一)| 环境搭建

Linux 下安装 已1.10.3 为例,下载源码包:下载地址 wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz ​ 将下载的源码包解...