切片
go語言中的切片,類似於動態陣列的概念。因為陣列的長度不可變,那麼對於可變長度的資料,就需要使用切片來儲存。需要注意的是,切片的底層任然是使用陣列來儲存的,只不過切片當中有一個變數,是指向底層陣列的指標(學過C語言的同學應該知道指標的概念)。
切片的資料結構
|-----|-----|-----|
| 地址指標 |長度 | 容量 |
包括3個屬性,第一個是指向底層儲存資料的陣列的指標,第二個是當前切片的長度,第三個是切片最大可以容納的元素個數。
切片宣告
使用make函式建立切片:
slice := make([]string,5)
複製程式碼
上面的程式碼表示建立一個字串切片,切片的長度和容量都是5個元素。
- 長度就是當前切片中擁有的元素個數
- 容量表示切片最大容許擁有的元素個數
可以分別指定長度和容量大小
slice := make([]string,5,10)
複製程式碼
上面的程式碼表示,建立一個大小為5,容量為10的字串切片。
注意:長度不能大於容量,否則會在編譯的時候報錯。
切片字面量
另一種宣告切片的方式是使用切片字面量:
slice := []string{"one","two","three"}
複製程式碼
上面的程式碼表示,建立一個長度和容量都為3的字串切片。這種方式和陣列的唯一區別就是[]中沒有數字,如果裡面指定了數字的話,建立的就是陣列,而不是切片。
slice := []bool{false,true,false}
複製程式碼
上面的程式碼表示,建立一個長度和容量都為3的boolean切片。
空切片和nil切片
package main
import (
"fmt"
)
func main(){
var slice []string
fmt.Println(slice == nil)
}
//true
複製程式碼
上面的程式碼表示建立一個nil切片,此時slice的值為nil,表示變數宣告瞭但是沒有初始化。
package main
import (
"fmt"
)
func main(){
slice := []string{}
fmt.Println(slice == nil)
}
//false
複製程式碼
上面的程式碼表示建立了一個空切片,空切片的長度和容量都是0,但是slice的值
package main
import (
"fmt"
)
func main(){
slice := []string{}
slice = append(slice,"one")
fmt.Println(slice)
}
//[one]
複製程式碼
package main
import (
"fmt"
)
func main(){
var slice []string
slice = append(slice,"one")
fmt.Println(slice)
}
//[one]
複製程式碼
看上面的兩段程式碼,奇怪的是,nil切片和空切片都可以使用append函式對切片新增元素,但是在上面的程式碼中nil的切片slice=nil,而空切片slice!=nil。這通常可以理解為nil切片表示一個不存在的切片,而空切片表示的是切片存在,但是切片不包含任何元素。然而對於兩者的append操作,效果卻是一樣的。
使用切片
slice := []string{"one","two"}
slice[0] = "newOne"
複製程式碼
上面的程式碼表示修改切片中第一個元素的值為"newOne"。會發現切片修改和陣列的修改形式是一樣的。更加體現了切片的底層是陣列的這個設計。
slice[3] = "three"
//runtime error: index out of range
複製程式碼
嘗試將上面的切片修改第4個值,會出錯。因為上面的切片長度和容量都是2。
package main
import (
"fmt"
)
func main(){
slice := []string{"one","two"}
slice[0] = "newOne"
//使用append增加元素
slice = append(slice,"three")
fmt.Println(slice)
}
//[one two three]
複製程式碼
上面的程式碼表示向slice中新增一個元素。此時切片的長度=3,容量=4。
注意:在向切片中追加元素的時候,如果切片容量不夠,會自動擴充切片的容量,當切片的容量小於1000的時候,總是會成倍的增加容量,如果超過1000,則會在原來容量的基礎上乘以1.25。
package main
import (
"fmt"
)
func main(){
slice1 := []int{1,2,3,4}
slice2 := []int{5,6,7,8}
//將切片slice2中的所有元素追加到slice1中
slice1 = append(slice1,slice2...)
fmt.Println(slice1)
}
//[1 2 3 4 5 6 7 8]
複製程式碼
看到上面的程式碼,有沒有發現和js的陣列的concat操作是一樣的效果?
從切片建立切片
package main
import (
"fmt"
)
func main(){
slice := []int{1,2,3,4,5}
//從slice中建立一個新的切片
slice2 := slice[0:2:3]
fmt.Println(slice2)
}
//[1 2]
複製程式碼
上面的程式碼表示從切片中建立切片,slice[0:2:3]第一個0表示起始位置,第二個2表示從新切片中包含的元素個數,第三個3表示新切片的容量。第三個值也可以不指定,如果不指定,新切片的容量=slice切片的容量-起始位置(這裡是0)
切片遍歷
package main
import (
"fmt"
)
func main(){
slice1 := []int{1,2,3,4}
slice2 := []int{5,6,7,8}
slice1 = append(slice1,slice2...)
for index , value := range slice1{
fmt.Println("index=",index,",value=",value)
}
}
// index= 0 ,value= 1
// index= 1 ,value= 2
// index= 2 ,value= 3
// index= 3 ,value= 4
// index= 4 ,value= 5
// index= 5 ,value= 6
// index= 6 ,value= 7
// index= 7 ,value= 8
複製程式碼
上面的程式碼使用range關鍵字來遍歷切片,range在每一次的迭代中返回兩個變數,第一個表示當前操作的元素的位置索引,第二個表示當前的元素值。
package main
import (
"fmt"
)
func main(){
slice1 := []int{1,2,3,4}
slice2 := []int{5,6,7,8}
slice1 = append(slice1,slice2...)
for index :=0 ; index < len(slice1) ; index++{
fmt.Println("index=",index,",value=",slice1[index])
}
}
// index= 0 ,value= 1
// index= 1 ,value= 2
// index= 2 ,value= 3
// index= 3 ,value= 4
// index= 4 ,value= 5
// index= 5 ,value= 6
// index= 6 ,value= 7
// index= 7 ,value= 8
複製程式碼
上面的程式碼使用傳統的for迴圈來遍歷切片。