golang笔记03--golang 內建容器

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 说明

  1. 软件环境
    go版本:go1.15.8
    操作系统:Ubuntu 20.04 Desktop
    Idea:2020.01.04
  2. 参考文档
    由浅入深掌握Go语言 --慕课网
    go 语言编程 --许式伟
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
个人学习golang笔记,从各种教程中总结而来,作为入门参考。目录如下 目录 1. 入门 1 1.1. Hello world 1 1.2. 命令行参数 2 2. 程序结构 3 2.1. 类型 4 2.1.1. 命名类型(named type)与未命名类型(unamed type) 4 2.1.2. 基础类型(underlying type) 4 2.1.3. 可赋值性 5 2.1.4. 类型方法集 6 2.1.5. 类型声明 6 2.2. 变量 8 2.2.1. 变量声明 8 2.2.2. 类型零值 12 2.2.3. 指针 13 2.3. 赋值 17 2.4. 包和文件 17 2.5. 作用域 18 2.6. 语句 19 2.7. 比较运算符 20 2.8. 类型转换 21 2.9. 控制流 23 2.9.1. If 23 2.9.2. Goto 24 2.9.3. For 25 2.9.4. Switch 25 2.9.5. break语句 31 2.9.6. Continue语句 31 3. 基础数据类型 31 3.1. golang类型 31 3.2. Numeric types 32 3.3. 字符串 33 3.3.1. 什么是字符串 33 3.3.2. 字符串底层概念 35 3.3.3. 获取每个字节 38 3.3.4. Rune 39 3.3.5. 字符串的 for range 循环 40 3.3.6. 用字节切片构造字符串 41 3.3.7. 用rune切片构造字符串 42 3.3.8. 字符串的长度 42 3.3.9. 字符串是不可变的 42 3.3.10. UTF8(go圣经) 43 3.4. 常量 45 3.4.1. 常量定义 45 3.4.2. 常量类型 46 3.4.3. Iota 46 4. 组合数据类型 47 4.1. 数组 47 4.1.1. 数组概述 47 4.1.2. 数组的声明 49 4.1.3. 数组的长度 50 4.1.4. 遍历数组 50 4.1.5. 多维数组 51 4.2. 切片 52 4.2.1. 什么是切片 52 4.2.2. 切片概述 55 4.2.3. 创建一个切片 55 4.2.4. 切片遍历 57 4.2.5. 切片的修改 58 4.2.6. 切片的长度和容量 60 4.2.7. 追加切片元素 62 4.2.8. 切片的函数传递 65 4.2.9. 多维切片 66 4.2.10. 内存优化 67 4.2.11. nil slice和empty slice 69 4.2.12. For range 70 4.3. 结构 71 4.3.1. 什么是结构体? 71 4.3.2. 结构体声明 73 4.3.3. 结构体初始化 77 4.3.4. 嵌套结构体(Nested Structs) 81 4.3.5. 匿名字段 82 4.3.6. 导出结构体和字段 84 4.3.7. 结构体相等性(Structs Equality) 85 4.4. 指针类型 86 4.5. 函数 87 4.6. map 87 4.6.1. 什么是map 87 4.6.2. 声明、初始化和make 89 4.6.3. 给 map 添加元素 91 4.6.4. 获取 map 中的元素 91 4.6.5. 删除 map 中的元素 92 4.6.6. 获取 map 的长度 92 4.6.7. Map 的相等性 92 4.6.8. map的排序 92 4.7. 接口 93 4.7.1. 什么是接口? 93 4.7.2. 接口的声明与实现 96 4.7.3. 接口的实际用途 97 4.7.4. 接口的内部表示 99 4.7.5. 空接口 102 4.7.6. 类型断言 105 4.7.7. 类型选择(Type Switch) 109 4.7.8. 实现接口:指针接受者与值接受者 112 4.7.9. 实现多个接口 114 4.7.10. 接口的嵌套 116 4.7.11. 接口的零值 119 4.8. Channel 120 4.9. 类型转换 120 5. 函数 120 5.1. 函数的声明 121 5.2. 一个递归函数的例子( recursive functions) 121 5.3. 多返回值 121 5.4. 命名返回值 121 5.5. 可变函数参数 122 5.6. Defer 123 5.6.1. Defer语句介绍 123 5.6.2. Defer使用场景 128 5.7. 什么是头等(第一类)函数? 130 5.8. 匿名函数 130 5.9. 用户自定义的函数类型 132 5.10. 高阶函数(装饰器?) 133 5.10.1. 把函数作为参数,传递给其它函数 134 5.10.2. 在其它函数中返回函数 134 5.11. 闭包 135 5.12. 头等函数的实际用途 137 6. 微服务创建 140 6.1. 使用net/http创建简单的web server 140 6.2. 读写JSON 144 6.2.1. Marshal go结构到JSON 144 6.2.2. Unmarshalling JSON 到Go结构 146 7. 方法 146 7.1. 什么是方法? 146 7.2. 方法示例 146 7.3. 函数和方法区别 148 7.4. 指针接收器与值接收器 153 7.5. 那么什么时候使用指针接收器,什么时候使用值接收器? 155 7.6. 匿名字段的方法 156 7.7. 在方法中使用值接收器 与 在函数中使用值参数 157 7.8. 在方法中使用指针接收器 与 在函数中使用指针参数 159 7.9. 在非结构体上的方法 161 8. 并发入门 162 8.1. 并发是什么? 162 8.2. 并行是什么? 162 8.3. 从技术上看并发和并行 163 8.4. Go 对并发的支持 164 9. Go 协程 164 9.1. Go 协程是什么? 164 9.2. Go 协程相比于线程的优势 164 9.3. 如何启动一个 Go 协程? 165 9.4. 启动多个 Go 协程 167 10. 信道channel 169 10.1. 什么是信道? 169 10.2. 信道的声明 169 10.3. 通过信道进行发送和接收 169 10.4. 发送与接收默认是阻塞的 170 10.5. 信道的代码示例 170 10.6. 信道的另一个示例 173 10.7. 死锁 174 10.8. 单向信道 175 10.9. 关闭信道和使用 for range 遍历信道 176 11. 缓冲信道和工作池(Buffered Channels and Worker Pools) 179 11.1. 什么是缓冲信道? 179 11.2. 死锁 182 11.3. 长度 vs 容量 183 11.4. WaitGroup 184 11.5. 工作池的实现 186 12. Select 188 12.1. 什么是 select? 188 12.2. 示例 189 12.3. select 的应用 190 12.4. 默认情况 190 12.5. 死锁与默认情况 191 12.6. 随机选取 191 12.7. 这下我懂了:空 select 191 13. 文件读写 191 13.1. GoLang几种读文件方式的比较 197 14. 个人 197 14.1. ++,-- 198 14.2. 逗号 198 14.3. 未使用的变量 199 14.4. Effective go 199 14.4.1. 指针 vs. 值 199 14.5. 可寻址性-mapslice的区别 201 14.5.1. slice 201 14.5.2. map 202 14.6. golang库 203 14.6.1. unicode/utf8包 203 14.6.2. time包 205 14.6.3. Strings包 205 14.6.4. 输入输出 212 14.6.5. 正则处理 224 14.6.6. Golang内建函数 226

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昕光xg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值