1 介绍
本文继上文 golang笔记02–golang基础语法, 进一步了解 golang 内建容器和相应注意事项。
具体包括 : 数组、切片、Map、字符和字符串 等常用的内建容器。
2 内建容器
2.1 数组
golang 可以通过 for 或者 range 来便利,但是 range 更加简洁;
go中数组是值类型,因此[10]int 和[20]int 是不同类型;
调用func f(arr [10]int) 会拷贝数组;
go语言一般不使用数组,而是使用切片;
package main
import "fmt"
func printArray1() {
arr3 := [...]int{2, 4, 6, 8, 10}
for i := 0; i < len(arr3); i++ {
fmt.Printf("%d\t", arr3[i])
}
fmt.Println()
for i := range arr3 {
fmt.Printf("%d\t", arr3[i])
}
fmt.Println()
// 通过 _ 省略变量
for _, v := range arr3 {
fmt.Printf("%d\t", v)
}
fmt.Println()
for i, v := range arr3 {
fmt.Printf("%d,%d\t", i, v)
}
}
func printArray2(arr [5]int) {
arr[0] = 100
for _, v := range arr {
fmt.Printf("%d\t", v)
}
}
func printArray3(arr *[5]int) {
arr[0] = 200
for _, v := range arr {
fmt.Printf("%d\t", v)
}
}
func main() {
fmt.Println("chapter 3.1")
var arr1 [5]int
arr2 := [3]int{1, 3, 5}
arr3 := [...]int{2, 4, 6, 8, 10} //[...] 让编译器统计有多少个元素
var grid [4][5]int
fmt.Println(arr1, arr2, arr3)
fmt.Println(grid)
printArray1()
fmt.Println()
printArray2(arr3)
fmt.Println()
fmt.Println(arr3)
printArray3(&arr3)
fmt.Println()
fmt.Println(arr3)
}
输出:
chapter 3.1
[0 0 0 0 0] [1 3 5] [2 4 6 8 10]
[[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
2 4 6 8 10
2 4 6 8 10
2 4 6 8 10
0,2 1,4 2,6 3,8 4,10
100 4 6 8 10
[2 4 6 8 10]
200 4 6 8 10
[200 4 6 8 10]
2.2 切片的概念
slice 是数组的一个 view,它本身是没有数据的,可以把它理解为一个指向原生数组的指针;它和数组的关系类似于c++ STL中的vector和数组的关系;
切片具备一系列的管理功能,可以随时动态扩充存放空间,并且可以随意传递而不会导致所管理的元素被重复复制。
切片可以进行更新、reslice、追加等常见操作,具体示例如下:
package main
import "fmt"
func create_slice() {
// 根据数组创建
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
myslice := arr[:5]
fmt.Println(myslice, len(myslice), cap(myslice))
// 直接创建
myslice2 := make([]int, 5)
myslice3 := make([]int, 5, 10)
fmt.Println(myslice2, len(myslice2), cap(myslice2))
fmt.Println(myslice3, len(myslice3), cap(myslice3))
}
func updateSlice(s []int) {
s[0] = 100
}
func main() {
fmt.Println("chapter 3.2")
//create_slice()
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("arr[2:6]=", arr[2:6])
fmt.Println("arr[:6]=", arr[:6])
s1 := arr[2:]
s2 := arr[:]
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
fmt.Println("updateSlice(s1)")
updateSlice(s1)
fmt.Println(s1)
fmt.Println(arr)
fmt.Println("updateSlice(s2)")
updateSlice(s2)
fmt.Println(s2)
fmt.Println(arr)
fmt.Println("Reslice")
fmt.Println(s2)
s2 = s2[:5]
fmt.Println(s2, cap(s2))
s2 = s2[2:]
fmt.Println(s2, cap(s2))
fmt.Println("Extending slice")
arr[0] = 0
arr[2] = 2
fmt.Println(arr)
s1 = arr[2:6]
s2 = s1[3:5] //此时s2仍然是arr的view,其起始位置为3+4=5,即第一个值为5,cap为arr 5-7对应的空间
fmt.Printf("s1=%v, len(s1)=%d, cap(s1)=%d\n", s1, len(s1), cap(s1))
fmt.Printf("s2=%v, len(s2)=%d, cap(s2)=%d\n", s2, len(s2), cap(s2))
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println("s3, s4, s5=", s3, s4, s5)
fmt.Println(arr)
}
输出:
chapter 3.2
arr[2:6]= [2 3 4 5]
arr[:6]= [0 1 2 3 4 5]
s1= [2 3 4 5 6 7]
s2= [0 1 2 3 4 5 6 7]
updateSlice(s1)
[100 3 4 5 6 7]
[0 1 100 3 4 5 6 7]
updateSlice(s2)
[100 1 100 3 4 5 6 7]
[100 1 100 3 4 5 6 7]
Reslice
[100 1 100 3 4 5 6 7]
[100 1 100 3 4] 8
[100 3 4] 6
Extending slice
[0 1 2 3 4 5 6 7]
s1=[2 3 4 5], len(s1)=4, cap(s1)=6
s2=[5 6], len(s2)=2, cap(s2)=3
s3, s4, s5= [5 6 10] [5 6 10 11] [5 6 10 11 12]
[0 1 2 3 4 5 6 10]
2.3 切片的操作
切片除了可以更新、reslice、extend外,还可以进行copy、删除某个元素、pop 首尾元素等操作,具体示例如下:
package main
import "fmt"
func printSlice(s []int) {
fmt.Printf("%v, len=%d, cap=%d\n", s, len(s), cap(s))
}
func main() {
fmt.Println("chapter 3.3")
var s []int //zero value, len=0, cap=0
fmt.Println(s, len(s), cap(s))
for i := 0; i < 100; i++ {
s = append(s, 2*i+1)
//printSlice(s)
}
fmt.Println("s info:", len(s), cap(s))
// 可以发现,每当slice cap满了之后,其cap会自动扩大一倍,和cpp的 vector 部分机制类似
fmt.Println("create slice")
s1 := []int{2, 4, 5, 8}
printSlice(s1)
s2 := make([]int, 16)
s3 := make([]int, 10, 32)
printSlice(s2)
printSlice(s3)
fmt.Println("copying slice")
copy(s2, s1)
printSlice(s2)
fmt.Println("deleting elements from slice")
s2 = append(s2[:3], s2[4:]...)
printSlice(s2)
fmt.Println("popping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println("front: ", front)
printSlice(s2)
fmt.Println("popping from back")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println("back: ", tail)
printSlice(s2)
}
输出:
chapter 3.3
[] 0 0
s info: 100 128
create slice
[2 4 5 8], len=4, cap=4
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16
[0 0 0 0 0 0 0 0 0 0], len=10, cap=32
copying slice
[2 4 5 8 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16
deleting elements from slice
[2 4 5 0 0 0 0 0 0 0 0 0 0 0 0], len=15, cap=16
popping from front
front: 2
[4 5 0 0 0 0 0 0 0 0 0 0 0 0], len=14, cap=15
popping from back
back: 0
[4 5 0 0 0 0 0 0 0 0 0 0 0], len=13, cap=15
2.4 Map
map 在go语言中是一对未排序的键值对的集合。它可以很方便的进行删除、赋值和元素查找。
package main
import (
"fmt"
)
func main() {
fmt.Println("chapter 3.4")
// 创建map
m := map[string]string{
"name": "tom",
"course": "golang",
"site": "immoc",
"quality": "not bad",
}
m2 := make(map[string]int) // empty map
var m3 map[string]int // nil
fmt.Println(m, m2, m3)
// 遍历map
fmt.Println("traversing map m")
for k, v := range m {
fmt.Println(k, v)
}
fmt.Println("getting values")
courseName := m["course"]
fmt.Println(`m["course"]=`, courseName)
if courseName, ok := m["cause"]; ok {
fmt.Println(courseName)
} else {
fmt.Println("key 'cause' does not exist")
}
fmt.Println("deleting values")
name, ok := m["name"]
fmt.Printf("m[%q] before delete: %q, %v\n", "name", name, ok)
delete(m, "name")
name, ok = m["name"]
fmt.Printf("m[%q] after delete: %q, %v\n", "name", name, ok)
}
输出:
chapter 3.4
map[course:golang name:tom quality:not bad site:immoc] map[] map[]
traversing map m
name tom
course golang
site immoc
quality not bad
getting values
m["course"]= golang
key 'cause' does not exist
deleting values
m["name"] before delete: "tom", true
m["name"] after delete: "", false
2.5 Map例题
求字符串中最长不重复的子串长度。
思路:设置最长字符串的起始位置为start,每后移一次就判断该移动是否会追加最大长度,若不增加则调整起始位置,重新再找一个最大的子串;依次循环直到遍历到最后一个字符,并输出最长子串长度。
package main
import "fmt"
func lengthOfNonRepeatSubStr(s string) int {
lastOccurred := make(map[rune]int)
start := 0
maxLength := 0
for i, ch := range []rune(s) {
if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
start = lastI + 1
}
if i-start+1 > maxLength {
maxLength = i - start + 1
}
lastOccurred[ch] = i
}
return maxLength
}
func main() {
fmt.Println("chapter 3.5, 求最长子串")
fmt.Println(lengthOfNonRepeatSubStr("abcabcbb"))
fmt.Println(lengthOfNonRepeatSubStr("bbbbb"))
fmt.Println(lengthOfNonRepeatSubStr("pwwkew"))
fmt.Println(lengthOfNonRepeatSubStr(""))
fmt.Println(lengthOfNonRepeatSubStr("b"))
fmt.Println(lengthOfNonRepeatSubStr("abcdef"))
fmt.Println(lengthOfNonRepeatSubStr("这里是慕课网"))
fmt.Println(lengthOfNonRepeatSubStr("一二三二一"))
fmt.Println(lengthOfNonRepeatSubStr("黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"))
}
输出:
chapter 3.5, 求最长子串
3
1
3
0
1
6
6
3
8
2.6 字符和字符串处理
go中字符串类型为string,字符有byte(单个代表UTF-8字符)和rune(单个代码Unicode字符)两种,并且可以轻松的将 string 转为 byte 和 rune 字符数组。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
fmt.Println("chapter 3.6")
s := "Yes我爱慕课网"
fmt.Println(s)
// 按照字节输出,根据下标 i 可知英文字母占1字节,中文站3字节
for i, ch := range s {
fmt.Printf("(%d %X)", i, ch)
}
// 获取rune类型字符长度
fmt.Println()
fmt.Println("rune count:", utf8.RuneCountInString(s))
bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes)
bytes = bytes[size:]
fmt.Printf("%c ", ch)
}
fmt.Println()
for i, ch := range []rune(s) {
fmt.Printf("(%d %c) ", i, ch)
}
}
输出:
chapter 3.6
Yes我爱慕课网
(0 59)(1 65)(2 73)(3 6211)(6 7231)(9 6155)(12 8BFE)(15 7F51)
rune count: 8
Y e s 我 爱 慕 课 网
(0 Y) (1 e) (2 s) (3 我) (4 爱) (5 慕) (6 课) (7 网)
3 注意事项
待添加
4 说明
- 软件环境
go版本:go1.15.8
操作系统:Ubuntu 20.04 Desktop
Idea:2020.01.04 - 参考文档
由浅入深掌握Go语言 --慕课网
go 语言编程 --许式伟