C語言中文網 目錄
首頁 > Go語言教程 > Go語言容器 閱讀:6,474

Go語言切片詳解

切片(Slice)是一個擁有相同類型元素的可變長度的序列。Go 語言切片的內部結構包含地址、大小和容量。切片一般用于快速地操作一塊數據集合。如果將數據集合比作切糕的話,切片就是你要的“那一塊”。切的過程包含從哪里開始(這個就是切片的地址)及切多大(這個就是切片的大小)。容量可以理解為裝切片的口袋大小,如下圖所示。


圖:切片結構和內存分配

從數組或切片生成新的切片

切片默認指向一段連續內存區域,可以是數組,也可以是切片本身。

從連續內存區域生成切片是常見的操作,格式如下:

slice [開始位置:結束位置]

  • slice 表示目標切片對象。
  • 開始位置對應目標切片對象的索引。
  • 結束位置對應目標切片的結束索引。

從數組生成切片,代碼如下:
var a  = [3]int{1, 2, 3}
fmt.Println(a, a[1:2])
a 是一個擁有 3 個整型元素的數組,被初始化數值 1 到 3。使用 a[1:2] 可以生成一個新的切片。代碼運行結果如下:

[1 2 3]  [2]

[2] 就是 a[1:2] 切片操作的結果。

從數組或切片生成新的切片擁有如下特性:
  • 取出的元素數量為:結束位置-開始位置。
  • 取出元素不包含結束位置對應的索引,切片最后一個元素使用 slice[len(slice)] 獲取。
  • 當缺省開始位置時,表示從連續區域開頭到結束位置。
  • 當缺省結束位置時,表示從開始位置到整個連續區域末尾。
  • 兩者同時缺省時,與切片本身等效。
  • 兩者同時為0時,等效于空切片,一般用于切片復位。

根據索引位置取切片 slice 元素值時,取值范圍是(0~len(slice)-1),超界會報運行時錯誤。生成切片時,結束位置可以填寫 len(slice) 但不會報錯。

下面在具體的例子中熟悉切片的特性。

1) 從指定范圍中生成切片

切片和數組密不可分。如果將數組理解為一棟辦公樓,那么切片就是把不同的連續樓層出租給使用者。出租的過程需要選擇開始樓層和結束樓層,這個過程就會生成切片。示例代碼如下:
var highRiseBuilding [30]int

for i := 0; i < 30; i++ {
        highRiseBuilding[i] = i + 1
}

// 區間
fmt.Println(highRiseBuilding[10:15])

// 中間到尾部的所有元素
fmt.Println(highRiseBuilding[20:])

// 開頭到中間的所有元素
fmt.Println(highRiseBuilding[:2])
代碼輸出如下:

[11 12 13 14 15]
[21 22 23 24 25 26 27 28 29 30]
[1 2]

代碼中構建了一個 30 層的高層建筑。數組的元素值從 1 到 30,分別代表不同的獨立樓層。輸出的結果是不同租售方案。

代碼說明如下:
  • 第 8 行,嘗試出租一個區間樓層。
  • 第 11 行,出租 20 層以上。
  • 第 14 行,出租 2 層以下,一般是商用鋪面。

切片有點像C語言里的指針。指針可以做運算,但代價是內存操作越界。切片在指針的基礎上增加了大小,約束了切片對應的內存區域,切片使用中無法對切片內部的地址和大小進行手動調整,因此切片比指針更安全、強大。

2) 表示原有的切片

生成切片的格式中,當開始和結束都范圍都被忽略,則生成的切片將表示和原切片一致的切片,并且生成的切片與原切片在數據內容上是一致的,代碼如下:
a := []int{1, 2, 3}
fmt.Println(a[:])
a 是一個擁有 3 個元素的切片。將 a 切片使用 a[:] 進行操作后,得到的切片與 a 切片一致,代碼輸出如下:

[1 2 3]

3) 重置切片,清空擁有的元素

把切片的開始和結束位置都設為 0 時,生成的切片將變空,代碼如下:
a := []int{1, 2, 3}
fmt.Println(a[0:0])
代碼輸出如下:

[]

直接聲明新的切片

除了可以從原有的數組或者切片中生成切片,你也可以聲明一個新的切片。每一種類型都可以擁有其切片類型,表示多個類型元素的連續集合。因此切片類型也可以被聲明。切片類型聲明格式如下:

var name []T

  • name 表示切片類型的變量名。
  • T 表示切片類型對應的元素類型。

下面代碼展示了切片聲明的使用過程:
// 聲明字符串切片
var strList []string

// 聲明整型切片
var numList []int

// 聲明一個空切片
var numListEmpty = []int{}

// 輸出3個切片
fmt.Println(strList, numList, numListEmpty)

// 輸出3個切片大小
fmt.Println(len(strList), len(numList), len(numListEmpty))

// 切片判定空的結果
fmt.Println(strList == nil)
fmt.Println(numList == nil)
fmt.Println(numListEmpty == nil)
代碼輸出結果:

[] [] []
0 0 0
true
true
false

代碼說明如下:
  • 第 2 行,聲明一個字符串切片,切片中擁有多個字符串。
  • 第 5 行,聲明一個整型切片,切片中擁有多個整型數值。
  • 第 8 行,將 numListEmpty 聲明為一個整型切片。本來會在{}中填充切片的初始化元素,這里沒有填充,所以切片是空的。但此時 numListEmpty 已經被分配了內存,但沒有元素。
  • 第 11 行,切片均沒有任何元素,3 個切片輸出元素內容均為空。
  • 第 14 行,沒有對切片進行任何操作,strList 和 numList 沒有指向任何數組或者其他切片。
  • 第 17 行和第 18 行,聲明但未使用的切片的默認值是 nil。strList 和 numList 也是 nil,所以和 nil 比較的結果是 true。
  • 第 19 行,numListEmpty 已經被分配到了內存,但沒有元素,因此和 nil 比較時是 false。

切片是動態結構,只能與nil判定相等,不能互相判等時。

聲明新的切片后,可以使用 append() 函數來添加元素。

使用 make() 函數構造切片

如果需要動態地創建一個切片,可以使用 make() 內建函數,格式如下:

make( []T, size, cap )

  • T:切片的元素類型。
  • size:就是為這個類型分配多少個元素。
  • cap:預分配的元素數量,這個值設定后不影響 size,只是能提前分配空間,降低多次分配空間造成的性能問題。

示例如下:
a := make([]int, 2)
b := make([]int, 2, 10)

fmt.Println(a, b)
fmt.Println(len(a), len(b))
代碼輸出如下:

[0 0] [0 0]
2 2

a 和 b 均是預分配 2 個元素的切片,只是 b 的內部存儲空間已經分配了 10 個,但實際使用了 2 個元素。

容量不會影響當前的元素個數,因此 a 和 b 取 len 都是 2。

溫馨提示

使用 make() 函數生成的切片一定發生了內存分配操作。但給定開始與結束位置(包括切片復位)的切片只是將新的切片結構指向已經分配好的內存區域,設定開始與結束位置,不會發生內存分配操作。

切片不一定必須經過 make() 函數才能使用。生成切片、聲明后使用 append() 函數均可以正常使用切片。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo