数组切片和映射

数组,切片,映射指的就是array,slice和map这三种类型.其中有部分第三章的内容在这里先做个记录.

  • 对于中文字符,utf8字符串的处理.可以转换为[]rune.这样可以遍历出完整的字符
    而不是字节
  • go doc指令的一个扩充,生成web页面查看 godoc -http=:6060访问本地的6060端口即可
  • 接下来是第四章的一些记录(基于golang1.7)

数组(array)

Go的数组的定义规范和java类似,是一个长度固定的数据类型,存储的数据必须是类型一致的.
其存储在一段连续的内存区间,所以读取快速.

声明和初始化

声明方法:

1
var array [5]int

这是声明了一个长度为5的int数组.编译器会根据其类型做相应的初始化.比如这里数组里存储的就是int的零值 0.在后续的代码里我们可以对其做赋值操作.当然也可以声明时初始化:

1
array := [...]int{1,2,3,4,5}

这里就是在声明时初始化.其中长度表示那里的三个点是代表由Go根据初始化时的值自己计算长度.还有一种指定下标的初始化方法:

1
array := [...]int{99:99}

上面的代码就是声明了一个长度为100的int数组,其中下标为99的元素的值就是99,其他则是默认的0

至于数组的处理则可以通过下标索引操作.比如赋值

1
array[1] = 22

长度以及类型一致的数组可以相互赋值

书里还提到了指针数组.

1
array := [5]*int{0:new(int),1:new(int)}

这里声明了一个长度为5的int指针的数组.而通过*操作符则可以操作指针指向的值

1
*array[0] = 0

这里还用到了new().这里记下make和new的区别
make:用来初始化map,slice,channel.返回的就是初始化后的对象而不是指针.其会根据类型赋予类型对应的零值

new:这个可以用于任何结构,但是只是声明空间返回指针,内部并不会做初始化.

切片(slice)

切片是建立在数组之上的数据结构,其内部存储了指向底层数组的指针,长度以及容量.
长度是指得当前slice中元素个数(可以通过len()获得),容量则是在创建slice时指定的长度上限(通过cap()可以获得).

声明以及初始化

上面有提到make()创建并初始化slice.具体语法是:

1
slice := make([]int,3,5)

这就是创建了一个长度为3,容量为5的切片.容量参数未设置默认与长度相同(当然容量必须大于等于长度).当然也可以和数组一样通过字面量创建或者基于数组创建

1
2
3
array := [5]int{0,1,2,3,4,5}
slice := []int{0,1,2}//长度和容量都是3,这里也可以用和上面类似的指定下标的方式
sliceA := array[:3]//基于数组创建,包头不包尾,长度为3,cap为6(就是slice头元素到数组尾部的长度)

切片的操作和数组其实是一样的,通过下标索引.也可以在切片的基础上再建切片.

1
2
slice := []int{0,1,2,3}
slicet :=slice[:3]//:前后代表上界和下界.省略表示从0开始或者直到末尾

不过这里在操作slice的时候实际操作的是底层数组,因为slice是基于底层数组的结构,而且内部存储的是指针.所以一个slice修改了之后会影响到其他所有共用一个底层数组的slice.

append扩充:利用函数append()可以修改slice的结构,比如尾部追加元素

1
slice = append(slice,1)//尾部追加int元素1

这里在容量(cap)可以容纳下新增元素的时候,slice的底层数组和cap是不会变化的,只是长度追加1.并且会修改底层数组对应下标的元素值:

1
2
3
4
array := [5]int{0,1,2,3,4,5}
slice := array[:2]//slice:{0,1} len :2,cap:6
slice = append(slice,6)//slice:{0,1,6} len:3,cap:6
//但是array:{0,1,6,3,4,5}

而当cap本身不能容纳元素时则会新建底层数组并将元素复制到新数组中,在新数组的基础上建立slice.至于新建扩容的问题,因为版本不同会出现不同的结果,1.6上测试是扩容后的长度的2倍作为新的cap(windows7 32bit),1.7则是原cap的2倍作为新的cap(debian 3.16 64bit)

当然append的用法很多比如删除slice中的一部分元素

1
slice = append(slice[:3],slice[4:]...)//去掉下标为3的元素,注意第二个参数的...这是代表将slice元素拆开传入,否将会报错,类型不符合

还有就是在建立slice的第三个参数的作用,指定cap的上限,可以保证底层数组的安全以及隐藏

1
2
3
4
5
6
slice := []int{0,1,2,3,4,5,6}
slice2 := slice[:3]//0,1,2
slice3 := slice2[:cap(slice2)]//{0,1,2,3,4,5,6}扩容获取到本不应该取到的3,4,5,6
slice4 := slice[:3:3]//0,1,2.cap是3.最后的三表示其cap之是开头到数组下标为3的地方
slice3 = slice4[:cap(slice4)]//0,1,2

slice部分,书里还提到了nil切片和空切片,nil切片指的是未初始化的,只声明的.空切片则是没有内容的

1
2
3
4
5
var slice []int//nil这里你在slice == nil是true,和数组是不一样的
//空切片 slice == nil :false
slice := make([]int,0)
or
slice := []int{}

slice的迭代

可以通过传统的for循环迭代.

1
2
3
for i := 0;i<len(slice);i++{
log.Println(i,slice[i])
}

另外还有for range迭代切片

1
2
3
4
5
slice := []int{0,1,2,3,4,5,6}
//在之前的记录中提到for range 提供的是元素副本
for index,value := range slice{
fmt.Printf("Index:%d,Value:%d,ValueAddr:%x,ElementAddr:%x \n",index,value,&value,&slice[index])
}

这里的格式化标识符可以go doc fmt在文档中看到,另外也有简单整理在这里

1
2
3
4
5
6
7
8
//结果如下
Index:0,Value:0,ValueAddr:c420072320,ElementAddr:c42007a140
Index:1,Value:1,ValueAddr:c420072320,ElementAddr:c42007a148
Index:2,Value:2,ValueAddr:c420072320,ElementAddr:c42007a150
Index:3,Value:3,ValueAddr:c420072320,ElementAddr:c42007a158
Index:4,Value:4,ValueAddr:c420072320,ElementAddr:c42007a160
Index:5,Value:5,ValueAddr:c420072320,ElementAddr:c42007a168
Index:6,Value:6,ValueAddr:c420072320,ElementAddr:c42007a170

这里主要提两点:

  • value的地址是同一个也就是每次迭代覆盖之前的值,这个在闭包一类的用法中需要注意
  • 就是上面说的for range 中value提供的是元素值的副本

至于书里提到的多维切片,类比数组即可

映射(map)

创建以及初始化

  • make关键字:dict := make(map[string]int)
  • 字面量初始化 dict := map[string]int{"first":1,"second":2}

仍然使用len()来获取map的长度,map的键需要是可以==操作的,且引用语义的不能作为key

nil映射和空映射

nil映射:var mapE map[int]string.这里==nil是true
空映射:上面的make以及这样操作的就是空映射dict := map[int]string{} ==nil自然是false

map的迭代以及使用

以key为索引操作,比如赋值:dict[1] = “one”

对nil映射赋值是会报错的

迭代使用for range:

1
2
3
for key,value := range dict{
log.Printf("key:%d,value:%s\n",key,value)
}

书里还提到了判断map中某个元素是否存在:

1
2
value,exist := dict[key]
//其中exist是bool类型,存在则返回value,true,不存在则返回类型的零值以及false

这种方式是可以在nil映射上操作的.另一种方式不推荐,即只有value返回值,这就无法判断零值是确实存储在map中的还是不存在的.

删除map中的元素:delete(dict,key).

和slice一样,map也具有引用语义,所以在方法中传递map需要注意,一处修改,处处可见.delete仅用于map

客官扫码领红包哟~