既然切片是一个集合,那么自然可以迭代其中的元素。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来初始化,第二个元素中的切片包括两个整数,这种组合可以让用户创建非常复杂且强大的数据结构。