三、常用数据结构
3.1 数组
先来看一下 Go 语言中的数组的几种声明方式:
var arr [3]int //声明数组并初始化为零值 arr[0] = 10 arr2 := [3]int{1, 2} //声明时初始化 arr3 := make([]int, 2) //声明长度为2的数组 arr4 := [3][3]int{{1, 2, 3}, {4, 5, 6}} //多维数组的初始化
数组的常见操作是对其进行遍历,我们可以按照一贯的做法,写一个 for 循环来完成,但是 Go 语言中的 range 关键字可以使代码更加的简洁:
//常规的遍历方式 for i := 0; i < len(arr2); i++ { fmt.Println(arr2[i]) } //数组元素的遍历 for i, v := range arr2 { fmt.Println(i, v) } //忽略下标或者值,_ 表示 for _, val := range arr2{ fmt.Println(val) } 数组还支持截取操作,这和 python 的特性非常类似: //数组截取 fmt.Println(arr[0:2]) fmt.Println(arr[:2]) fmt.Println(arr[0:]) fmt.Println(arr[1:len(arr)])
3.2 切片
切片可以看做是对数组的一层封装,因为每个切片的底层一定都是一个数组,先来看看切片的声明方式:
var sli0 []int sli0 = append(sli0, 1, 2) sli1 := []int{} sli1 = append(sli1, 1, 2, 3) sli2 := []int{1, 2, 3} sli3 := make([]int, 5) //指定长度,默认值0 fmt.Println(len(sli2)) fmt.Println(cap(sli2)) sli4 := make([]int, 5, 8) //指定长度及容量, fmt.Println(sli4[3]) //可以访问,默认值0 fmt.Println(sli4[7]) //无法访问 可以看到切片的声明方式其实和数组非常的类似,只是方括号中没有了表示指定长度的数字。如果需要往切片中新增加元素,我们可以使用 append 函数: sli := make([]int, 5) sli = append(sli, 1) sli = append(sli, 2, 3)
那么数组和切片的区别都有哪些呢?
数组的长度一定是一个定值,就是其在声明时所指定的长度,但是切片类型的长度是可变化的,切片的长度随着其中元素的增长而增长,只不过却不会随着元素的减少而减少。
当切片的容量不够时,那么它会扩展至原来的两倍,如果切片中的数据量大于等于 1024 的话,那么每次扩容 1.25 倍。
在声明切片时,可以指定切片的长度及容量,如果不指定容量,那么默认长度就是其容量。
//指定长度及容量 sli1 := make([]int, 5, 10) fmt.Println(len(sli1)) //5 fmt.Println(cap(sli1)) //10 //不指定容量,那么长度就是其容量 sli2 := make([]int, 5) fmt.Println(len(sli2)) //5 fmt.Println(cap(sli2)) //5
你可以将切片想象成一个数组上方的窗口,你只能通过这个窗口看见数组的一部分元素,这个窗口的大小就是切片的大小,而数组就是切片底层的数组。
例如上面声明的切片 slie1,长度是 5,容量是 10,那么切片能够看到的数组中的元素便是下标为 0 - 4 的这五个元素。
3.3 集合
Go 语言中的 map 的声明方式有下面的几种:
map1 := map[string]int{} map1["a"] = 1 map2 := map[string]int{ "a": 1, "b": 2, } map3 := make(map[int]string, 10) map 也可以使用 range 关键字像数组那样进行遍历: m := map[int]string{ 1: "a", 2: "b", 3: "c", 4: "d", } for k, v := range m{ fmt.Println(k, v) }
需要注意的是,map 中如果一个键对应的值不存在,那么它的默认值会是零值,例如下面的示例:
m := map[int]int{ 1: 1, 2: 2, 3: 3, 4: 4, } fmt.Println(m[5]) //打印出 0
这样的话就会存在一个问题,我们怎么判断一个 map 中键对应的值,到底是不存在还是它的值本来就是 0 呢?其实访问 map 中的值时,它会带有两个返回值,一个返回值是键对应的值,另一个则是是否存在,可以借助这个返回值来判断:
i, ok := m[5] if ok{ fmt.Println(i) }else { fmt.Println("不存在键为5") }
了解了 map,你可能会很自然的想到 set,是的,set 也是一个非常重要并且常用的数据结构,但是在 Go 语言中并没有 set 的实现,只不过我们可以使用 map 来实现一个 set,具体看代码:
set := map[int]bool{} //存放数据 set[1] = true set[4] = true set[4] = true //删除数据 delete(set, 1) //判断元素是否存在 if set[1] { fmt.Println("存在 1") }else { fmt.Println("不存在 1") } //元素的个数 size := len(set)
上面的程序基本完成了常见的 set 的常用功能,其实现也很简单,就是改造了一个 map,其键就相当于我们 set 的值,而 map 键对应的值是一个 bool 值,如果为 true 则表示存在于 set 中,如果为 false 则表示不存在。
3.4 字符串
Go 语言中的字符串 string 是数据类型,而不是引用类型或者指针类型,这也是为什么前面提到的,string 的默认值是空字符串,而不是 nil。
字符串也可以像数组一样进行截取和遍历操作:
s := "I like Golang" //截取 fmt.Println(s[1]) fmt.Println(s[1:4]) //遍历 for i, v := range s{ fmt.Printf("%d, %c \n", i, v) }
字符串的常用操作方法都在 strings 和 strconv 包下面,下面代码给出了几个示例:
s := "1,2,3" //字符串分割 spliS := strings.Split(s, ",") fmt.Println(spliS) //是否包含 c := strings.Contains(s, "1") fmt.Println(c) //替换 reS := strings.Replace(s, ",", "-", -1) fmt.Println(reS) //strconv 包中的函数,主要是和其他数据类型的转换 v, _ := strconv.ParseBool("false") fmt.Println(v)
Go 语言的基础语法介绍的第一篇文章就结束了,如果大家有不懂的地方,或者对文中的内容有疑议,欢迎与我交流!
我一直认为编程是一项熟能生巧的手艺,只有多写代码才能够在实践当中提升自己的编程能力。
为此我在 Github 上新建了一个学习项目,使用 Java、Golang、Python 三种语言实现常见的数据结构和算法,以此做为练习编程的素材,你可以多看看代码,并且自己多动手编写,项目地址是 :