溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

go語(yǔ)言有哪些類(lèi)型及怎么表示

發(fā)布時(shí)間:2022-12-03 09:37:25 來(lái)源:億速云 閱讀:211 作者:iii 欄目:編程語(yǔ)言

本篇內(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ǔ)言的類(lèi)型

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視頻教程】

布爾類(lèi)型

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)型及怎么表示

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)型。

go語(yǔ)言有哪些類(lèi)型及怎么表示

go語(yǔ)言有哪些類(lèi)型及怎么表示

go語(yǔ)言有哪些類(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ǔ)法如下:

go語(yǔ)言有哪些類(lèi)型及怎么表示

示例如下:

go語(yǔ)言有哪些類(lèi)型及怎么表示

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ǔ)言有哪些類(lèi)型及怎么表示

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”,

輸出表示如下圖:

go語(yǔ)言有哪些類(lèi)型及怎么表示

浮點(diǎn)型

字面量八進(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ǔ)言有哪些類(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ù)類(lèi)型

復(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 位。

go語(yǔ)言有哪些類(lèi)型及怎么表示

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)型及怎么表示

在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所示。
go語(yǔ)言有哪些類(lèi)型及怎么表示
更多的字符串操作,請(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ǔ)言有哪些類(lèi)型及怎么表示

字符類(lèi)型

在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í)際上較少使用。

byte 和 rune 類(lèi)型

字符指類(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ǔ)言有哪些類(lèi)型及怎么表示

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)型的。

遍歷字符串

如下:

go語(yǔ)言有哪些類(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ù)組。如下:

go語(yǔ)言有哪些類(lèi)型及怎么表示

常見(jiàn)的轉(zhuǎn)義字符

go語(yǔ)言有哪些類(lèi)型及怎么表示

多行字符串的顯示,可以使用反引號(hào)進(jìn)行,需要注意的是反引號(hào)間換行將被作為字符串中的換行,但是所有的轉(zhuǎn)義字符均無(wú)效,文本將會(huì)原樣輸出

go語(yǔ)言有哪些類(lèi)型及怎么表示

數(shù)組

數(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í)例。

數(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è)位置

map

在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í)!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI