GO中?分組聲明與array,?slice,?map函數(shù)
前言:
在 Go 語言中,同時聲明多個常量、變量,或者導(dǎo)入多個包時,可采用分組的方式進(jìn)行聲明。
例如下面的代碼:
import "fmt" import "os" const i = 100 const pi = 3.1415 const prefix = "Go_" var i int var pi float32 var prefix string1
可以改成下面的方式:
import( ? ? "fmt" ? ? "os" ) const( ? ? i = 100 ? ? pi = 3.1415 ? ? prefix = "Go_" ) var( ? ? i int ? ? pi float32 ? ? prefix string )
iota 枚舉
Go 里面有一個關(guān)鍵字 iota,這個關(guān)鍵字用來聲明 enum 的時候采用,它默認(rèn)開始值是 0,每調(diào)用一次加 1:
const( ? ? x = iota // x == 0 ? ? y = iota // y == 1 ? ? z = iota // z == 2 ? ? w // 常量聲明省略值時,默認(rèn)和之前一個值的字面相同。 ? ? ? //這里隱式地說 w = ? ? ? //iota,因此 w == 3。其實(shí)上面 y 和 z 可同樣不用"= iota" )
注:const v = iota // 每遇到一個 const 關(guān)鍵字,iota 就會重置,此時 v == 0。
Go 程序設(shè)計的一些規(guī)則
Go 之所以會那么簡潔,是因?yàn)樗幸恍┠J(rèn)的行為:
- 大寫字母開頭的變量是可導(dǎo)出的,也就是其它包可以讀取的,是公用變量;小寫字母開頭的就是不可導(dǎo)出的,是私有變量
- 大寫字母開頭的函數(shù)也是一樣,相當(dāng)于
class
中的帶 public 關(guān)鍵詞的公有函數(shù);小寫字母開頭的就是有 private 關(guān)鍵詞的私有函數(shù)。
數(shù)組
array 就是數(shù)組,它的定義方式如下:
var arr [n]type
在[n]type
中,n 表示數(shù)組的長度,type 表示存儲元素的類型。對數(shù)組的操作和其它語言類似,都是通過[]來進(jìn)行讀取或賦值:
var arr [10]int // 聲明了一個 int 類型的數(shù)組 arr[0] = 42 // 數(shù)組下標(biāo)是從 0 開始的 arr[1] = 13 // 賦值操作 fmt.Printf("The first element is %d\n", arr[0]) // 獲取數(shù)據(jù),返回 42 fmt.Printf("The last element is %d\n", arr[9]) //返回未賦值的最后一個元素,默認(rèn)返回 0
由于長度也是數(shù)組類型的一部分,因此[3]int 與[4]int 是不同的類型,數(shù)組也就不能改變長度。
數(shù)組之間的賦值是值的賦值,即當(dāng)把一個數(shù)組作為參數(shù)傳入函數(shù)的時候,傳入的其實(shí)是該數(shù)組的副本,而不是它的指針。
如果要使用指針,那么就需要用到后面介紹的 slice 類型了。
數(shù)組可以使用另一種:=來聲明。
a := [3]int{1, 2, 3} // 聲明了一個長度為 3 的 int 數(shù)組 b := [10]int{1, 2, 3} // 聲明了一個長度為 10 的 int 數(shù)組,其中前三個元素初始化為 1、2、3,其它默認(rèn) 為 0 c := [...]int{4, 5, 6} // 可以省略長度而采用`...`的方式,Go 會自動根據(jù)元素個數(shù)來計算長度
也許你會說,我想數(shù)組里面的值還是數(shù)組,能實(shí)現(xiàn)嗎?
當(dāng)然咯,Go 支持嵌套數(shù)組,即多維 數(shù)組。
比如下面的代碼就聲明了一個二維數(shù)組:
// 聲明了一個二維數(shù)組,該數(shù)組以兩個數(shù)組作為元素,其中每個數(shù)組中又有 4 個 int 類型的元素 doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} // 如果內(nèi)部的元素和外部的一樣,那么上面的聲明可以簡化,直接忽略內(nèi)部的 類型 easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
數(shù)組的分配如下所示:
切片
在很多應(yīng)用場景中,數(shù)組并不能滿足我們的需求。在初始定義數(shù)組時,我們并不知道需要多大的數(shù)組,因此我們就需要“動態(tài)數(shù)組”。在 Go 里面這種數(shù)據(jù)結(jié)構(gòu)叫 slice , 翻譯過來就是切片的意思,大白話就是切成一片一片的:
- slice 并不是真正意義上的動態(tài)數(shù)組,而是一個引用類型。
- slice 總是指向一個底層array。
- slice 的聲明也可以像 array 一樣,只是不需要長度。
// 和聲明 array 一樣,只是少了長度 var fslice []int
接下來我們可以聲明一個 slice,并初始化數(shù)據(jù),如下所示:
slice := []byte {'a', 'b', 'c', 'd'}
slice
可以從一個數(shù)組或一個已經(jīng)存在的 slice 中再次聲明。- slice 通過 array[i:j]來獲取,其中 i 是數(shù)組的開始位置,j 是結(jié)束位置,但不包含 array[j],即[i,j),前包括后不包括, 它的長度是 j-i。
// 聲明一個含有 10 個元素元素類型為 byte 的數(shù)組 var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 聲明兩個含有 byte 的 slice var a, b []byte // a 指向數(shù)組的第 3 個元素開始,并到第五個元素結(jié)束, a = ar[2:5] //現(xiàn)在 a 含有的元素: ar[2]、ar[3]和 ar[4] // b 是數(shù)組 ar 的另一個 slice b = ar[3:5] // b 的元素是:ar[3]和 ar[4]
注意 slice 和數(shù)組在聲明時的區(qū)別:
- 聲明數(shù)組時,方括號內(nèi)寫明了數(shù)組的長度或使用...。
- 自動計算長度,而聲明 slice 時,方括號內(nèi)沒有任何字符。
它們的數(shù)據(jù)結(jié)構(gòu)如下所示:
slice 有一些簡便的操作:
- slice 的默認(rèn)開始位置是 0,ar[:n]等價于 ar[0:n]。
- slice 的第二個序列默認(rèn)是數(shù)組的長度,ar[n:]等價于ar[n:len(ar)]。
- 如果從一個數(shù)組里面直接獲取 slice,可以這樣 ar[:],因?yàn)槟J(rèn)第一個序列是 0,第 二個是數(shù)組的長度,即等價于 ar[0:len(ar)]。
下面這個例子展示了更多關(guān)于 slice 的操作:
// 聲明一個數(shù)組 var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 聲明兩個 slice var aSlice, bSlice []byte // 演示一些簡便操作 aSlice = array[:3] // 等價于 aSlice = array[0:3] aSlice 包含元素: a,b,c aSlice = array[5:] // 等價于 aSlice = array[5:10] aSlice 包含元素: f,g,h,i,j aSlice = array[:] // 等價于 aSlice = array[0:10] 這樣 aSlice 包含了全部的元素 // 從 slice 中獲取 slice aSlice = array[3:7] // aSlice 包含元素: d,e,f,g,len=4,cap=7 bSlice = aSlice[1:3] // bSlice 包含 aSlice[1], aSlice[2] 也就是含有: e,f bSlice = aSlice[:3] // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是 含有: d,e,f bSlice = aSlice[0:5] // 對 slice 的 slice 可以在 cap 范圍內(nèi)擴(kuò)展,此時 bSlice 包含:d,e,f,g,h bSlice = aSlice[:] // bSlice 包含所有 aSlice 的元素: d,e,f,g
slice
是引用類型,所以當(dāng)引用改變其中元素的值時,其它的所有引用都會改變該值,例如上面的aSlice 和bSlice,如果修改了aSlice中元素的值,那么 bSlice相對應(yīng)的值也會改變。 從概念上面來說 slice像一個結(jié)構(gòu)體,這個結(jié)構(gòu)體包含了三個元素:
- 一個指針,指向數(shù)組中slice指定的開始位置。
- 長度,即 slice 的長度。
- 最大長度,也就是 slice 開始位置到數(shù)組的最后位置的長度。
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} Slice_a := Array_a[2:5]
上面代碼的真正存儲結(jié)構(gòu)如下圖所示:
對于 slice 有幾個有用的內(nèi)置函數(shù):
- len 獲取 slice 的長度。
- cap 獲取 slice 的最大容量。
- append 向 slice 里面追加一個或者多個元素,然后返回一個和 slice 一樣類型的slice。
- copy 函數(shù) copy 從源 slice 的 src 中復(fù)制元素到目標(biāo) dst,并且返回復(fù)制的元素的個數(shù)。
注:append 函數(shù)會改變 slice 所引用的數(shù)組的內(nèi)容,從而影響到引用同一數(shù)組的其它 slice。
但當(dāng) slice 中沒有剩余空間(即(cap-len) == 0)時,此時將動態(tài)分配新的數(shù)組空間。
返回的slice 數(shù)組指針將指向這個空間,而原數(shù)組的內(nèi)容將保持不變;
其它引用此數(shù)組的 slice 則不受影響。
map
map 也就是 Python 中字典的概念,它的格式為 map[keyType]valueType我們看下面的代碼,map 的讀取和設(shè)置也類似 slice 一樣,通過 key 來操作,只是 slice 的index只能是int類型,而 map 多了很多類型,可以是 int,可以是 string 及所有完全定
義了==與!=操作的類型。
// 聲明一個 key 是字符串,值為 int 的字典,這種方式的聲明需要在使用之前使用 make 初始化 var numbers map[string] int // 另一種 map 的聲明方式 numbers := make(map[string]int) numbers["one"] = 1 //賦值 numbers["ten"] = 10 //賦值 numbers["three"] = 3 fmt.Println("第三個數(shù)字是: ", numbers["three"]) // 讀取數(shù)據(jù) // 打印出來如:第三個數(shù)字是: 3
這個 map 就像我們平常看到的表格一樣,左邊列是 key,右邊列是值使用 map 過程中需要注意的幾點(diǎn):
- map 是無序的,每次打印出來的 map 都會不一樣,它不能通過 index 獲取,而必須通過 key 獲取。
- map 的長度是不固定的,也就是和 slice 一樣,也是一種引用類型。
- 內(nèi)置的 len 函數(shù)同樣適用于 map,返回 map 擁有的 key 的數(shù)量。
- map 的值可以很方便的修改,通過 numbers["one"]=11 可以很容易的把 key 為 one 的字典值改為 11。
map 的初始化可以通過 key:val 的方式初始化值,同時 map 內(nèi)置有判斷是否存在 key 的方式,通過 delete 刪除 map 的元素:
// 初始化一個字典 rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 } // map 有兩個返回值,第二個返回值,如果不存在 key,那么 ok 為 false,如果存在 ok 為 true csharpRating, ok := rating["C#"] if ok { ? ? fmt.Println("C# is in the map and its rating is ", csharpRating) } else { ? ? fmt.Println("We have no rating associated with C# in the map") } delete(rating, "C") // 刪除 key 為 C 的元素
上面說過了,map 也是一種引用類型,如果兩個 map 同時指向一個底層,那么一個改變, 另一個也相應(yīng)的改變:
m := make(map[string]string) m["Hello"] = "Bonjour" m1 := m m1["Hello"] = "Salut" // 現(xiàn)在 m["hello"]的值已經(jīng)是 Salut 了
make、new 操作
- make用于內(nèi)建類型(map、slice 和 channel)的內(nèi)存分配。
- new 用于各種類型的內(nèi)存分配。
- 內(nèi)建函數(shù) new 本質(zhì)上說跟其它語言中的同名函數(shù)功能一樣:new(T)分配了零值填充的 T 類型的內(nèi)存空間,并且返回其地址,即一個*T類型的值。用 Go 的術(shù)語說,它返回了一個指針,指向新分配的類型 T的零值。
- 有一點(diǎn)非常重要:new 返回指針。
- 內(nèi)建函數(shù)make(T, args)與 new(T)有著不同的功能,make 只能創(chuàng)建 slice、map 和 channel,并且返回一個有初始值(非零)的 T 類型,而不是*T。
- 本質(zhì)來講,導(dǎo)致這三個類型有所不同的 原因是指向數(shù)據(jù)結(jié)構(gòu)的引用在使用前必須被初始化。例如,一個 slice,是一個包含指向數(shù) 據(jù)(內(nèi)部 array)的指針、長度和容量的三項(xiàng)描述符;在這些項(xiàng)目被初始化之前,slice為nil。
- 對于 slice、map 和 channel 來說,make初始化了內(nèi)部的數(shù)據(jù)結(jié)構(gòu),填充適當(dāng)?shù)闹怠?/li>
- make 返回初始化后的(非零)值。下面這個圖詳細(xì)的解釋了 new 和 make 之間的區(qū)別。
關(guān)于“零值”,所指并非是空值,而是一種“變量未填充前”的默認(rèn)值,通常為0。 此處羅列部分類型 的“零值”。
int ? ? 0 int8 ? ? 0 int32 ? ? 0 int64 ? ? 0 uint ? ? 0x0 rune ? ? 0 //rune 的實(shí)際類型是 int32 byte ? ? 0x0 // byte 的實(shí)際類型是 uint8 float32 0 //長度為 4 byte float64 0 //長度為 8 byte bool ? ? false string ? ? ""
到此這篇關(guān)于GO中 分組聲明與array, slice, map函數(shù)的文章就介紹到這了,更多相關(guān)GO分組聲明與array, slice, map內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- go語言中切片Slice與數(shù)組Array對比以及panic:?runtime?error:?index?out?of?range問題解決
- Golang?range?slice?與range?array?之間的區(qū)別
- Golang 拷貝Array或Slice的操作
- Go 修改map slice array元素值操作
- Golang學(xué)習(xí)筆記(四):array、slice、map
- Go語言入門教程之Arrays、Slices、Maps、Range操作簡明總結(jié)
- 理解Golang中的數(shù)組(array)、切片(slice)和map
- Go語言中的Array、Slice、Map和Set使用詳解
- GO語言的數(shù)組array與切片slice詳解
相關(guān)文章
Go語言實(shí)現(xiàn)的簡單網(wǎng)絡(luò)端口掃描方法
這篇文章主要介紹了Go語言實(shí)現(xiàn)的簡單網(wǎng)絡(luò)端口掃描方法,實(shí)例分析了Go語言網(wǎng)絡(luò)程序的實(shí)現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-02-02利用Go語言實(shí)現(xiàn)簡單Ping過程的方法
相信利用各種語言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過程,而對Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計。2016-09-09