既然切片是一个集合,那么自然可以迭代其中的元素。go语言有特殊关键之range,可以配合关键字for来迭代切片里的元素

 

package main

import "fmt"

func main() {
	slice := []int{10, 20, 30, 40}
	for index, value := range slice {
		fmt.Printf("index:%d,value:%d\n", index, value)
	}
}

和数组类似,迭代切片是,range会返回两个值,第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值的一份副本。

range创建的是每个元素的副本,而不是返回对该元素的引用,如下代码所示。

package main

import "fmt"

func main() {
	slice := []int{10, 20, 30, 40}
	for index, value := range slice {
		fmt.Printf("value :%d value-Addr:%x ElemAddr:%x\n", value, &value, &slice[index])
	}
}

创建一个整型切片,其长度和容量都是4个元素。迭代每个元素,并显示值和地址

上面的value-Addr之所以全部一样,是因为range执行时返回的这个value变量实际上是同一个变量,被依次不断赋值输出,所以value的地址总是相同的。因此要想获取每个元素的地址,可以使用切片变量和索引值,如果不用索引值,可以使用空白占位符来忽略

//创建一个整形切片
//其长度和容量都是4个元素
slice :=[]int{10,20,30,40}
//迭代每个元素,并显示其值
for _,value := range slice {
 fmt.printf("value:%d\n",value)
}

关键字总是从切片头部开始迭代的,如果想迭代更过的控制,依旧可以使用传统的for循环

package main

import "fmt"

func main() {
	slice := []int{10, 20, 30, 40}
	for index := 2; index < len(slice); index++ {
		fmt.Printf("index:%d value:%d\n", index, slice[index])
	}
}

上面的代码遇到了一个内置函数,go语言中,有两个特殊的内置函数len() cap()可以用于处理数组,切片,通道。对于切片,函数len()返回切片的长度,函数cap()返回切片的容量。在上面代码里,使用函数len()来决定什么时候停止对切片的迭代

限制容量

在创建切片时,还可以使用之前没有提到的第三个索引选项。第三个索引可以用来控制新切片的容量,其目的并不是要增加容量,而是要限制容量,可以看到允许显示新切片的容量为底层数组提供了一定保护,可以更好的控制追加操作

souce :=[]string{"apple","orange","banana","grape"}

slice  := souce[2:3:4]其长度为1 容量为2个元素

这个切片操作执行后,新切片里从底层数组引用了一个元素,容量是2个元素。

可以应用之前的方法来计算新切片的长度和容量,对于slice[i:j:k]([2:3:4],长度为j-i,容量为k-i

和之前一样,第一个值表示新切片开始的元素的索引位置,这个例子中就是2,第二个值表示开始的索引位置2,加上希望包括的元素的个数1,2+1的结果是3,所以第二个值就是3。为了设置容量,从索引位置2开始,加上希望容量中包含 的元素的个数2,就是第三个值3

如果试图设置的容量比可用的容量还大,就会得到一个语言运行时的错误。

//这个切片操作试图设置容量为4个元素
//这比可用的容量大
slice := souce[2:3:6]

内置函数append()会首先使用可用容量,一旦没有可用容量,会分配一个新的底层数组,这导致很容易忘记切片间正在共享同一个底层数组,一单发生这种情况,对切片进行修改,会出现问题。

对切片内容的修改会影响多个切片,却很难找到问题的原因。如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个append()操作创建新的底层数组与原有的底层数组分离,新切片与原有的底层数组分离后,可以安全的进行后续修改

//创建字符串切片
//其长度和容量都是5个元素
souce  :=[]string{"apple","orange","plum","banana","grape"}
//对第三个元素切片,并限制容量,其长度和容量都是一个元素
slice :=souce[2:3:3]
//向slice追加新字符串
slice =append(slice,"kiwi")

如果不加第三个索引,由于剩余的所有容量都属于slice,向slice追加kiwi元素会改变原有底层数组索引为3的元素的值。不过在上面的代码中限制了slice的容量为1,当第一次对slice调用append()时,会创建一个新的底层数组,这个数组包括2个元素,并返回一个引用了底层数组的新切片。

 因为新的切片slice拥有了自己的底层数组,所以杜绝了可能发生的问题。

内置函数append()也是一个可变参数的函数,可以在一次调用中传递多个追加的值,如果使用...运算符,可以将一个切片的所有元素追加到另一个切片里

package main

import "fmt"

func main() {
	s1 := []int{1, 2}
	s2 := []int{3, 4}
	fmt.Printf("%v\n", append(s1, s2...)) //将两个切片追加在一起并显示
	// fmt.Printf("%v\n", append(s1, s2))
}

就像通过输出看到的那样,切片s2里面的所有值都追加到了切片S1的后面

多维切片

和数组一样,切片一维的。可以组合多个切片形成多维切片。

slice := [][]int{10},{100,200} //创建一个整形切片的切片

一个包含两个元素的外层切片,每个元素包含一个内层的整形切片,外层的切片包含两个元素,每个元素都是一个切片

 第一个元素的中切片使用单个整数10来初始化,第二个元素中的切片包括两个整数,这种组合可以让用户创建非常复杂且强大的数据结构。