您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“go語(yǔ)言有哪些類(lèi)型及怎么表示”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“go語(yǔ)言有哪些類(lèi)型及怎么表示”吧!
go語(yǔ)言的類(lèi)型有布爾型(bool)、整型(int8、byte、int16等)、浮點(diǎn)型(float32、float64)、復(fù)數(shù)類(lèi)型(complex64、complex128)、字符串、字符型、錯(cuò)誤型、指針、數(shù)組、切片、字典、通道、結(jié)構(gòu)體、接口。浮點(diǎn)類(lèi)型采用IEEE-754標(biāo)準(zhǔn)的表達(dá)方式;復(fù)數(shù)實(shí)際上由兩個(gè)實(shí)數(shù)(在計(jì)算機(jī)中用浮點(diǎn)數(shù)表示)構(gòu)成,一個(gè)表示實(shí)部,一個(gè)表示虛部。
Go語(yǔ)言?xún)?nèi)置以下這些基礎(chǔ)類(lèi)型:
布爾類(lèi)型:bool。
整型:int8、byte、int16、int、uint、uintptr等。
浮點(diǎn)類(lèi)型:float32、float64。
復(fù)數(shù)類(lèi)型:complex64、complex128。
字符串:string。
字符類(lèi)型:rune。
錯(cuò)誤類(lèi)型:error。
此外,Go語(yǔ)言也支持以下這些復(fù)合類(lèi)型:
指針(pointer)
數(shù)組(array)
切片(slice)
字典(map)
通道(chan)
結(jié)構(gòu)體(struct)
接口(interface)
在這些基礎(chǔ)類(lèi)型之上Go還封裝了下面這幾種類(lèi)型:
int、uint和uintptr等。這些類(lèi)型的特點(diǎn)在于使用方便,但使用者不能對(duì)這些類(lèi)型的長(zhǎng)度做任何假設(shè)。對(duì)于常規(guī)的開(kāi)發(fā)來(lái)說(shuō),用int和uint就可以了,沒(méi)必要用int8之類(lèi)明確指定長(zhǎng)度的類(lèi)型,以免導(dǎo)致移植困難?!鞠嚓P(guān)推薦:Go視頻教程】
Go 語(yǔ)言中以 bool 類(lèi)型進(jìn)行聲明布爾類(lèi)型數(shù)據(jù),布爾型數(shù)據(jù)只有 true
和 false 兩個(gè)值,需要注意的是:
go 語(yǔ)言中不允許將整型強(qiáng)制轉(zhuǎn)換為布爾型
布爾類(lèi)型變量默認(rèn)值為 false
布爾型無(wú)法參與數(shù)值運(yùn)算,也無(wú)法與其他類(lèi)型進(jìn)行轉(zhuǎn)換
Go語(yǔ)言中的布爾類(lèi)型與其他語(yǔ)言基本一致,關(guān)鍵字也為bool,可賦值為預(yù)定義的true和false示例代碼如下:
var v1 bool
v1 = true
v2 := (1 == 2) // v2也會(huì)被推導(dǎo)為bool類(lèi)型
布爾類(lèi)型不能接受其他類(lèi)型的賦值,不支持自動(dòng)或強(qiáng)制的類(lèi)型轉(zhuǎn)換。
以下的示例是一些錯(cuò)誤的用法,會(huì)導(dǎo)致編譯錯(cuò)誤:
var b bool
b = 1 // 編譯錯(cuò)誤
b = bool(1) // 編譯錯(cuò)誤
以下的用法才是正確的:
var b bool
b = (1!=0) // 編譯正確
fmt.Println("Result:", b) // 打印結(jié)果為Result: true
整型是所有編程語(yǔ)言里最基礎(chǔ)的數(shù)據(jù)類(lèi)型。Go語(yǔ)言支持表2-1所示的這些整型類(lèi)型。
1. 類(lèi)型表示
需要注意的是,int和int32在Go語(yǔ)言里被認(rèn)為是兩種不同的類(lèi)型,編譯器也不會(huì)幫你自動(dòng)做類(lèi)型轉(zhuǎn)換,比如以下的例子會(huì)有編譯錯(cuò)誤:
var value2 int32
value1 := 64 // value1將會(huì)被自動(dòng)推導(dǎo)為int類(lèi)型
value2 = value1 // 編譯錯(cuò)誤
編譯錯(cuò)誤類(lèi)似于:
cannot use value1 (type int) as type int32 in assignment
使用強(qiáng)制類(lèi)型轉(zhuǎn)換可以解決這個(gè)編譯錯(cuò)誤:
value2 = int32(value1) // 編譯通過(guò)
當(dāng)然,開(kāi)發(fā)者在做強(qiáng)制類(lèi)型轉(zhuǎn)換時(shí),需要注意數(shù)據(jù)長(zhǎng)度被截短而發(fā)生的數(shù)據(jù)精度損失(比如將浮點(diǎn)數(shù)強(qiáng)制轉(zhuǎn)為整數(shù))和值溢出(值超過(guò)轉(zhuǎn)換的目標(biāo)類(lèi)型的值范圍時(shí))問(wèn)題。
類(lèi)型轉(zhuǎn)換
Go 語(yǔ)言中只有強(qiáng)類(lèi)型轉(zhuǎn)換,沒(méi)有隱式轉(zhuǎn)換。該語(yǔ)法只能在兩個(gè)類(lèi)型
之間支持相互轉(zhuǎn)換的時(shí)候使用。強(qiáng)制轉(zhuǎn)換的語(yǔ)法如下:
示例如下:
2. 數(shù)值運(yùn)算
Go語(yǔ)言支持下面的常規(guī)整數(shù)運(yùn)算:+、-、*、/和%
加減乘除就不詳細(xì)解釋了,需要說(shuō)下的是,% 和在C語(yǔ)言中一樣是求余運(yùn)算,比如:
5 % 3 // 結(jié)果為:2
3. 比較運(yùn)算
Go語(yǔ)言支持以下的幾種比較運(yùn)算符:>、<、==、>=、<=和!=
這一點(diǎn)與大多數(shù)其他語(yǔ)言相同,與C語(yǔ)言完全一致。
下面為條件判斷語(yǔ)句的例子:
i, j := 1, 2
if i == j {
fmt.Println("i and j are equal.")
}
兩個(gè)不同類(lèi)型的整型數(shù)不能直接比較,比如int8類(lèi)型的數(shù)和int類(lèi)型的數(shù)不能直接比較,但各種類(lèi)型的整型變量都可以直接與字面常量(literal)進(jìn)行比較,比如
var i int32
var j int64
i, j = 1, 2
if i == j { // 編譯錯(cuò)誤
fmt.Println("i and j are equal.")
}
if i == 1 || j == 2 { // 編譯通過(guò)
fmt.Println("i and j are equal.")
}
4. 位運(yùn)算
Go語(yǔ)言支持表2-2所示的位運(yùn)算符。
Go語(yǔ)言的大多數(shù)位運(yùn)算符與C語(yǔ)言都比較類(lèi)似,除了取反在C語(yǔ)言中是~x,而在Go語(yǔ)言中是^x。
編程語(yǔ)言中表示固定值的符號(hào)叫做字面量常量,簡(jiǎn)稱(chēng)字面量。如整形
字面量八進(jìn)制:“012”或者“0o17”,十六進(jìn)制: “0x12”,二進(jìn)制:”0b101”,
輸出表示如下圖:
字面量八進(jìn)制:“012”或者“0o17”,十六進(jìn)制: “0x12”,二進(jìn)制:”0b101”,
輸出表示如下圖:
浮點(diǎn)型用于表示包含小數(shù)點(diǎn)的數(shù)據(jù),比如1.234就是一個(gè)浮點(diǎn)型數(shù)據(jù)。Go語(yǔ)言中的浮點(diǎn)類(lèi)型采用IEEE-754標(biāo)準(zhǔn)的表達(dá)方式
1. 浮點(diǎn)數(shù)表示
Go語(yǔ)言定義了兩個(gè)類(lèi)型float32和float64,其中float32等價(jià)于C語(yǔ)言的float類(lèi)型,float64等價(jià)于C語(yǔ)言的double類(lèi)型
在Go語(yǔ)言里,定義一個(gè)浮點(diǎn)數(shù)變量的代碼如下:
var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 如果不加小數(shù)點(diǎn),fvalue2會(huì)被推導(dǎo)為整型而不是浮點(diǎn)型
對(duì)于以上例子中類(lèi)型被自動(dòng)推導(dǎo)的fvalue2,需要注意的是其類(lèi)型將被自動(dòng)設(shè)為float64,而不管賦給它的數(shù)字是否是用32位長(zhǎng)度表示的。因此,對(duì)于以上的例子,下面的賦值將導(dǎo)致編譯錯(cuò)誤:
fvalue1 = fvalue2
而必須使用這樣的強(qiáng)制類(lèi)型轉(zhuǎn)換:
fvalue1 = float32(fvalue2)
2. 浮點(diǎn)數(shù)比較
因?yàn)楦↑c(diǎn)數(shù)不是一種精確的表達(dá)方式,所以像整型那樣直接用==來(lái)判斷兩個(gè)浮點(diǎn)數(shù)是否相等是不可行的,這可能會(huì)導(dǎo)致不穩(wěn)定的結(jié)果
下面是一種推薦的替代方案:
import "math"
// p為用戶(hù)自定義的比較精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
復(fù)數(shù)實(shí)際上由兩個(gè)實(shí)數(shù)(在計(jì)算機(jī)中用浮點(diǎn)數(shù)表示)構(gòu)成,一個(gè)表示實(shí)部(real),一個(gè)表示虛部(imag)。如果了解了數(shù)學(xué)上的復(fù)數(shù)是怎么回事,那么Go語(yǔ)言的復(fù)數(shù)就非常容易理解了。
復(fù)數(shù)有實(shí)部和虛部,complex64 的實(shí)部和虛部為 32 位,complex128的實(shí)部和虛部為 64 位。
1. 復(fù)數(shù)表示
復(fù)數(shù)表示的示例如下:
var value1 complex64 // 由2個(gè)float32構(gòu)成的復(fù)數(shù)類(lèi)型
value1 = 3.2 + 12i
value2 := 3.2 + 12i // value2是complex128類(lèi)型
value3 := complex(3.2, 12) // value3結(jié)果同 value2
2. 實(shí)部與虛部
對(duì)于一個(gè)復(fù)數(shù)z = complex(x, y),就可以通過(guò)Go語(yǔ)言?xún)?nèi)置函數(shù)real(z)獲得該復(fù)數(shù)的實(shí)部,也就是x,通過(guò)imag(z)獲得該復(fù)數(shù)的虛部,也就是y。
Go 語(yǔ)言中的字符串以原生數(shù)據(jù)類(lèi)型出現(xiàn),使用字符串就像使用其他
原生數(shù)據(jù)類(lèi)型一樣。Go 語(yǔ)言里的字符串的內(nèi)部實(shí)現(xiàn)使用 utf-8 編碼。字符串的值為雙引號(hào)中的內(nèi)容,如
在Go語(yǔ)言中,字符串也是一種基本類(lèi)型。相比之下, C/C++語(yǔ)言中并不存在原生的字符類(lèi)型,通常使用字符數(shù)組來(lái)表示,并以字符指針來(lái)傳遞。
Go語(yǔ)言中字符串的聲明和初始化非常簡(jiǎn)單,舉例如下:
var str string // 聲明一個(gè)字符串變量
str = "Hello world" // 字符串賦值
ch := str[0] // 取字符串的第一個(gè)字符
fmt.Printf("The length of \"%s\" is %d \n", str, len(str))
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)
輸出結(jié)果為:
The length of "Hello world" is 11
The first character of "Hello world" is H.
字符串的內(nèi)容可以用類(lèi)似于數(shù)組下標(biāo)的方式獲取,但與數(shù)組不同,字符串的內(nèi)容不能在初始化后被修改,比如以下的例子:
str := "Hello world" // 字符串也支持聲明時(shí)進(jìn)行初始化的做法
str[0] = 'X' // 編譯錯(cuò)誤
編譯器會(huì)報(bào)類(lèi)似如下的錯(cuò)誤:
cannot assign to str[0]
在這個(gè)例子中我們使用了一個(gè)Go語(yǔ)言?xún)?nèi)置的函數(shù)len()來(lái)取字符串的長(zhǎng)度。這個(gè)函數(shù)非常有用,我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中處理字符串、數(shù)組和切片時(shí)將會(huì)經(jīng)常用到。
Printf()函數(shù)的用法與C語(yǔ)言運(yùn)行庫(kù)中的printf()函數(shù)如出一轍。
Go編譯器支持UTF-8的源代碼文件格式。這意味著源代碼中的字符串可以包含非ANSI的字符,比如“Hello world. 你好,世界!”可以出現(xiàn)在Go代碼中。但需要注意的是,如果你的Go代碼需要包含非ANSI字符,保存源文件時(shí)請(qǐng)注意編碼格式必須選擇UTF-8。特別是在Windows下一般編輯器都默認(rèn)存為本地編碼,比如中國(guó)地區(qū)可能是GBK編碼而不是UTF-8,如果沒(méi)注意這點(diǎn)在編譯和運(yùn)行時(shí)就會(huì)出現(xiàn)一些意料之外的情況。
字符串的編碼轉(zhuǎn)換是處理文本文檔(比如TXT、XML、HTML等)非常常見(jiàn)的需求,不過(guò)可惜的是Go語(yǔ)言?xún)H支持UTF-8和Unicode編碼。對(duì)于其他編碼,Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)并沒(méi)有內(nèi)置的編碼轉(zhuǎn)換支持。不過(guò),所幸的是我們可以很容易基于iconv庫(kù)用Cgo包裝一個(gè)。這里有一個(gè)開(kāi)源項(xiàng)目:https://github.com/xushiwei/go-iconv。
1. 字符串操作
平時(shí)常用的字符串操作如表2-3所示。
更多的字符串操作,請(qǐng)參考標(biāo)準(zhǔn)庫(kù)strings包。
2. 字符串遍歷
Go語(yǔ)言支持兩種方式遍歷字符串。一種是以字節(jié)數(shù)組的方式遍歷:
str := "Hello,世界"n := len(str) for i := 0; i < n; i++ {
ch := str[i] // 依據(jù)下標(biāo)取字符串中的字符,類(lèi)型為byte
fmt.Println(i, ch) }
這個(gè)例子的輸出結(jié)果為:
0 72 1 101 2 108 3 108 4 111 5 44 6 32 7 228 8 184 9 150 10 231 11 149 12 140
可以看出,這個(gè)字符串長(zhǎng)度為13。盡管從直觀(guān)上來(lái)說(shuō),這個(gè)字符串應(yīng)該只有9個(gè)字符。這是因?yàn)槊總€(gè)中文字符在UTF-8中占3個(gè)字節(jié),而不是1個(gè)字節(jié)
另一種是以Unicode字符遍歷:
str := "Hello,世界"for i, ch := range str {
fmt.Println(i, ch)//ch的類(lèi)型為rune }
輸出結(jié)果為:
0 72 1 101 2 108 3 108 4 111 5 44 6 32 7 19990 10 30028
以Unicode字符方式遍歷時(shí),每個(gè)字符的類(lèi)型是rune(早期的Go語(yǔ)言用int類(lèi)型表示Unicode字符),而不是byte。
字符串的常見(jiàn)操作
在Go語(yǔ)言中支持兩個(gè)字符類(lèi)型,一個(gè)是byte(實(shí)際上是uint8的別名),代表UTF-8字符串的單個(gè)字節(jié)的值;另一個(gè)是rune,代表單個(gè)Unicode字符。
關(guān)于rune相關(guān)的操作,可查閱Go標(biāo)準(zhǔn)庫(kù)的unicode包。另外unicode/utf8包也提供了UTF8和Unicode之間的轉(zhuǎn)換。
出于簡(jiǎn)化語(yǔ)言的考慮,Go語(yǔ)言的多數(shù)API都假設(shè)字符串為UTF-8編碼。盡管Unicode字符在標(biāo)準(zhǔn)庫(kù)中有支持,但實(shí)際上較少使用。
字符指類(lèi)字形單位或符號(hào),包括字母、數(shù)字、運(yùn)算符號(hào)、標(biāo)點(diǎn)符
號(hào)和其他符號(hào),以及一些功能性符號(hào)。每個(gè)字符在計(jì)算機(jī)中都有相應(yīng)的二進(jìn)制代碼
Go 語(yǔ)言?xún)?nèi)置兩種字符類(lèi)型:
一種是 byte 的字節(jié)類(lèi)型,它是 uint8 的別名。
另一種是 Unicode 編碼的 rune 類(lèi)型,它是 int32 類(lèi)型的別名。
Go 語(yǔ)言默認(rèn)的字符編碼就是 UTF-8 類(lèi)型的。
遍歷字符串
如下:
UTF-8 編碼下一個(gè)中文漢字由 3-4 個(gè)字節(jié)組成,所以我們不能簡(jiǎn)
單的按照字節(jié)去遍歷一個(gè)包含中文字符的字符串。
字符串底層是一個(gè) byte 數(shù)組,所以可以和[]byte 類(lèi)型相互轉(zhuǎn)換。
字符串是不能修改的,字符串由 byte 字節(jié)組成,所以字符串的長(zhǎng)度
就是 byte 字節(jié)的長(zhǎng)度。rune 類(lèi)型用來(lái)表示 utf-8 字符,一個(gè) rune 字
符由一個(gè)或多個(gè) byte 組成。
修改字符串
要修改字符串,需要先將其轉(zhuǎn)換成[]rune 或者[]byte,修改以后,再轉(zhuǎn)換為 string。
無(wú)論哪種轉(zhuǎn)換,都會(huì)重新分配內(nèi)存并復(fù)制字節(jié)數(shù)組。如下:
多行字符串的顯示,可以使用反引號(hào)進(jìn)行,需要注意的是反引號(hào)間換行將被作為字符串中的換行,但是所有的轉(zhuǎn)義字符均無(wú)效,文本將會(huì)原樣輸出
數(shù)組是Go語(yǔ)言編程中最常用的數(shù)據(jù)結(jié)構(gòu)之一。顧名思義,數(shù)組就是指一系列同一類(lèi)型數(shù)據(jù)的集合。數(shù)組中包含的每個(gè)數(shù)據(jù)被稱(chēng)為數(shù)組元素(element),一個(gè)數(shù)組包含的元素個(gè)數(shù)被稱(chēng)為數(shù)組的長(zhǎng)度。
以下為一些常規(guī)的數(shù)組聲明方法:
[32]byte // 長(zhǎng)度為32的數(shù)組,每個(gè)元素為一個(gè)字節(jié)
[2*N] struct { x, y int32 } // 復(fù)雜類(lèi)型數(shù)組
[1000]*float64 // 指針數(shù)組
[3][5]int // 二維數(shù)組
[2][2][2]float64 // 等同于[2]([2]([2]float64))
從以上類(lèi)型也可以看出,數(shù)組可以是多維的,比如[3][5]int就表達(dá)了一個(gè)3行5列的二維整型數(shù)組,總共可以存放15個(gè)整型元素。
在Go語(yǔ)言中,數(shù)組長(zhǎng)度在定義后就不可更改,在聲明時(shí)長(zhǎng)度可以為一個(gè)常量或者一個(gè)常量表達(dá)式(常量表達(dá)式是指在編譯期即可計(jì)算結(jié)果的表達(dá)式)。數(shù)組的長(zhǎng)度是該數(shù)組類(lèi)型的一個(gè)內(nèi)置常量,可以用Go語(yǔ)言的內(nèi)置函數(shù)len() 來(lái)獲取。
下面是一個(gè)獲取數(shù)組arr元素個(gè)數(shù)的寫(xiě)法:
arrLength := len(arr)
1. 元素訪(fǎng)問(wèn)
可以使用數(shù)組下標(biāo)來(lái)訪(fǎng)問(wèn)數(shù)組中的元素。與C語(yǔ)言相同,數(shù)組下標(biāo)從0開(kāi)始,len(array)-1則表示最后一個(gè)元素的下標(biāo)。下面的示例遍歷整型數(shù)組并逐個(gè)打印元素內(nèi)容:
for i := 0; i < len(array); i++ {
fmt.Println("Element", i, "of array is", array[i])
}
Go語(yǔ)言還提供了一個(gè)關(guān)鍵字range,用于便捷地遍歷容器中的元素。當(dāng)然,數(shù)組也是range的支持范圍。上面的遍歷過(guò)程可以簡(jiǎn)化為如下的寫(xiě)法:
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}
在上面的例子里可以看到
range具有兩個(gè)返回值,第一個(gè)返回值是元素的數(shù)組下標(biāo),第二個(gè)返回值是元素的值。
2. 值類(lèi)型
需要特別注意的是,在Go語(yǔ)言中數(shù)組是一個(gè)值類(lèi)型(value type)。所有的值類(lèi)型變量在賦值和作為參數(shù)傳遞時(shí)都將產(chǎn)生一次復(fù)制動(dòng)作。
如果將數(shù)組作為函數(shù)的參數(shù)類(lèi)型,則在函數(shù)調(diào)用時(shí)該參數(shù)將發(fā)生數(shù)據(jù)復(fù)制。因此,在函數(shù)體中無(wú)法修改傳入的數(shù)組的內(nèi)容,因?yàn)楹瘮?shù)內(nèi)操作的只是所傳入數(shù)組的一個(gè)副本。
下面用例子來(lái)說(shuō)明這一特點(diǎn):
package main
import "fmt"
func modify(array [10]int) {
array[0] = 10 // 試圖修改數(shù)組的第一個(gè)元素
fmt.Println("In modify(), array values:", array)
}
func main() {
array := [5]int{1,2,3,4,5} // 定義并初始化一個(gè)數(shù)組
modify(array) // 傳遞給一個(gè)函數(shù),并試圖在函數(shù)體內(nèi)修改這個(gè)數(shù)組內(nèi)容
fmt.Println("In main(), array values:", array)
}
該程序的執(zhí)行結(jié)果為:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]
從執(zhí)行結(jié)果可以看出,函數(shù)modify()內(nèi)操作的那個(gè)數(shù)組跟main()中傳入的數(shù)組是兩個(gè)不同的實(shí)例。
我們已經(jīng)提過(guò)數(shù)組的特點(diǎn):數(shù)組的長(zhǎng)度在定義之后無(wú)法再次修改;數(shù)組是值類(lèi)型,每次傳遞都將產(chǎn)生一份副本。顯然這種數(shù)據(jù)結(jié)構(gòu)無(wú)法完全滿(mǎn)足開(kāi)發(fā)者的真實(shí)需求。
不用失望,Go語(yǔ)言提供了數(shù)組切片(slice) 這個(gè)非??岬墓δ軄?lái)彌補(bǔ)數(shù)組的不足。
初看起來(lái),數(shù)組切片就像一個(gè)指向數(shù)組的指針,實(shí)際上它擁有自己的數(shù)據(jù)結(jié)構(gòu),而不僅僅是個(gè)指針。
數(shù)組切片的數(shù)據(jù)結(jié)構(gòu)可以抽象為以下3個(gè)變量:
一個(gè)指向原生數(shù)組的指針;
數(shù)組切片中的元素個(gè)數(shù);
數(shù)組切片已分配的存儲(chǔ)空間。
從底層實(shí)現(xiàn)的角度來(lái)看,數(shù)組切片實(shí)際上仍然使用數(shù)組來(lái)管理元素,因此它們之間的關(guān)系讓C++程序員們很容易聯(lián)想起STL中std::vector和數(shù)組的關(guān)系?;跀?shù)組,數(shù)組切片添加了一系列管理功能,可以隨時(shí)動(dòng)態(tài)擴(kuò)充存放空間,并且可以被隨意傳遞而不會(huì)導(dǎo)致所管理的元素被重復(fù)復(fù)制。
1. 創(chuàng)建數(shù)組切片
創(chuàng)建數(shù)組切片的方法主要有兩種——基于數(shù)組和直接創(chuàng)建,下面我們來(lái)簡(jiǎn)要介紹一下這兩種方法。
基于數(shù)組
數(shù)組切片可以基于一個(gè)已存在的數(shù)組創(chuàng)建。數(shù)組切片可以只使用數(shù)組的一部分元素或者整個(gè)數(shù)組來(lái)創(chuàng)建,甚至可以創(chuàng)建一個(gè)比所基于的數(shù)組還要大的數(shù)組切片。代碼清單2-1演示了如何基于一個(gè)數(shù)組的前5個(gè)元素創(chuàng)建一個(gè)數(shù)組切片。
代碼清單2-1 slice.go
package main
import "fmt"
func main() {
// 先定義一個(gè)數(shù)組
var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 基于數(shù)組創(chuàng)建一個(gè)數(shù)組切片
var mySlice []int = myArray[:5]
fmt.Println("Elements of myArray: ")
for _, v := range myArray {
fmt.Print(v, " ")
}
fmt.Println("\nElements of mySlice: ")
for _, v := range mySlice {
fmt.Print(v, " ")
}
fmt.Println()
}
運(yùn)行結(jié)果為:
Elements of myArray:
1 2 3 4 5 6 7 8 9 10
Elements of mySlice:
1 2 3 4 5
Go語(yǔ)言支持用myArray[first:last]這樣的方式來(lái)基于數(shù)組生成一個(gè)數(shù)組切片,而且這個(gè)用法還很靈活,比如下面幾種都是合法的。
基于myArray的所有元素創(chuàng)建數(shù)組切片:
mySlice = myArray[:]
基于myArray的前5個(gè)元素創(chuàng)建數(shù)組切片:
mySlice = myArray[:5]
基于從第5個(gè)元素開(kāi)始的所有元素創(chuàng)建數(shù)組切片:
mySlice = myArray[5:]
直接創(chuàng)建
并非一定要事先準(zhǔn)備一個(gè)數(shù)組才能創(chuàng)建數(shù)組切片。Go語(yǔ)言提供的內(nèi)置函數(shù)make()可以用于靈活地創(chuàng)建數(shù)組切片。下面的例子示范了直接創(chuàng)建數(shù)組切片的各種方法。
創(chuàng)建一個(gè)初始元素個(gè)數(shù)為5的數(shù)組切片,元素初始值為0:
mySlice1 := make([]int, 5)
創(chuàng)建一個(gè)初始元素個(gè)數(shù)為5的數(shù)組切片,元素初始值為0,并預(yù)留10個(gè)元素的存儲(chǔ)空間:
mySlice2 := make([]int, 5, 10)
直接創(chuàng)建并初始化包含5個(gè)元素的數(shù)組切片:
mySlice3 := []int{1, 2, 3, 4, 5}
當(dāng)然,事實(shí)上還會(huì)有一個(gè)匿名數(shù)組被創(chuàng)建出來(lái),只是不需要我們來(lái)操心而已。
2. 元素遍歷
操作數(shù)組元素的所有方法都適用于數(shù)組切片,比如數(shù)組切片也可以按下標(biāo)讀寫(xiě)元素,用len()函數(shù)獲取元素個(gè)數(shù),并支持使用range關(guān)鍵字來(lái)快速遍歷所有元素。
傳統(tǒng)的元素遍歷方法如下:
for i := 0; i <len(mySlice); i++ {
fmt.Println("mySlice[", i, "] =", mySlice[i])
}
使用range關(guān)鍵字可以讓遍歷代碼顯得更整潔。range表達(dá)式有兩個(gè)返回值,第一個(gè)是索引,第二個(gè)是元素的值:
for i, v := range mySlice {
fmt.Println("mySlice[", i, "] =", v)
}
對(duì)比上面的兩個(gè)方法,我們可以很容易地看出使用range的代碼更簡(jiǎn)單易懂。
3. 動(dòng)態(tài)增減元素
可動(dòng)態(tài)增減元素是數(shù)組切片比數(shù)組更為強(qiáng)大的功能。與數(shù)組相比,數(shù)組切片多了一個(gè)存儲(chǔ)能力(capacity)的概念,即元素個(gè)數(shù)和分配的空間可以是兩個(gè)不同的值。合理地設(shè)置存儲(chǔ)能力的值,可以大幅降低數(shù)組切片內(nèi)部重新分配內(nèi)存和搬送內(nèi)存塊的頻率,從而大大提高程序性能。
假如你明確知道當(dāng)前創(chuàng)建的數(shù)組切片最多可能需要存儲(chǔ)的元素個(gè)數(shù)為50,那么如果你設(shè)置的存儲(chǔ)能力小于50,比如20,那么在元素超過(guò)20時(shí),底層將會(huì)發(fā)生至少一次這樣的動(dòng)作——重新分配一塊“夠大”的內(nèi)存,并且需要把內(nèi)容從原來(lái)的內(nèi)存塊復(fù)制到新分配的內(nèi)存塊,這會(huì)產(chǎn)生比較明顯的開(kāi)銷(xiāo)。給“夠大”這兩個(gè)字加上引號(hào)的原因是系統(tǒng)并不知道多大才是夠大,所以只是一個(gè)簡(jiǎn)單的猜測(cè)。比如,將原有的內(nèi)存空間擴(kuò)大兩倍,但兩倍并不一定夠,所以之前提到的內(nèi)存重新分配和內(nèi)容復(fù)制的過(guò)程很有可能發(fā)生多次,從而明顯降低系統(tǒng)的整體性能。但如果你知道最大是50并且一開(kāi)始就設(shè)置存儲(chǔ)能力為50,那么之后就不會(huì)發(fā)生這樣非常耗費(fèi)CPU的動(dòng)作,從而達(dá)到空間換時(shí)間的效果。
數(shù)組切片支持Go語(yǔ)言?xún)?nèi)置的cap()函數(shù)和len()函數(shù),代碼清單2-2簡(jiǎn)單示范了這兩個(gè)內(nèi)置函數(shù)的用法??梢钥闯?,cap()函數(shù)返回的是數(shù)組切片分配的空間大小,而len()函數(shù)返回的是數(shù)組切片中當(dāng)前所存儲(chǔ)的元素個(gè)數(shù)。
代碼清單2-2 slice2.go
package main
import "fmt"
func main() {
mySlice := make([]int, 5, 10)
fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))
}
該程序的輸出結(jié)果為:
len(mySlice): 5
cap(mySlice): 10
如果需要往上例中mySlice已包含的5個(gè)元素后面繼續(xù)新增元素,可以使用append()函數(shù)。
下面的代碼可以從尾端給mySlice加上3個(gè)元素,從而生成一個(gè)新的數(shù)組切片:
mySlice = append(mySlice, 1, 2, 3)
函數(shù)append()的第二個(gè)參數(shù)其實(shí)是一個(gè)不定參數(shù),我們可以按自己需求添加若干個(gè)元素,甚至直接將一個(gè)數(shù)組切片追加到另一個(gè)數(shù)組切片的末尾:
mySlice2 := []int{8, 9, 10}
// 給mySlice后面添加另一個(gè)數(shù)組切片
mySlice = append(mySlice, mySlice2...)
需要注意的是,我們?cè)诘诙€(gè)參數(shù)mySlice2后面加了三個(gè)點(diǎn),即一個(gè)省略號(hào),如果沒(méi)有這個(gè)省略號(hào)的話(huà),會(huì)有編譯錯(cuò)誤,因?yàn)榘碼ppend()的語(yǔ)義,從第二個(gè)參數(shù)起的所有參數(shù)都是待附加的元素。因?yàn)閙ySlice中的元素類(lèi)型為int,所以直接傳遞mySlice2是行不通的。加上省略號(hào)相當(dāng)于把mySlice2包含的所有元素打散后傳入。
上述調(diào)用等同于:
mySlice = append(mySlice, 8, 9, 10)
數(shù)組切片會(huì)自動(dòng)處理存儲(chǔ)空間不足的問(wèn)題。如果追加的內(nèi)容長(zhǎng)度超過(guò)當(dāng)前已分配的存儲(chǔ)空間(即cap()調(diào)用返回的信息),數(shù)組切片會(huì)自動(dòng)分配一塊足夠大的內(nèi)存。
4. 基于數(shù)組切片創(chuàng)建數(shù)組切片
類(lèi)似于數(shù)組切片可以基于一個(gè)數(shù)組創(chuàng)建,數(shù)組切片也可以基于另一個(gè)數(shù)組切片創(chuàng)建。下面的例子基于一個(gè)已有數(shù)組切片創(chuàng)建新數(shù)組切片:
oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前3個(gè)元素構(gòu)建新數(shù)組切片
有意思的是,選擇的oldSlicef元素范圍甚至可以超過(guò)所包含的元素個(gè)數(shù),比如newSlice可以基于oldSlice的前6個(gè)元素創(chuàng)建,雖然oldSlice只包含5個(gè)元素。只要這個(gè)選擇的范圍不超過(guò)oldSlice存儲(chǔ)能力(即cap()返回的值),那么這個(gè)創(chuàng)建程序就是合法的。newSlice中超出oldSlice元素的部分都會(huì)填上0。
5. 內(nèi)容復(fù)制
數(shù)組切片支持Go語(yǔ)言的另一個(gè)內(nèi)置函數(shù)copy(),用于將內(nèi)容從一個(gè)數(shù)組切片復(fù)制到另一個(gè)數(shù)組切片。如果加入的兩個(gè)數(shù)組切片不一樣大,就會(huì)按其中較小的那個(gè)數(shù)組切片的元素個(gè)數(shù)進(jìn)行復(fù)制。下面的示例展示了copy()函數(shù)的行為:
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只會(huì)復(fù)制slice1的前3個(gè)元素到slice2中
copy(slice1, slice2) // 只會(huì)復(fù)制slice2的3個(gè)元素到slice1的前3個(gè)位置
在C++/Java中,map一般都以庫(kù)的方式提供,比如在C++中是STL的std::map<>,在C#中是Dictionary<>,在Java中是Hashmap<>,在這些語(yǔ)言中,如果要使用map,事先要引用相應(yīng)的庫(kù)。而在Go中,使用map不需要引入任何庫(kù),并且用起來(lái)也更加方便。
map是一堆鍵值對(duì)的未排序集合。比如以身份證號(hào)作為唯一鍵來(lái)標(biāo)識(shí)一個(gè)人的信息,則這個(gè)map可以定義為代碼清單 2-3所示的方式。
代碼清單2-3 map1.go
package main
import "fmt"
// PersonInfo是一個(gè)包含個(gè)人詳細(xì)信息的類(lèi)型
type PersonInfo struct {
ID string
Name string
Address string
}
func main() {
var personDB map[string] PersonInfo
personDB = make(map[string] PersonInfo)
// 往這個(gè)map里插入幾條數(shù)據(jù)
personDB["12345"] = PersonInfo{"12345", "Tom", "Room 203,..."}
personDB["1"] = PersonInfo{"1", "Jack", "Room 101,..."}
// 從這個(gè)map查找鍵為"1234"的信息
person, ok := personDB["1234"]
// ok是一個(gè)返回的bool型,返回true表示找到了對(duì)應(yīng)的數(shù)據(jù)
if ok {
fmt.Println("Found person", person.Name, "with ID 1234.")
} else {
fmt.Println("Did not find person with ID 1234.")
}
}
上面這個(gè)簡(jiǎn)單的例子基本上已經(jīng)覆蓋了map的主要用法,下面對(duì)其中的關(guān)鍵點(diǎn)進(jìn)行細(xì)述。
1. 變量聲明
map的聲明基本上沒(méi)有多余的元素,比如:
var myMap map[string] PersonInfo
其中,myMap是聲明的map變量名,string是鍵的類(lèi)型,PersonInfo則是其中所存放的值類(lèi)型。
2. 創(chuàng)建
我們可以使用Go語(yǔ)言?xún)?nèi)置的函數(shù)make()來(lái)創(chuàng)建一個(gè)新map。下面的這個(gè)例子創(chuàng)建了一個(gè)鍵類(lèi)型為string、值類(lèi)型為PersonInfo的map:
myMap = make(map[string] PersonInfo)
也可以選擇是否在創(chuàng)建時(shí)指定該map的初始存儲(chǔ)能力,下面的例子創(chuàng)建了一個(gè)初始存儲(chǔ)能力為100的map:
myMap = make(map[string] PersonInfo, 100)
創(chuàng)建并初始化map的代碼如下:
myMap = map[string] PersonInfo{
"1234": PersonInfo{"1", "Jack", "Room 101,..."},
}
3. 元素賦值
賦值過(guò)程非常簡(jiǎn)單明了,就是將鍵和值用下面的方式對(duì)應(yīng)起來(lái)即可:
myMap["1234"] = PersonInfo{"1", "Jack", "Room 101,..."}
4. 元素刪除
Go語(yǔ)言提供了一個(gè)內(nèi)置函數(shù)delete(),用于刪除容器內(nèi)的元素。下面我們簡(jiǎn)單介紹一下如何用delete()函數(shù)刪除map內(nèi)的元素:
delete(myMap, "1234")
上面的代碼將從myMap中刪除鍵為“1234”的鍵值對(duì)。如果“1234”這個(gè)鍵不存在,那么這個(gè)調(diào)用將什么都不發(fā)生,也不會(huì)有什么副作用。但是如果傳入的map變量的值是nil,該調(diào)用將導(dǎo)致程序拋出異常(panic)
5. 元素查找
在Go語(yǔ)言中,map的查找功能設(shè)計(jì)得比較精巧。而在其他語(yǔ)言中,我們要判斷能否獲取到一個(gè)值不是件容易的事情。判斷能否從map中獲取一個(gè)值的常規(guī)做法是:
(1) 聲明并初始化一個(gè)變量為空;
(2) 試圖從map中獲取相應(yīng)鍵的值到該變量中;
(3) 判斷該變量是否依舊為空,如果為空則表示map中沒(méi)有包含該變量。
這種用法比較啰唆,而且判斷變量是否為空這條語(yǔ)句并不能真正表意(是否成功取到對(duì)應(yīng)的值),從而影響代碼的可讀性和可維護(hù)性。有些庫(kù)甚至?xí)O(shè)計(jì)為因?yàn)橐粋€(gè)鍵不存在而拋出異常,讓開(kāi)發(fā)者用起來(lái)膽戰(zhàn)心驚,不得不一層層嵌套try-catch語(yǔ)句,這更是不人性化的設(shè)計(jì)。在Go語(yǔ)言中,要從map中查找一個(gè)特定的鍵,可以通過(guò)下面的代碼來(lái)實(shí)現(xiàn):
value, ok := myMap["1234"]
if ok { // 找到了
// 處理找到的value
}
判斷是否成功找到特定的鍵,不需要檢查取到的值是否為nil,只需查看第二個(gè)返回值ok,這讓表意清晰很多。配合:=操作符,讓你的代碼沒(méi)有多余成分,看起來(lái)非常清晰易懂。
到此,相信大家對(duì)“go語(yǔ)言有哪些類(lèi)型及怎么表示”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。