上一篇 Go聖經-臨時插入ORM的小trickgolang
數組是一個固定長度類型的序列,由零個或者多個同構元素組成。Go語言不多直接使用數組,通常使用Slice多。數組的表現形式:安全
var q [3]int =[3]int{1,2,3} var r [3]int =[3]int{1,2} var s =[...]int{1,2,3}
數組類型是由類型和長度組成的。所以[3]int和[5]int是兩個不一樣的數據類型app
若是相同數組的元素類型是能夠比較的,則這個數組是能夠比較的。咱們能夠經過==進行數組比較。注意:這裏不是C++,a==b,不是指針比較,而是數組全部元素的比較
。函數
咱們如何比較兩個動態數組是否相等呢?有兩種方法:學習
func compareSlice(a []int, b []int) bool{ if len(a) != len(b) { return false } for i:=0 ;i<len(a); i++{ if a[i]!=b[i]{ return false } } return true }
這裏有必要提一下,爲啥判斷a和b相等, 不比較cap(a)與cap(b)的大小關係呢?是否是比較時,只比較數據呢?優化
注意:不用比較slice的底層數組指針所指向的內存地址, 數據存放確定不在一塊兒的。ui
package main import ( "crypto/sha256" "fmt" ) func main() { c1 := sha256.Sum256([]byte("x")) c2 := sha256.Sum256([]byte("x")) fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1) // Output: // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881 // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881 // true // [32]uint8 }
當調用函數時,調用參數將會被賦值給函數內部的參數變量,因此函數參數接收是一個變量的副本,並非原始的變量。Go語言的數據傳遞所有是值類型。因此數組傳遞是低效的,而且對數組的修改都是發生在拷貝的數組上,對傳入的實參沒有任何影響。如果C++等其餘語言,則數組傳遞時,是傳遞的指針。同時附帶數組的長度信息。若是必定要傳數組,請傳數組指針,例如func(aptr *[32]byte)。.net
一個很形象的例子指針
months := []string{1: "January", /* ... */, 12: "December"}
這裏有個我很懵逼的問題:
var months = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} // 夏天 summer:=months[6:9] fmt.Println(summer[:20]) // slice超出了cap(summer),直接panic。 明白,由於summer容量是12-6=6 fmt.Println(summer[:6]) // [ 7,8,9,10,11,12 ] ,這裏有點懵逼,其實summer[:6]已經超出len(summer)了,我以爲應該是有越界控制的,可是沒有 var months = make([]int, 0, 12) fmt.Println(months[:10]) // [ 0,0,0,0,0,0,0,0,0,0 ] months[0] = 1 // panic: index out of range
主要懵逼的問題:
個人問題是,爲何不作一致性呢?讀寫都控制在len範圍內呢?
由於上面這個問題,咱們要十分當心在slice動態數組讀取的時候,剋制len的範圍,否則就會出現下面這種狀況
// nonempty returns a slice holding only the non-empty strings. // The underlying array is modified during the call. func nonempty(strings []string) []string { i := 0 for _, s := range strings { if s != "" { strings[i] = s i++ } } return strings[:i] } func main() { data := []string{"one", "", "three"} data = nonempty(data) fmt.Println(data[:3]) // [one three three], 這個是不想看到的 fmt.Println(data) // [one three] }
利用slice動態數組,左旋轉slice的前N個元素
package main import "fmt" func reverse(s []int) { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } } func reverseN(s []int, n int) { reverse(s[:n]) // 反轉前N個數 reverse(s[n:]) // 反轉後len(s)-n-1 reverse(s) } func main() { var a = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} var n = 5 // 指定旋轉前N個數 reverseN(a, n) fmt.Println(a) // [5 6 7 8 9 10 11 12 0 1 2 3 4] }
和數組不一樣的是,slice動態數組不能直接比較,不過標準庫提供了bytes.Equal方法判斷兩個字節型slice是否相等。對於其餘咱們須要比較slice的元素個數和元素值。同時咱們又引出struct類型的比較。
type Person struct { Cap int Len int Bt *byte } func main() { p1 := Person{ Cap: 28, Len: 28, Bt: new(byte), } p2 := Person{ Cap: 28, Len: 28, Bt: new(byte), } fmt.Println(p1 == p2) }
上面這個DEMO是能夠編譯經過,且運行沒有問題的,返回false。
可是slice的底層結構和這個是相似的。可是它不能比較,slice是一個特殊類型。儘可能少的考慮它是一個struct類型。同時struct可以進行比較,取決於內部的各個元素是否可以比較。
那爲何不直接slice動態數組比較呢?只須要比較元素個數和每一個元素比較。咱們從《Go聖經-學習筆記入門bufio》, 的bytes.ReadSlice方法就能夠知道line多是間接引用的,它底層的數據可能會隨時發生變化。這樣比較的話是沒有意義的,因此安全的作法是直接禁止slice比較。
在使用slice動態數組的擴容時,咱們常常會用到append函數。它的原型:
func append(src []T, elem ...T) []T
由於cap容量有界的,因此當slice靜態增加到達cap容量後,就須要從新分配必定大小的內存空間,把老的slice動態數組複製到新的slice中,而後再靜態增加。整個流程就是這樣子的。
那麼,append函數就是這個做用,分兩種狀況討論:
內存的動態增加,會產生暫時的內存浪費、以及屢次內存分配致使時效低下。若是咱們一開始分配合理的內存空間大小,既能夠節約內存空間,又能夠優化時間。