日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
復合數據類型:數組和切片Slice

公司 Tony 老師這兩天請假,找來了他的好朋友 Kevin 頂班,這兩個人的風格真是相差十萬八千里。

10年積累的成都網站制作、網站設計經驗,可以快速應對客戶對網站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網絡服務。我雖然不認識你,你也不認識我。但先網站設計后付款的網站建設流程,更有柯城免費網站建設讓你可以放心的選擇與我們合作。

Tony 性格緩慢,手法輕柔。到底有多輕呢?洗頭發(fā)的時候我都懷疑他是不是怕把我頭發(fā)弄濕。

Kevin 則完全不同,嗓音洪亮,風風火火。說是洗頭發(fā),但我感覺他就是在扇我腦袋。眼前水花四濺,霧氣繚繞,仿佛都能看見彩虹。

理發(fā)的小感受,夸張了點兒。

經過上一篇的學習,對 Go 應該已經越來越有感覺了,今天來點更高級的內容:復雜數據類型。

本篇主要介紹數組和切片 slice,開整~

數組

數組有兩個特點:

  • 固定長度
  • 元素類型相同

正是因為其長度固定,所以相比于切片,在開發(fā)過程中用的是比較少的。但數組是切片的基礎,理解了數組,再學習切片就容易多了。

聲明和初始化

聲明一個長度是 3,元素類型是 int 的數組。通過索引來訪問數組元素,索引從 0 到數組長度減 1,內置函數 len 可以獲取數組長度。

 
 
 
 
  1. var a [3]int
  2. // 輸出數組第一個元素
  3. fmt.Println(a[0]) // 0
  4. // 輸出數組長度
  5. fmt.Println(len(a)) // 3

數組初始值為元素類型零值,也可以用數組字面量初始化數組。

 
 
 
 
  1. // 數組字面量初始化
  2. var b [3]int = [3]int{1, 2, 3}
  3. var c [3]int = [3]int{1, 2}
  4. fmt.Println(b)    // [1 2 3]
  5. fmt.Println(c[2]) // 0

如果沒有顯示指定數組長度,而是用 ...,那么數組長度由實際的元素數量決定。

 
 
 
 
  1. // 使用 ...
  2. d := [...]int{1, 2, 3, 4, 5}
  3. fmt.Printf("%T\n", d) // [5]int

還可以指定索引位置來初始化,如果沒有指定數組長度,則長度由索引來決定。

 
 
 
 
  1. // 指定索引位置初始化
  2. e := [4]int{5, 2: 10}
  3. f := [...]int{2, 4: 6}
  4. fmt.Println(e) // [5 0 10 0]
  5. fmt.Println(f) // [2 0 0 0 6]

多維數組

多維數組的聲明和初始化同理,這里以二維數組來舉例說明,有一點需要注意,多維數組僅第一維允許使用 ...。

 
 
 
 
  1. // 二維數組
  2. var g [4][2]int
  3. h := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
  4. // 聲明并初始化外層數組中索引為 1 和 3 的元素
  5. i := [4][2]int{1: {20, 21}, 3: {40, 41}}
  6. // 聲明并初始化外層數組和內層數組的單個元素
  7. j := [...][2]int{1: {0: 20}, 3: {1: 41}}
  8. fmt.Println(g, h, i, j)

使用數組

只要數組元素是可比較的,那么數組就是可比較的,而且數組長度也是數組類型的一部分。

所以 [3]int 和 [4]int 是兩種不同的類型。

 
 
 
 
  1. // 數組比較
  2. a1 := [2]int{1, 2}
  3. a2 := [...]int{1, 2}
  4. a3 := [2]int{1, 3}
  5. // a4 := [3]int{1, 2}
  6. fmt.Println(a1 == a2, a1 == a3, a2 == a3) // true false false
  7. // fmt.Println(a1 == a4)                     // invalid operation: a1 == a4 (mismatched types [2]int and [3]int)

數組遍歷:

 
 
 
 
  1. // 數組遍歷
  2. for i, n := range e {
  3.     fmt.Println(i, n)
  4. }

值類型

Go 數組是值類型,賦值和傳參都會復制整個數組。

從輸出結果可以看出來,內容都是相同的,但地址不同。

 
 
 
 
  1. package main
  2. import "fmt"
  3. func main() {
  4.     // 數組復制
  5.     x := [2]int{10, 20}
  6.     y := x
  7.     fmt.Printf("x: %p, %v\n", &x, x) // x: 0xc00012e020, [10 20]
  8.     fmt.Printf("y: %p, %v\n", &y, y) // y: 0xc00012e030, [10 20]
  9.     test(x)
  10. }
  11. func test(a [2]int) {
  12.     fmt.Printf("a: %p, %v\n", &a, a) // a: 0xc00012e060, [10 20]
  13. }

再來看看函數傳參的情況:

 
 
 
 
  1. package main
  2. import "fmt"
  3. func main() {
  4.     x := [2]int{10, 20}
  5.     // 傳參
  6.     modify(x)
  7.     fmt.Println("main: ", x) // main:  [10 20]
  8. }
  9. func modify(a [2]int) {
  10.     a[0] = 30
  11.     fmt.Println("modify: ", a) // modify:  [30 20]
  12. }

同樣從結果可以看到,modify 中數組內容修改后,main 中數組內容并沒有變化。

那么,有沒有可能在函數內修改,而影響到函數外呢?答案是可以的,接下來要說的切片就可以做到。

切片 slice

切片是一種引用類型,它有三個屬性:指針,長度和容量。

  1. 指針:指向 slice 可以訪問到的第一個元素。
  2. 長度:slice 中元素個數。
  3. 容量:slice 起始元素到底層數組最后一個元素間的元素個數。

看到這樣的解釋是不是一臉懵呢?別慌,咱們來詳細解釋一下。

它的底層結構是這樣的:

再來看一個例子,看看到底各部分都是什么意思。

底層是一個包含 10 個整型元素的數組,data1 指向數組第 4 個元素,長度是 3,容量取到數組最后一個元素,是 7。data2 指向數組第 5 個元素,長度是 4,容量是 6。

創(chuàng)建切片

創(chuàng)建切片有兩種方式:

第一種方式是基于數組創(chuàng)建:

 
 
 
 
  1. // 基于數組創(chuàng)建切片
  2. var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8}
  3. s1 := array[3:6]
  4. s2 := array[:5]
  5. s3 := array[4:]
  6. s4 := array[:]
  7. fmt.Printf("s1: %v\n", s1) // s1: [4 5 6]
  8. fmt.Printf("s2: %v\n", s2) // s2: [1 2 3 4 5]
  9. fmt.Printf("s3: %v\n", s3) // s3: [5 6 7 8]
  10. fmt.Printf("s4: %v\n", s4) // s4: [1 2 3 4 5 6 7 8]

第二種方式是使用內置函數 make 來創(chuàng)建:

 
 
 
 
  1. // 使用 make 創(chuàng)建切片
  2. // len: 10, cap: 10
  3. a := make([]int, 10)
  4. // len: 10, cap: 15
  5. b := make([]int, 10, 15)
  6. fmt.Printf("a: %v, len: %d, cap: %d\n", a, len(a), cap(a))
  7. fmt.Printf("b: %v, len: %d, cap: %d\n", b, len(b), cap(b))

使用切片

遍歷

和遍歷數組方法相同。

 
 
 
 
  1. // 切片遍歷
  2. for i, n := range s1 {
  3.     fmt.Println(i, n)
  4. }

比較

不能使用 == 來測試兩個 slice 是否有相同元素,但 slice 可以和 nil 比。slice

類型的零值是 nil,表示沒有對應的底層數組,而且長度和容量都是零。

但也要注意,長度和容量都是零的,其值也并不一定是 nil。

 
 
 
 
  1. // 比較
  2. var s []int
  3. fmt.Println(len(s) == 0, s == nil) // true true
  4. s = nil
  5. fmt.Println(len(s) == 0, s == nil) // true true
  6. s = []int(nil)
  7. fmt.Println(len(s) == 0, s == nil) // true true
  8. s = []int{}
  9. fmt.Println(len(s) == 0, s == nil) // true false

所以,判斷 slice 是否為空,要用內置函數 len,而不是判斷其是否為 nil。

追加元素

使用內置函數 append。

 
 
 
 
  1. // 追加
  2. s5 := append(s4, 9)
  3. fmt.Printf("s5: %v\n", s5) // s5: [1 2 3 4 5 6 7 8 9]
  4. s6 := append(s4, 10, 11)
  5. fmt.Printf("s6: %v\n", s6) // s5: [1 2 3 4 5 6 7 8 10 11]

追加另一個切片,需要在另一個切片后面跟三個點。

 
 
 
 
  1. // 追加另一個切片
  2. s7 := []int{12, 13}
  3. s7 = append(s7, s6...)
  4. fmt.Printf("s7: %v\n", s7) // s7: [12 13 1 2 3 4 5 6 7 8 10 11]

復制

 
 
 
 
  1. // 復制
  2. s8 := []int{1, 2, 3, 4, 5}
  3. s9 := []int{5, 4, 3}
  4. s10 := []int{6}
  5. copy(s8, s9)
  6. fmt.Printf("s8: %v\n", s8) // s8: [5 4 3 4 5]
  7. copy(s10, s9)
  8. fmt.Printf("s10: %v\n", s10) // s10: [5]

引用類型

上文介紹數組時說過,數組屬于值類型,所以在傳參時會復制整個數組內容,如果數組很大的話,是很影響性能的。而傳遞切片只會復制切片本身,并不影響底層數組,是很高效的。

 
 
 
 
  1. package main
  2. import "fmt"
  3. func main() {
  4.     s9 := []int{5, 4, 3}
  5.     // 傳參
  6.     modify(s9)
  7.     fmt.Println("main: ", s9) // main:  [30 4 3]
  8. }
  9. func modify(a []int) {
  10.     a[0] = 30
  11.     fmt.Println("modify: ", a) // modify:  [30 4 3]
  12. }

在 modify 中修改的值會影響到 main 中。

總結

本文學習了復合數據類型的前兩種:數組和切片。分別介紹了它們的創(chuàng)建,常用操作,以及函數間的傳遞。

數組長度固定,是切片的基礎;切片長度可變,多一個容量屬性,其指針指向的底層結構就是數組。

在函數傳參過程中,數組如果很大的話,很影響效率,而切片則解決了這個問題,效率更高。

在日常開發(fā)中,使用切片的頻率會更高一些。

本文轉載自微信公眾號「AlwaysBeta」,可以通過以下二維碼關注。轉載本文請聯系AlwaysBeta公眾號。


名稱欄目:復合數據類型:數組和切片Slice
URL網址:http://www.dlmjj.cn/article/coehhes.html