Golang的變量常量及基本類型

1,變量

變量的聲明:
Go語言的變量聲明方式與C和C++語言有明顯的不同。對於純粹的變量聲明, Go語言引入了
關鍵字var,而類型信息放在變量名之後,示例如下:

1.1變量的聲明

var v1 int
var v2 string
var v3 [10]int // 數組
var v4 []int // 數組切片
var v5 struct {
f int
}
var v6 *int // 指針
var v7 map[string]int // map, key爲string類型, value爲int類型
var v8 func(a int) int

變量聲明語句不需要使用分號作爲結束符。與C語言相比, Go語言摒棄了語句必須以分號作
爲語句結束標記的習慣。
var關鍵字的另一種用法是可以將若干個需要聲明的變量放置在一起,免得程序員需要重複
寫var關鍵字,如下所示:

var (
v1 int
v2 string
)

1.2變量的初始化

對於聲明變量時需要進行初始化的場景, var關鍵字可以保留,但不再是必要的元素,如下
所示:

var v1 int = 10 // 正確的使用方式1
var v2 = 10 // 正確的使用方式2,編譯器可以自動推導出v2的類型
v3 := 10 // 正確的使用方式3,編譯器可以自動推導出v3的類型

以上三種用法的效果是完全一樣的。與第一種用法相比,第三種用法需要輸入的字符數大大
減少,是懶程序員和聰明程序員的最佳選擇。這裏Go語言也引入了另一個C和C++中沒有的符號
(冒號和等號的組合:=),用於明確表達同時進行變量聲明和初始化的工作。
指定類型已不再是必需的, Go編譯器可以從初始化表達式的右值推導出該變量應該聲明爲
哪種類型,這讓Go語言看起來有點像動態類型語言,儘管Go語言實際上是不折不扣的強類型語
言(靜態類型語言)。
當然,出現在:=左側的變量不應該是已經被聲明過的,否則會導致編譯錯誤,比如下面這個
寫法:
var i int
i := 2
會導致類似如下的編譯錯誤:
no new variables on left side of :=
:=左側的變量必須是沒有聲明過,不然就會報錯

1.3 變量賦值

在Go語法中,變量初始化和變量賦值是兩個不同的概念。下面爲聲明一個變量之後的賦值
過程:
var v10 int
v10 = 123
Go語言的變量賦值與多數語言一致,但Go語言中提供了C/C++程序員期盼多年的多重賦值功
能,比如下面這個交換i和j變量的語句:
i, j = j, i
在不支持多重賦值的語言中,交互兩個變量的內容需要引入一箇中間變量:
t = i; i = j; j = t;
多重賦值的特性在Go語言庫的實現中也被使用得相當充分,在介紹函數的多重返回值時,
將對其進行更加深入的介紹。總而言之,多重賦值功能讓Go語言與C/C++語言相比可以非常明顯
地減少代碼行數。多重賦值細節和原理參照官方文檔golang官網

1.4變量匿名

我們在使用傳統的強類型語言編程時,經常會出現這種情況,即在調用函數時爲了獲取一個
值,卻因爲該函數返回多個值而不得不定義一堆沒用的變量。在Go中這種情況可以通過結合使
用多重返回和匿名變量來避免這種醜陋的寫法,讓代碼看起來更加優雅。
假 設 GetName() 函 數 的 定 義 如 下 , 它 返 回 3 個 值 , 分 別 爲firstName 、 lastName 和
nickName:

func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"
}

若只想獲得nickName,則函數調用語句可以用如下方式編寫:

_, _, nickName := GetName()

這種用法可以讓代碼非常清晰,基本上屏蔽掉了可能混淆代碼閱讀者視線的內容,從而大幅
降低溝通的複雜度和代碼維護的難度

2常量

在Go語言中,常量是指編譯期間就已知且不可改變的值。常量可以是數值類型(包括整型、
浮點型和複數類型)、布爾類型、字符串類型等。

2.1字面常量

所謂字面常量(literal),是指程序中硬編碼的常量,如:
-12
3.14159265358979323846 // 浮點類型的常量
3.2+12i // 複數類型的常量
true // 布爾類型的常量
“foo” // 字符串常量
在其他語言中,常量通常有特定的類型,比如-12在C語言中會認爲是一個int類型的常量。
如果要指定一個值爲-12的long類型常量,需要寫成-12l,這有點違反人們的直觀感覺。 Go語言
的字面常量更接近我們自然語言中的常量概念,它是無類型的。只要這個常量在相應類型的值域
範圍內,就可以作爲該類型的常量,比如上面的常量-12,它可以賦值給int、 uint、 int32、
int64、 float32、 float64、 complex64、 complex128等類型的變量。

2.2常量定義

通過const關鍵字,你可以給字面常量指定一個友好的名字:

const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 無類型浮點常量
const (
size int64 = 1024
eof = -1 // 無類型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值
const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", 無類型整型和字符串常量

Go的常量定義可以限定常量類型,但不是必需的。如果定義常量時沒有指定類型,那麼它
與字面常量一樣,是無類型常量。
常量定義的右值也可以是一個在編譯期運算的常量表達式,比如
const mask = 1 << 3
由於常量的賦值是一個編譯期行爲,所以右值不能出現任何需要運行期才能得出結果的表達
式,比如試圖以如下方式定義常量就會導致編譯錯誤:
const Home = os.GetEnv(“HOME”)
原因很簡單, os.GetEnv()只有在運行期才能知道返回結果,在編譯期並不能確定,所以
無法作爲常量定義的右值。
2.2.3 預定義常量

Go語言預定義了這些常量: true、 false和iota。
iota比較特殊,可以被認爲是一個可被編譯器修改的常量,在每一個const關鍵字出現時被
重置爲0,然後在下一個const出現之前,每出現一次iota,其所代表的數字會自動增1。
從以下的例子可以基本理解iota的用法:

const ( // iota被重設爲0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota在每個const開頭被重設爲0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0
v float64 = iota * 42 // v == 42.0
w = iota * 42 // w == 84
)
const x = iota // x == 0 (因爲iota又被重設爲0了)
const y = iota // y == 0 (同上)
如果兩個const的賦值語句的表達式是一樣的,那麼可以省略後一個賦值表達式。因此,上
面的前兩個const語句可簡寫爲:
const ( // iota被重設爲0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <<iota // a == 1 (iota在每個const開頭被重設爲0)
b // b == 2
c // c == 4
)

2.3枚舉(Golang沒有)

枚舉指一系列相關的常量,比如下面關於一個星期中每天的定義。通過上一節的例子,我們
看到可以用在const後跟一對圓括號的方式定義一組常量,這種定義法在Go語言中通常用於定義
枚舉值。 Go語言並不支持衆多其他語言明確支持的enum關鍵字。
下面是一個常規的枚舉表示法,其中定義了一系列整型常量:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 這個常量沒有導出
)
同Go語言的其他符號(symbol)一樣,以大寫字母開頭的常量在包外可見。
以上例子中numberOfDays爲包內私有,其他符號則可被其他包訪問。

3類型

Go語言內置以下這些基礎類型:

  • 布爾類型: bool
  • 整型: int8、 byte、 int16、 int、 uint、 uintptr等
  • 浮點類型: float32、 float64。
  • 複數類型: complex64、 complex128。
  • 字符串: string。
  • 字符類型: rune。
  • 錯誤類型: error。
  • 此外, Go語言也支持以下這些複合類型:
  • 指針(pointer)
  • 數組(array)
  • 切片(slice)
  • 字典(map)
  • 通道(chan)
  • 結構體(struct)
  • 接口(interface)

3.1布爾類型

Go語言中的布爾類型與其他語言基本一致,關鍵字也爲bool,可賦值爲預定義的true和
false示例代碼如下:

var v1 bool
v1 = true
v2 := (1 == 2) // v2也會被推導爲bool類型

布爾類型不能接受其他類型的賦值,不支持自動或強制的類型轉換。以下的示例是一些錯誤
的用法,會導致編譯錯誤:

var b bool
b = 1 // 編譯錯誤
b = bool(1) // 編譯錯誤
以下的用法纔是正確的:
var b bool
b = (1!=0) // 編譯正確
fmt.Println("Result:", b) // 打印結果爲Result: true

3.2,整形

整型是所有編程語言裏最基礎的數據類型。 Go語言支持下表所示的這些整型類型。

類 型 長度(字節) 值 範 圍
int8 1 128 ~ 127
uint8(即byte) 1 0 ~ 255
int16 2 32 768 ~ 32 767
uint16 2 0 ~ 65 535
int32 4 2 147 483 648 ~ 2 147 483 647
uint32 4 0 ~ 4 294 967 295
int64 8 9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
uint64 8 0 ~ 18 446 744 073 709 551 615
int 平臺相關 平臺相關
uint 平臺相關 平臺相關
uintptr 同指針 在32位平臺下爲4字節, 64位平臺下爲8字節

1. 類型表示
需要注意的是, int和int32在Go語言裏被認爲是兩種不同的類型,編譯器也不會幫你自動
做類型轉換,比如以下的例子會有編譯錯誤:
var value2 int32
value1 := 64 // value1將會被自動推導爲int類型
value2 = value1 // 編譯錯誤
編譯錯誤類似於:
cannot use value1 (type int) as type int32 in assignment。
使用強制類型轉換可以解決這個編譯錯誤:
value2 = int32(value1) // 編譯通過
當然,開發者在做強制類型轉換時,需要注意數據長度被截短而發生的數據精度損失(比如
將浮點數強制轉爲整數)和值溢出(值超過轉換的目標類型的值範圍時)問題。
2. 數值運算
Go語言支持下面的常規整數運算: +、 、 *、 /和%。加減乘除就不詳細解釋了,需要說下的
是, % 和在C語言中一樣是求餘運算,比如:
5 % 3 // 結果爲: 2
3. 比較運算
Go語言支持以下的幾種比較運算符: >、 <、 ==、 >=、 <=和!=。這一點與大多數其他語言相
同,與C語言完全一致。
下面爲條件判斷語句的例子:
i, j := 1, 2
if i == j {
fmt.Println(“i and j are equal.”)
}
兩個不同類型的整型數不能直接比較,比如int8類型的數和int類型的數不能直接比較,但
各種類型的整型變量都可以直接與字面常量(literal)進行比較,比如:
var i int32
var j int64
i, j = 1, 2
if i == j { // 編譯錯誤
fmt.Println(“i and j are equal.”)
}
if i == 1 || j == 2 { // 編譯通過
fmt.Println(“i and j are equal.”)
}
4. 位運算
Go語言支持表2-2所示的位運算符。

運 算 含 義 樣 例
x << y 左移 124 << 2 // 結果爲496
x >> y 右移 124 >> 2 // 結果爲31
x ^ y 異或 124 ^ 2 // 結果爲126
x & y 124 & 2 // 結果爲0
x y
^x 取反 ^2 // 結果爲-3

Go語言的大多數位運算符與C語言都比較類似,除了取反在C語言中是~x,而在Go語言中
是^x。

3.3浮點型

浮點型用於表示包含小數點的數據,比如1.234就是一個浮點型數據。 Go語言中的浮點類型
採用IEEE-754標準的表達方式。
1. 浮點數表示
Go語言定義了兩個類型float32和float64,其中float32等價於C語言的float類型,
float64等價於C語言的double類型。
在Go語言裏,定義一個浮點數變量的代碼如下:
var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 如果不加小數點, fvalue2會被推導爲整型而不是浮點型
對於以上例子中類型被自動推導的fvalue2,需要注意的是其類型將被自動設爲float64,
而不管賦給它的數字是否是用32位長度表示的。因此,對於以上的例子,下面的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
而必須使用這樣的強制類型轉換:
fvalue1 = float32(fvalue2)
2. 浮點數比較
因爲浮點數不是一種精確的表達方式,所以像整型那樣直接用==來判斷兩個浮點數是否相等
是不可行的,這可能會導致不穩定的結果。
下面是一種推薦的替代方案:
import “math”
// p爲用戶自定義的比較精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}

3.4 複數類型

複數實際上由兩個實數(在計算機中用浮點數表示)構成,一個表示實部(real),一個表示
虛部(imag)。如果瞭解了數學上的複數是怎麼回事,那麼Go語言的複數就非常容易理解了。
1. 複數表示
複數表示的示例如下:

var value1 complex64 // 由2個float32構成的複數類型
value1 = 3.2 + 12i
value2 := 3.2 + 12i // value2是complex128類型
value3 := complex(3.2, 12) // value3結果同 value2

2. 實部與虛部
對於一個複數z = complex(x, y),就可以通過Go語言內置函數real(z)獲得該複數的實
部,也就是x,通過imag(z)獲得該複數的虛部,也就是y。
更多關於複數的函數,請查閱math/cmplx標準庫的文檔。

3.5 字符串

在Go語言中,字符串也是一種基本類型。相比之下, C/C++語言中並不存在原生的字符串
類型,通常使用字符數組來表示,並以字符指針來傳遞。
Go語言中字符串的聲明和初始化非常簡單,舉例如下:
var str string // 聲明一個字符串變量
str = “Hello world” // 字符串賦值
ch := str[0] // 取字符串的第一個字符
fmt.Printf(“The length of \”%s\” is %d \n”, str, len(str))
fmt.Printf(“The first character of \”%s\” is %c.\n”, str, ch)
輸出結果爲:
The length of “Hello world” is 11
The first character of “Hello world” is H.
字符串的內容可以用類似於數組下標的方式獲取,但與數組不同,字符串的內容不能在初始
化後被修改,比如以下的例子:
str := “Hello world” // 字符串也支持聲明時進行初始化的做法
str[0] = ‘X’ // 編譯錯誤
編譯器會報類似如下的錯誤:
cannot assign to str[0]
在這個例子中我們使用了一個Go語言內置的函數len()來取字符串的長度。這個函數非常有
用,我們在實際開發過程中處理字符串、數組和切片時將會經常用到。
本節中我們還順便示範了Printf()函數的用法。有C語言基礎的讀者會發現, Printf()函
數的用法與C語言運行庫中的printf()函數如出一轍。讀者在以後學習更多的Go語言特性時,
可以配合使用Println()和Printf()來打印各種自己感興趣的信息,從而讓學習過程更加直
觀、有趣。
Go編譯器支持UTF-8的源代碼文件格式。這意味着源代碼中的字符串可以包含非ANSI的字
符,比如“ Hello world. 你好,世界!”可以出現在Go代碼中。但需要注意的是,如果你的Go代
碼需要包含非ANSI字符,保存源文件時請注意編碼格式必須選擇UTF-8。特別是在Windows下一
般編輯器都默認存爲本地編碼,比如中國地區可能是GBK編碼而不是UTF-8,如果沒注意這點在
編譯和運行時就會出現一些意料之外的情況。
字符串的編碼轉換是處理文本文檔(比如TXT、 XML、 HTML等)非常常見的需求,不過可
惜的是Go語言僅支持UTF-8和Unicode編碼。對於其他編碼, Go語言標準庫並沒有內置的編碼轉
換支持。不過,所幸的是我們可以很容易基於iconv庫用Cgo包裝一個。這裏有一個開源項目:
https://github.com/xushiwei/go-iconv
1. 字符串操作
平時常用的字符串操作如表所示。

運 算 含 義 樣 例
x + y 字符串連接 “Hello” + “123” // 結果爲Hello123
len(s) 字符串長度 len(“Hello”) // 結果爲5
s[i] 取字符 “Hello” [1] // 結果爲’e’

更多的字符串操作,請參考標準庫strings包。
2. 字符串遍歷
Go語言支持兩種方式遍歷字符串。一種是以字節數組的方式遍歷:
str := “Hello,世界”
n := len(str)
for i := 0; i < n; i++ {
ch := str[i] // 依據下標取字符串中的字符,類型爲byte
fmt.Println(i, ch)
}
這個例子的輸出結果爲:

72
101
108
108
111
44
32
228
184
150
231
149
140

可以看出,這個字符串長度爲13。儘管從直觀上來說,這個字符串應該只有9個字符。這是
因爲每個中文字符在UTF-8中佔3個字節,而不是1個字節。
另一種是以Unicode字符遍歷:
str := “Hello,世界”
for i, ch := range str {
fmt.Println(i, ch)//ch的類型爲rune
}
輸出結果爲:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028
以Unicode字符方式遍歷時,每個字符的類型是rune(早期的Go語言用int類型表示Unicode
字符),而不是byte。

3.6 字符類型

在Go語言中支持兩個字符類型,一個是byte(實際上是uint8的別名),代表UTF-8字符串
的單個字節的值;另一個是rune,代表單個Unicode字符。
關於rune相關的操作,可查閱Go標準庫的unicode包。另外unicode/utf8包也提供了
UTF8和Unicode之間的轉換。
出於簡化語言的考慮, Go語言的多數API都假設字符串爲UTF-8編碼。儘管Unicode字符在標
準庫中有支持,但實際上較少使用。

3.7 數組

數組是Go語言編程中最常用的數據結構之一。顧名思義,數組就是指一系列同一類型數據
的集合。數組中包含的每個數據被稱爲數組元素(element),一個數組包含的元素個數被稱爲數
組的長度。
以下爲一些常規的數組聲明方法:
[32]byte // 長度爲32的數組,每個元素爲一個字節
[2*N] struct { x, y int32 } // 複雜類型數組
[1000]*float64 // 指針數組
[3][5]int // 二維數組
[2][2][2]float64 // 等同於2
從以上類型也可以看出,數組可以是多維的,比如[3][5]int就表達了一個3行5列的二維整
型數組,總共可以存放15個整型元素。
在Go語言中,數組長度在定義後就不可更改,在聲明時長度可以爲一個常量或者一個常量
表達式(常量表達式是指在編譯期即可計算結果的表達式)。數組的長度是該數組類型的一個內
置常量,可以用Go語言的內置函數len()來獲取。下面是一個獲取數組arr元素個數的寫法:
arrLength := len(arr)
元素訪問
可以使用數組下標來訪問數組中的元素。與C語言相同,數組下標從0開始, len(array)-1
則表示最後一個元素的下標。下面的示例遍歷整型數組並逐個打印元素內容:
for i := 0; i < len(array); i++ {
fmt.Println(“Element”, i, “of array is”, array[i])
}
Go語言還提供了一個關鍵字range,用於便捷地遍歷容器中的元素。當然,數組也是range
的支持範圍。上面的遍歷過程可以簡化爲如下的寫法:
for i, v := range array {
fmt.Println(“Array element[“, i, “]=”, v)
}
在上面的例子裏可以看到, range具有兩個返回值,第一個返回值是元素的數組下標,第二
個返回值是元素的值。
- 值類型
需要特別注意的是,在Go語言中數組是一個值類型(value type)。所有的值類型變量在賦值
和作爲參數傳遞時都將產生一次複製動作。如果將數組作爲函數的參數類型,則在函數調用時該
參數將發生數據複製。因此,在函數體中無法修改傳入的數組的內容,因爲函數內操作的只是所
傳入數組的一個副本。
下面用例子來說明這一特點:
package main
import “fmt”
func modify(array [10]int) {
array[0] = 10 // 試圖修改數組的第一個元素
fmt.Println(“In modify(), array values:”, array)
}
func main() {
array := [5]int{1,2,3,4,5} // 定義並初始化一個數組
modify(array) // 傳遞給一個函數,並試圖在函數體內修改這個數組內容
fmt.Println(“In main(), array values:”, array)
}
該程序的執行結果爲:
In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]
從執行結果可以看出, 函數modify()內操作的那個數組跟main()中傳入的數組是兩個不同的實
例。那麼,如何才能在函數內操作外部的數據結構呢?我們將在2.3.6節中詳細介紹如何用數組切
片功能來達成這個目標。

3.8 數組切片

在前一節裏我們已經提過數組的特點:數組的長度在定義之後無法再次修改;數組是值類型,
每次傳遞都將產生一份副本。顯然這種數據結構無法完全滿足開發者的真實需求。
不用失望, Go語言提供了數組切片(slice)這個非常酷的功能來彌補數組的不足。
初看起來,數組切片就像一個指向數組的指針,實際上它擁有自己的數據結構,而不僅僅是
個指針。數組切片的數據結構可以抽象爲以下3個變量:
- 一個指向原生數組的指針;
- 數組切片中的元素個數;
- 數組切片已分配的存儲空間。
從底層實現的角度來看,數組切片實際上仍然使用數組來管理元素,因此它們之間的關係讓
C++程序員們很容易聯想起STL中std::vector和數組的關係。基於數組,數組切片添加了一系
列管理功能,可以隨時動態擴充存放空間,並且可以被隨意傳遞而不會導致所管理的元素被重複
複製。
1. 創建數組切片
創建數組切片的方法主要有兩種——基於數組和直接創建,下面我們來簡要介紹一下這兩種
方法。
基於數組
數組切片可以基於一個已存在的數組創建。數組切片可以只使用數組的一部分元素或者整個
數組來創建,甚至可以創建一個比所基於的數組還要大的數組切片。代碼清單2-1演示瞭如何基
於一個數組的前5個元素創建一個數組切片。
slice.go

package main
import "fmt"
func main() {
// 先定義一個數組
var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 基於數組創建一個數組切片
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()
}
運行結果爲:
Elements of myArray:
1 2 3 4 5 6 7 8 9 10
Elements of mySlice:
1 2 3 4 5

看官大人已經注意到,Go語言支持用myArray[first:last]這樣的方式來基於數組生成一
個數組切片,而且這個用法還很靈活,比如下面幾種都是合法的。
基於myArray的所有元素創建數組切片:
mySlice = myArray[:]
基於myArray的前5個元素創建數組切片:
mySlice = myArray[:5]
基於從第5個元素開始的所有元素創建數組切片:
mySlice = myArray[5:] 直接創建
並非一定要事先準備一個數組才能創建數組切片。 Go語言提供的內置函數make()可以用於
靈活地創建數組切片。下面的例子示範了直接創建數組切片的各種方法。
創建一個初始元素個數爲5的數組切片,元素初始值爲0:
mySlice1 := make([]int, 5)
創建一個初始元素個數爲5的數組切片,元素初始值爲0,並預留10個元素的存儲空間:
mySlice2 := make([]int, 5, 10)
直接創建並初始化包含5個元素的數組切片:
mySlice3 := []int{1, 2, 3, 4, 5}
當然,事實上還會有一個匿名數組被創建出來,只是不需要我們來操心而已。
2. 元素遍歷
操作數組元素的所有方法都適用於數組切片,比如數組切片也可以按下標讀寫元素,用len()
函數獲取元素個數,並支持使用range關鍵字來快速遍歷所有元素。
傳統的元素遍歷方法如下:
for i := 0; i

3.9 map

在C++/Java中, map一般都以庫的方式提供,比如在C++中是STL的std::map<>,在C#中是
Dictionary<>,在Java中是Hashmap<>,在這些語言中,如果要使用map,事先要引用相應的
庫。而在Go中,使用map不需要引入任何庫,並且用起來也更加方便。
map是一堆鍵值對的未排序集合。比如以身份證號作爲唯一鍵來標識一個人的信息,則這個
map可以定義爲代碼清單 2-3所示的方式。
代碼清單2-3 map1.go
package main
import “fmt”
// PersonInfo是一個包含個人詳細信息的類型
type PersonInfo struct {
ID string
Name string
Address string
}
func main() {
var personDB map[string] PersonInfo
personDB = make(map[string] PersonInfo)
// 往這個map裏插入幾條數據
personDB[“12345”] = PersonInfo{“12345”, “Tom”, “Room 203,…”}
personDB[“1”] = PersonInfo{“1”, “Jack”, “Room 101,…”}
// 從這個map查找鍵爲”1234”的信息
person, ok := personDB[“1234”]

// ok是一個返回的bool型,返回true表示找到了對應的數據
if ok {
fmt.Println(“Found person”, person.Name, “with ID 1234.”)
} else {
fmt.Println(“Did not find person with ID 1234.”)
}
}
上面這個簡單的例子基本上已經覆蓋了map的主要用法,下面對其中的關鍵點進行細述。
1. 變量聲明
map的聲明基本上沒有多餘的元素,比如:
var myMap map[string] PersonInfo
其中, myMap是聲明的map變量名, string是鍵的類型, PersonInfo則是其中所存放的值類型。
2. 創建
我們可以使用Go語言內置的函數make()來創建一個新map。下面的這個例子創建了一個鍵
類型爲string、值類型爲PersonInfo的map:
myMap = make(map[string] PersonInfo)
也可以選擇是否在創建時指定該map的初始存儲能力,下面的例子創建了一個初始存儲能力
爲100的map:
myMap = make(map[string] PersonInfo, 100)
關於存儲能力的說明,可以參見2.3.6節中的內容。
創建並初始化map的代碼如下:
myMap = map[string] PersonInfo{
“1234”: PersonInfo{“1”, “Jack”, “Room 101,…”},
}
3. 元素賦值
賦值過程非常簡單明瞭,就是將鍵和值用下面的方式對應起來即可:
myMap[“1234”] = PersonInfo{“1”, “Jack”, “Room 101,…”}
4. 元素刪除
Go語言提供了一個內置函數delete(),用於刪除容器內的元素。下面我們簡單介紹一下如
何用delete()函數刪除map內的元素:
delete(myMap, “1234”)
上面的代碼將從myMap中刪除鍵爲“ 1234”的鍵值對。如果“ 1234”這個鍵不存在,那麼這個調
用將什麼都不發生,也不會有什麼副作用。但是如果傳入的map變量的值是nil,該調用將導致
程序拋出異常(panic)。
5. 元素查找
在Go語言中, map的查找功能設計得比較精巧。而在其他語言中,我們要判斷能否獲取到一
個值不是件容易的事情。判斷能否從map中獲取一個值的常規做法是:
(1) 聲明並初始化一個變量爲空;
(2) 試圖從map中獲取相應鍵的值到該變量中;
(3) 判斷該變量是否依舊爲空,如果爲空則表示map中沒有包含該變量。
這種用法比較囉唆,而且判斷變量是否爲空這條語句並不能真正表意(是否成功取到對應的
值) ,從而影響代碼的可讀性和可維護性。有些庫甚至會設計爲因爲一個鍵不存在而拋出異常,
讓開發者用起來膽戰心驚,不得不一層層嵌套try-catch語句,這更是不人性化的設計。在Go
語言中,要從map中查找一個特定的鍵,可以通過下面的代碼來實現:
value, ok := myMap[“1234”]
if ok { // 找到了
// 處理找到的value
}
判斷是否成功找到特定的鍵,不需要檢查取到的值是否爲nil,只需查看第二個返回值ok,
這讓表意清晰很多。配合:=操作符,讓你的代碼沒有多餘成分,看起來非常清晰易懂。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章