Slice切片
Go中的数组和PHP中的数组很不一样,没法排序,没法添加。不过Go中有一种数据类型叫做Slice切片,这个数据类型很类似于PHP中的数组。我个人感觉也是利用率比较高的数据类型。
Slice切片实际上是对一个数组上的连续一段的引用,一个未初始化的分片的值是nil。一个切片,一旦初始化后它总是关联着一个容纳其元素的底层数组,所以一个分片和它的数组共享存储。
定义
slice := make([]type, start_length, capacity)//其中 start_length 作为切片初始长度而 capacity 作为相关数组的长度。
//基本方法var s1 []string{"vb","vc++","Python","java","C lang","D lang","Go lang"}//数组引用array:=[...]string{"vb","vc++","Python","java","C lang","D lang","Go lang"}s2 := array[:]//make或者new定义。//make的调用会创建一个隐含的数组,Slice就是引用的这个数组。make([]string, 5, 10) //这两种方法定义的Slice是相等的new([10]string)[0:5]
append
在切片后面追加数据,方法有两种
slice = append(slice, elem1, elem2)//切片后面追加相同的type的元素slice = append(slice, anotherSlice…)//切片后面追加切片
第一种用法中,第一个参数为slice,后面可以添加多个参数。
如果是将两个slice拼接在一起,则需要使用第二种用法,在第二个slice的名称后面加三个点,而且这时候append只支持两个参数,不支持任意个数的参数。
长度计算
初次使用Go的时候,容易对len和cap混淆,感觉一维切片的长度和容量是一回事,其实不然。
切片是一种结构体(我们会在后面具体讲什么是结构体),大体结构像这样:
struct slice{ptr *Elem //指针len int //长度cap int //容量}
先看一个小例子:pro03_5_3.go
package mainimport "fmt"func main() {slice := []int{5}slice = append(slice, 7)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])slice = append(slice, 9)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])s1 := append(slice, 11)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s1) =", &s1[0])s2 := append(slice, 12)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s2) =", &s2[0])}

通过结果图,我们可以看到len和cap的长度是不一样的。但是他们所指向的地址是相同的。
通过上面的例子还引出一个有趣的问题。把程序稍微修改一下:pro03_5_4.go
package mainimport "fmt"func main() {slice := []int{5}slice = append(slice, 7)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])slice = append(slice, 9)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])s1 := append(slice, 11)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s1) =", &s1[0])s2 := append(slice, 12)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s2) =", &s2[0])fmt.Println("slice:", slice, "s1:", s1, "s2:", s2)}
输出结果:slice: [5 7 9] s1: [5 7 9 12] s2 [5 7 9 12]
难道s1不应该是[5 7 9 11]吗?这个时候在看看上面的显示结果图的地址
cap(slice) = 4 len(slice) = 3 ptr(slice) = 0xc082002760 ptr(s1) = 0xc082002760cap(slice) = 4 len(slice) = 3 ptr(slice) = 0xc082002760 ptr(s2) = 0xc082002760
地址一样,OMG,这个是怎么回事?我来解释一下,append的实现原理:本质上append是在原有空间中添加,若空间不足时,采用 newSlice := make([]int, len(slice), 2*len(slice)+1)的方式进行扩容。
cap(slice) = 2 len(slice) = 2 ptr(slice) = 0xc082006290 //原始地址
cap(slice) = 4 len(slice) = 3 ptr(slice) = 0xc082002760 //容量不够,扩容
cap(slice) = 4 len(slice) = 3 ptr(slice) = 0xc082002760 ptr(s1) = 0xc082002760 //容量够了,直接追加
cap(slice) = 4 len(slice) = 3 ptr(slice) = 0xc082002760 ptr(s2) = 0xc082002760 //容量够了,直接追加
由于s1和s2的地址指向是一样的,所以s1的内容和我们原来想象的不一样。这个就是使用指针需要注意的。
程序换一下:pro03_5_5.go
package mainimport "fmt"func main() {slice := []int{5}slice = append(slice, 7)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])slice = append(slice, 9)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0])s1 := make([]int, len(slice))copy(s1, slice)s1 = append(s1, 11)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s1) =", &s1[0])s2 := make([]int, len(slice))copy(s2, slice)s2 = append(s2, 12)fmt.Println("cap(slice) =", cap(slice), "len(slice) =", len(slice), "ptr(slice) =", &slice[0], "ptr(s2) =", &s2[0])fmt.Println("slice:", slice, "s1:", s1, "s2:", s2)}

运算的结果:slice: [5 7 9] s1: [5 7 9 11] s2: [5 7 9 12]
这次正确了。从这个例子我们可以清晰的看出,切片就是数组的指针。
排序
 ; Go的切片排序是根据类型来排的, GO分别提供了 sort.Ints() 、 sort.Float64s() 和 sort.Strings() 函数, 默认都是从小到大排序。(没有 sort.Float32s() 函数。)
pro03_5_6.go
package mainimport ("fmt""sort")func main() {array := [...]string{"vb", "vc++", "Python", "java", "C lang", "D lang", "Go lang"}slice := array[:]fmt.Println(slice, "ptr(slice) =", &slice[0])sort.Strings(slice)fmt.Println(slice, "ptr(slice) =", &slice[0])}

我们看到切片的地址没有变化,但是值发生了变化。 实际上在发生sort的时候,执行了三个方法 : Len() 求长度、 Less(i,j) 比较第 i 和 第 j 个元素大小的函数、 Swap(i,j) 交换第 i 和第 j 个元素的函数。
在Go默认的排序方式是从小到大,从大到小的方式稍微复杂一点。pro03_5_7.go
array := [...]string{"vb", "vc++", "Python", "java", "C lang", "D lang", "Go lang"}slice := array[:]sort.Strings(slice) //正序排序sort.Sort(sort.Reverse(sort.StringSlice(slice))) //倒序排序
其他类型的如下:
sort.Sort(sort.Reverse(sort.IntSlice(slice)))sort.Sort(sort.Reverse(sort.Float64Slice(slice)))
slice,类似python的list.index()方法
golang里面默认没有这个方法,这个方法就是为了查找slice里面是否包含所查询的参数,如果找到返回这个参数所在的index,未找到返回-1。
具体函数如下:
type Element interface{}// 类似python的list.index()方法,找出slice里面是否包含查询的参数// 返回值是-1表示没有搜索到。func SliceIndex(a Element, i interface{}) (result int) {result = -1if b, ok := a.([]int); ok {if c, ok1 := i.(int); ok1 {for indexC, v := range b {if v == c {result = indexCbreak}}}}if b, ok := a.([]string); ok {if c, ok1 := i.(string); ok1 {for indexC, v := range b {if v == c {result = indexCbreak}}}}if b, ok := a.([]float64); ok {if c, ok1 := i.(float64); ok1 {for indexC, v := range b {if v == c {result = indexCbreak}}}}return}}
会有后续的更改,更改的功能去这里看
一件趣事引发的程序
无意间看到了这样一则新闻:
程序猿招租广告 手机号竟是一串代码日前,有程序员在南京某小区张贴了一份合租广告,表示屋内均是IT行业人士,喜欢安静,所以要求来租者最好是同行或者刚毕业的年轻人。而在联系方式部分,这条广告并没有提供明显的手机号,而是一段代码。。。

这个一看就是第一的C语言练习题目,基本就是Go的slice俩习题呀,我们来做一下:
func main() {var arr = []int{8, 2, 1, 0, 3} //手机号码只是由这5个数字组成var index = []int{2, 0, 3, 2, 4, 0, 1, 3, 2, 3, 3} //手机号码各位的顺序tel := ""for _, i := range index {tel += strconv.Itoa(arr[i])}fmt.Println("联系方式:", tel)}
运行结果如下:
联系方式: 18013820100