數組長度在定義的時候就已經定義好了,且不能夠修改數組
數組長度屬於類型的一部分,因此數組有不少的侷限性app
package main import ( "fmt" "reflect" ) func main() { a := [3]int{1, 2, 3} fmt.Println(reflect.TypeOf(a)) } // [3]int 就是a的類型
切片(Slice)是一個擁有相同類型元素的可變長度的序列函數
切片是基於數組類型作的一層封裝oop
切片很是靈活: 支持自動擴容性能
// var names []T * names是變量名 * T是切片中元素的類型
實際例子指針
package main import "fmt" func main() { // 定義一個名稱爲a,元素類型爲string的切片 var a []string // 定義一個名稱爲b,元素類型爲int的切片,並初始化賦值 b := []int{1, 2, 3, 4, 5} // 定義一個名稱爲c,元素類型爲bool的切片,並初始化賦值 c := []bool{true, false} // 經過make函數構造切片 d := make([]string, 0, 10) fmt.Println(a, b, c, d) }
切片 slice是引用類型,變量不能直接判斷兩個變量是否相等,只有: string、bool、int相關類型、array、struct能夠直接判斷code
若是能夠對切片的容量大小有個概念的話建議使用make,由於他能夠指定容量,目的就是提升性能(由於一旦容量滿了就須要擴容影響性能)索引
make([]T, size, cap)內存
package main import "fmt" func main() { d := make([]string, 0, 100) d = append(d, "alex", "eson") fmt.Println(len(d), cap(d)) }
在就是使用初始化賦值的方式了更直觀一些string
package main import "fmt" func main() { s := []string{"alex", "eson", "eric"} fmt.Println(s) }
切片的本質就是對底層數組的封裝,它包含了三個信息:底層數組的指針、切片的長度(len)和切片的容量(cap)
type slice struct { array unsafe.Pointer len int cap int }
如今我有一個數組,[8]int{0,1,2,3,4,5,6,7}, 那麼新建立一個切片
package main import "fmt" func main() { // s是一個數組 s := [8]int{0, 1, 2, 3, 4, 5, 6, 7} // 切片的本質就是對底層數組的封裝,它包含了三個信息:底層數組的指針、切片的長度(len)和切片的容量(cap) s1 := s[0:5] fmt.Printf("s-type:%T, s1-type:%T\n", s, s1) fmt.Println(s, s1) }
新、增、刪、改、複製、循環、注意事項
package main import "fmt" func main() { // 定義一個名稱爲a,元素類型爲string的切片 var a []string // 定義一個名稱爲b,元素類型爲int的切片,並初始化賦值 b := []int{1, 2, 3, 4, 5} // 定義一個名稱爲c,元素類型爲bool的切片,並初始化賦值 c := []bool{true, false} // 經過make函數構造切片 d := make([]string, 0, 10) fmt.Println(a, b, c, d) }
package main import "fmt" func main() { // 建立一個長度爲0,容量爲1的切片 nums := make([]int, 0, 1) fmt.Printf("nums長度:%d, nums容量:%d, nums內存地址:%p\n", len(nums), cap(nums), nums) for i := 0; i < 10; i++ { nums = append(nums, 1) fmt.Printf("nums長度:%d, nums容量:%d, nums內存地址:%p\n", len(nums), cap(nums), nums) } // 添加多個元素 nums = append(nums, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) fmt.Printf("nums長度:%d, nums容量:%d, nums內存地址:%p\n", len(nums), cap(nums), nums) fmt.Println(nums) }
輸出結果:
nums長度:0, nums容量:1, nums內存地址:0xc0000bc008 nums長度:1, nums容量:1, nums內存地址:0xc0000bc008 nums長度:2, nums容量:2, nums內存地址:0xc0000bc040 nums長度:3, nums容量:4, nums內存地址:0xc0000be040 nums長度:4, nums容量:4, nums內存地址:0xc0000be040 nums長度:5, nums容量:8, nums內存地址:0xc0000b4080 nums長度:6, nums容量:8, nums內存地址:0xc0000b4080 nums長度:7, nums容量:8, nums內存地址:0xc0000b4080 nums長度:8, nums容量:8, nums內存地址:0xc0000b4080 nums長度:9, nums容量:16, nums內存地址:0xc0000c2000 nums長度:10, nums容量:16, nums內存地址:0xc0000c2000 nums長度:23, nums容量:32, nums內存地址:0xc0000c4000 [1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3]
從結果能夠看出:
切片的擴容策略 $GOROOT/src/runtime/slice.go中
newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } }
package main import "fmt" func main() { nums := []int{11, 12, 13, 14, 15} // 切片沒有給刪除元素單獨指定方法,可是能夠經過append以及切片特性來實現: // 刪除索引爲2的元素(索引是0開始的) nums = append(nums[:2], nums[3:]...) fmt.Println(nums) // 輸出結果:[11 12 14 15] }
package main import "fmt" func main() { nums := []int{11, 12, 13, 14, 15} // 修改下標爲1的元素 nums[1] = 111 fmt.Println(nums) // 輸出結果:[11 111 13 14 15] }
由於切片類型的特性,它是一個引用類型,變量指向的並非實際的數據,因此當我複製的時候其實至關於把指針複製了一遍
他們指向了相同的內存
package main import "fmt" func main() { n1 := []int{11, 12, 13, 14, 15} n2 := n1 fmt.Printf("n1的內存地址:%p, n2的內存地址:%p", n1, n2) // 輸出結果: n1的內存地址:0xc000138000, n2的內存地址:0xc000138000 // 同理因此n1和n2是同一個內存指向,修改任意一個都會影響另一個 }
因此須要一個函數來解決:Go語言內建的copy()函數能夠迅速地將一個切片的數據複製到另一個切片空間中
package main import "fmt" func main() { n1 := []int{11, 12, 13, 14, 15} n2 := make([]int, 5, 5) // copy接收兩個參數目標和源 copy(n2, n1) fmt.Printf("n1的內存地址:%p, n2的內存地址:%p\n", n1, n2) // 輸出結果: n1的內存地址:0xc00001c0f0, n2的內存地址:0xc00001c120 // 兩個不一樣的內存 // 如今修改n1和n2就不會互相影響了 n1[0] = 123 n2[0] = 321 fmt.Printf("n1的值:%v n1的內存地址:%p\n", n1, n1) fmt.Printf("n2的值:%v n2的內存地址:%p\n", n2, n2) // 輸出結果: // n1的值:[123 12 13 14 15] n1的內存地址:0xc00001c0f0 // n2的值:[321 12 13 14 15] n2的內存地址:0xc00001c120 }
package main import "fmt" func main() { n1 := []int{11, 12, 13, 14, 15} // 第一種循環經過切片長度 for i := 0; i < len(n1); i++ { fmt.Printf("n1的當前下標是:%d, n1當前下標元素值是: %d\n", i, n1[i]) } // 第二種經過range for index, value := range n1 { fmt.Printf("n1的當前下標是:%d, n1當前下標元素值是: %d\n", index, value) } }