Golang的常用容器
本篇主要是讲述了数组和切片、Map的初始化方式与基本使用、重点阐述了如何使用Map实现Set、用Map实现工厂模式,以及字符串的使用,字符串常用API、Unicode与UTF8的关系!
数组和切片
1func TestArray01(t *testing.T) {
2 //声明并初始化为默认值
3 var a [3]int
4 a[0] = 1
5
6 b := [3] int {11, 22, 33}
7 c := [2][2] int {{11, 22}, {33, 44}}
8
9 for _,data := range b{
10 t.Log(data)
11 }
12 for _,data := range c{
13 t.Log(data)
14 }
15}
16
17func TestArraySection(t *testing.T){
18 arr := [...]int{11, 22, 33, 44}
19 arr_sec := arr[:3] //取前三个元素
20 t.Log(arr_sec)
21 arr_sec = arr[3:] //取下标为3的后面的所有元素
22 t.Log(arr_sec)
23}
其实切片的内部结构和Buffer的结构是差不多的,切片的使用:
make函数相当于申请空间、并且可以控制多少空间初始化!
1package slice_test
2
3import "testing"
4
5func TestSlice(t *testing.T) {
6 var s0 []int //切片的声明
7 t.Log(len(s0), cap(s0)) //0 0
8 s0 = append(s0, 1)
9 t.Log(len(s0), cap(s0)) //1 1
10
11 s1 := []int{1, 2, 3, 4}
12 t.Log(len(s1), cap(s1)) //4 4
13
14 s2 := make([]int, 3, 5)
15 t.Log(len(s2), cap(s2)) //4 4
16
17 s2 = append(s2, 1)
18 t.Log(len(s2), cap(s2)) //4 4
19 t.Log(s2[0], s2[1], s2[2], s2[3])
20}
切片如何扩容呢?
1func TestSliceGrowing(t *testing.T) {
2 s := []int{}
3 for i:=0; i<10; i++{
4 s = append(s, i)
5 t.Log(len(s), cap(s))
6 }
7}
可以看出来,每次增长二倍的关系,所以就解释了为什么每次append之后都要接收append的返回值,因为go的内部实现就是内存区域Copy的形式,所以随时可能返回新的内存地址,所以需要接收!
1func TestSliceShareMemory(t *testing.T){
2 year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
3 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
4
5 Q2 := year[3:6]
6 t.Log(Q2, len(Q2), cap(Q2)) //[Apr May Jun] 3 9
7 summer := year[5:8]
8 t.Log(summer, len(summer), cap(summer)) //[Jun Jul Aug] 3 7
9 summer[0] = "UnKnow"
10 t.Log(Q2, len(Q2), cap(Q2)) //[Apr May UnKnow] 3 9
11}
数组和切片的比较:数组不可伸缩、切片可以伸缩;相同维数且相同长度的数组才是可以比较的,切片是不可以比较的,下面的代码会编译不通过:
1func TestSliceCompare(t *testing.T){
2 a := []int{1, 2 , 3, 4}
3 b := []int{1, 2 , 3, 4}
4 if a == b{ //error
5 t.Log("equal")
6 }
7}
Map的基本使用
1package map_test
2
3import "testing"
4
5func TestInitMap(t *testing.T) {
6 m1 := map[string]int{"one":1, "two":2, "three":3}
7 t.Log(m1, len(m1)) //map[one:1 three:3 two:2] 3
8
9 m2 := map[int]int{}
10 m2[4] = 16
11 t.Log(m2, len(m2)) //map[4:16] 1
12
13 m3 := make(map[int]int, 10)
14 t.Log(m3, len(m3)) //map[] 0
15}
Map的遍历
1func TestForEachMap(t *testing.T) {
2 m1 := map[string]int{"one":1, "two":2, "three":3}
3 for k, v := range m1{
4 t.Log(k, "=", v)
5 }
6}
Map与工厂模式
Map的Value可以是一个方法,因为在Golang中,方法也是一种类型,可以当做参数传递的,与Dock type接口方式一起,可以方便的实现单一方法对象的工厂模式
1func TestMapWithFunValue(t *testing.T) {
2 //Key是整型、Value是方法类型
3 m := map[int] func(op int) int {}
4
5 m[1] = func(op int) int {
6 return op
7 }
8
9 m[2] = func(op int) int {
10 return op * op
11 }
12
13 m[3] = func(op int) int {
14 return op * op * op
15 }
16
17 t.Log(m[1](2 ), m[2](2), m[3](2)) //2 4 8
18}
Map实现Set
Go内置集合没有Set实现,可以map[type]bool
1、元素的唯一性
2、基本操作
- 添加元素
- 判断元素是否存在
- 删除元素
- 元素个数
1func TestMapForSet(t *testing.T) {
2 mySet := map[int]bool{}
3 //1、添加元素
4 mySet[1] = true
5
6 //n := 3 //3 is not existing
7 n := 1 //1 is existing
8
9 //2、判断一个元素是否存在
10 if mySet[n]{
11 t.Logf("%d is existing", n) //1 is existing
12 }else{
13 t.Logf("%d is not existing", n)
14 }
15
16 //3、获取Set的长度
17 mySet[2] = true
18 mySet[3] = true
19 t.Log("len =", len(mySet)) //len = 3
20
21 //4、从Set删除元素
22 delete(mySet, 1)
23 n = 1
24 if mySet[n]{
25 t.Logf("%d is existing", n)
26 }else{
27 t.Logf("%d is not existing", n) //1 is not existing
28 }
29}
字符串
与其他主要编程语言的差异:
string 是数据类型,不是引用或指针类型
string 是只读的byte slice, len 函数可以它所包含的byte数
string 的byte数组可以存放任何数据
string实际上就是一个不可变的切片
1func TestString(t *testing.T) {
2 var s string
3 t.Log(s) //初始化默认值""
4 s = "hello"
5 t.Log(len(s))
6
7 //s[3] = 'c' error string不可变
8 s = "\xE4\xB8\xA5" //可以存储任意二进制数据
9 t.Log(s, len(s)) //严
10 s = "中国"
11 t.Log(len(s)) //len求的是字节数
12}
Unicode与UTF8
1、Unicode是一种字符集(code point)
2、UTF8是Unicode的存储实现(转为字节序列的规则)
Unicode为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF(十六进制),有110多万,每个字符都有一个唯一的Unicode编号,这个编号一般写成16进制, Unicode就相当于一张表,建立了字符与编号之间的联系,而UTF8是具体实现, 在UTF8中编号小的使用的字节就少,编号大的使用的字节就多。使用的字节个数从1到4个不等。
rune函数
在Go当中 string底层是用byte数组存的,并且是不可以改变的。fmt.Println(len(“Go编程”)) 输出结果应该是8因为中文字符是用3个字节存的, len(string(rune(‘编’)))的结果是3 ,如果想要获得我们想要的情况的话,需要先转换为rune切片再使用内置的len函数: fmt.Println(len([]rune(s))) 结果就是4了。 所以用string存储unicode的话,如果有中文,按下标是访问不到的,因为你只能得到一个byte。 要想访问中文的话,还是要用rune切片,这样就能按下标访问。
1func TestString(t *testing.T) {
2 s := "中"
3 t.Log(len(s)) //3
4
5 c := []rune(s)
6
7 t.Logf("中 unicode %x", c[0]) //中 unicode 4e2d
8 t.Logf("中 UTF8 %x", s) //中 UTF8 e4b8ad
9}
常用的字符串处理函数: strings包: https://golang.org/pkg/strings
strconv包 https://golang.org/pkg/strconv
1func TestStringFunc(t *testing.T) {
2 //切割字符串
3 s := "A,B,C"
4 split := strings.Split(s, ",")
5 for _, ch := range split{
6 t.Log(ch)
7 }
8
9 //连接字符串数组的元素
10 t.Log(strings.Join(split, "-"))
11
12 //字符串与数字的转换
13 s = strconv.Itoa(250)
14 t.Logf("s = %s", s)
15
16 num := "100"
17 //返回两个值,err代表是否发生错误
18 if i, err := strconv.Atoi(num); err == nil{
19 t.Log(100 + i)
20 }
21}