3. Go 數(shù)據(jù)類型及數(shù)據(jù)結(jié)構(gòu)

3. Go 數(shù)據(jù)類型及數(shù)據(jù)結(jié)構(gòu)

前面的幾節(jié)中有意無意地創(chuàng)建了很多變量,在變量聲明過程中,除非聲明過程就初始化硕旗,否則通常需要指明數(shù)據(jù)類型。不同的數(shù)據(jù)類型代表著不同的數(shù)據(jù)處理和表達(dá)方法女责,為了滿足對(duì)現(xiàn)實(shí)世界的刻畫要求漆枚,通常需要?jiǎng)?chuàng)建復(fù)雜的數(shù)據(jù)類型或結(jié)構(gòu),因?yàn)橐粋€(gè)好的數(shù)據(jù)類型或結(jié)構(gòu)可以恰到好處的解決所面臨的問題抵知,所以需要考慮數(shù)據(jù)結(jié)構(gòu)的問題墙基,但無論多么復(fù)雜的數(shù)據(jù)類型或結(jié)構(gòu),都是由簡單的數(shù)據(jù)類型構(gòu)建而來的刷喜,下面逐一進(jìn)行熟悉残制。

Go 簡單數(shù)據(jù)類型

Go 中簡單數(shù)據(jù)類型大概有這么幾類:

  • 布爾型:bool,表示 truefalse
  • 數(shù)值型: complex64 吱肌, complex128痘拆,float32float64氮墨, int 纺蛆, int8int16 规揪, int32 桥氏, int64int128猛铅, unit 字支,unit8uint16uint32 堕伪, uint128揖庄,表示各種大小長度的數(shù)字
  • 字符串類型:string ,表示字符串
  • 指針:Pointer
  • 雜項(xiàng):byte, rune

布爾型

布爾型欠雌,只有兩種值蹄梢,truefalse,但確實(shí)一種重要的表示狀態(tài)的存在富俄,布爾型變量主要進(jìn)行邏輯運(yùn)算禁炒,生成布爾型值的方式除了直接聲明外,多數(shù)都是通過關(guān)系運(yùn)算符獲取的霍比。

關(guān)系運(yùn)算符幕袱,關(guān)系運(yùn)算符用于可比較的兩種類型的值進(jìn)行比較,包括悠瞬, ==, !=, > , >=, <, <=

  • == 檢測(cè)兩個(gè)值是否相等们豌,相等返回 true,否則返回 false
  • != 檢測(cè)兩個(gè)值是否不相等阁危,相等返回 false玛痊,否則返回 true
  • >, >= 比較左端和右端兩個(gè)值的大小,左端大于/大于等于右端的值狂打,返回 true擂煞,否則返回 false
  • <, <= 比較左端和右端兩個(gè)值的大小,左端小于/小于等于右端的值趴乡,返回 true对省,否則返回 false

邏輯運(yùn)行,邏輯運(yùn)算包括三種晾捏,與或非蒿涎,對(duì)應(yīng)符號(hào)為 &&, ||, !
兩個(gè)/一個(gè)布爾型變量做上述運(yùn)行時(shí),與數(shù)學(xué)上的真值表是一致的惦辛。

  • x && y 結(jié)果要為true劳秋,要求 x 為 true 并且 y 為 true,其他均為false
  • x || y 結(jié)果要為true胖齐,要求 x 為 true 或者 y 為 true玻淑,其他均為false
  • !x x為 true!x 就是 false呀伙; 反之补履,x為false,!x 就是 true
    邏輯運(yùn)算的優(yōu)先順序是 非剿另,與箫锤,或贬蛙,但是為了自己邏輯清楚,可以加小括號(hào)表示哪些關(guān)系密不可分谚攒。

注意:Go 中阳准,true,false 并不能和 1, 0進(jìn)行默認(rèn)轉(zhuǎn)換五鲫,必須顯式的進(jìn)行轉(zhuǎn)換溺职。

func i2b(i int) bool {
   if i > 0 {
       return 1
   }
   return 0
}// int to bool
func b2i(b bool) int {
 if b {
       return 1
 }
return 0
}// bool to int

數(shù)值型

Go 中數(shù)值型有兩種岔擂,一種是沒有任何長度標(biāo)記的 int, uint 這兩種位喂,另一種就是有長度標(biāo)記的數(shù)值,例如 int8, uint16乱灵。前者的數(shù)據(jù)長度與平臺(tái)相關(guān)塑崖,后者與平臺(tái)無關(guān),個(gè)人感覺是痛倚,如果你不排斥使用后者规婆,那么你最好使用后者。

對(duì)于整型數(shù)據(jù)來說蝉稳, uintx 的為無符號(hào)整型抒蚜, intx 的為有符號(hào)整型,兩者可以表達(dá)的數(shù)據(jù)范圍是不一致的耘戚。

  • uint8: 無符號(hào) 8 位整型 (0 到 255)
  • uint16: 無符號(hào) 16 位整型 (0 到 65535)
  • uint32: 無符號(hào) 32 位整型 (0 到 4294967295)
  • uint64: 無符號(hào) 64 位整型 (0 到 18446744073709551615)
  • int8: 有符號(hào) 8 位整型 (-128 到 127)
  • int16 有符號(hào) 16 位整型 (-32768 到 32767)
  • int32: 有符號(hào) 32 位整型 (-2147483648 到 2147483647)
  • int64: 有符號(hào) 64 位整型 (-9223372036854775808 到 9223372036854775807)
  • float32:32 位浮點(diǎn)數(shù) (很大)
  • float64:64 位浮點(diǎn)數(shù) (非常大)
  • complex64:實(shí)部和虛部都是 float32 類型的的復(fù)數(shù)
  • complex128:實(shí)部和虛部都是 float64 類型的的復(fù)數(shù)

對(duì)于數(shù)值型變量嗡髓,最為常見的就是進(jìn)行算術(shù)運(yùn)算符,包括 +, -, *, /, %, ++, --, +=, *=, /=

  • % 整數(shù)取余收津,與被取模數(shù)的符號(hào)是一致的;
  • x++, x-- 自增和自減(沒有++x,--x這樣的東東)晌缘;
  • x+=y 就是x = x+y 的簡寫運(yùn)算晦闰,其他幾個(gè)類似;

除此之外吻贿, Go 還支持位運(yùn)算

& 位運(yùn)算 AND
| 位運(yùn)算 OR
^ 位運(yùn)算 XOR
&^ 位清空 (AND NOT)
<< 左移
>> 右移

字符串

字符串就是字符的集合串结,更準(zhǔn)確一點(diǎn),是一個(gè)一旦創(chuàng)建完就不可改變的字節(jié)序列舅列。之前提到肌割,Go 語言字符串使用 UTF-8 編碼實(shí)現(xiàn),此處展開 UTF-8 不太合適剧蹂,但需要知道 UTF-8 是一個(gè)變長編碼声功,可以容納世界上絕大多數(shù)語言及其字符。

字符串長度可以使用 len 來測(cè)量宠叼,字符串支持切片訪問 string[index]先巴,字符串支持通過 + 對(duì)兩個(gè)或多個(gè)字符串進(jìn)行拼接其爵。可以通過循環(huán)對(duì)一個(gè)字符串進(jìn)行遍歷伸蚯。

package main
import "fmt"
func main() {
    b := "Hello 中國"
    fmt.Printf(b+", And have %d length.\n", len(b))
    fmt.Println(b[0:10])
    printStringHex(b)
    printStringOnebyOne(b)

}
func printStringOnebyOne(s string) {
    for index, rune := range s {
        fmt.Printf("%c\t%x\t%d\n", rune, rune, index)
    }
}
func printStringHex(s string) {
    for i := 0; i < len(s); i++ {
        fmt.Printf("%d\t%x\t%c\n", i, s[i], s[i])
    }
}
/*
Hello 中國, And have 12 length.
Hello 中?
0       48      H
1       65      e
2       6c      l
3       6c      l
4       6f      o
5       20
6       e4      ?
7       b8      ?
8       ad      -
9       e5      ?
10      9b      ?
11      bd      ?
H       48      0
e       65      1
l       6c      2
l       6c      3
o       6f      4
        20      5
中      4e2d    6
國      56fd    9
*/

注意對(duì)字符串遍歷的方法摩渺,因?yàn)?UTF-8 變長編碼的緣故,當(dāng)處理英文字符剂邮,編碼位置只有一個(gè)byte時(shí)摇幻,可以正常顯示,但是對(duì)于中文挥萌,無法通過位置索引得到完整的一個(gè)字符绰姻,較好的穩(wěn)妥的辦法使用 range 來遍歷,獲取 rune引瀑。你也可能發(fā)現(xiàn)了狂芋,rune 是一個(gè)數(shù)據(jù)類型,它其實(shí)是 type rune int32, 是一個(gè)足夠容納編碼位置的表達(dá)類型憨栽,按照如下方式訪問字符串也是完全可以的帜矾。

func printChars(s string) {
    runes := []rune(s)
    for i:= 0; i < len(runes); i++ {
        fmt.Printf("%c ",runes[i])
    }
}

有一些回車符,Tab符號(hào)等不可見字符的表達(dá)需要使用轉(zhuǎn)義字符屑柔,這一點(diǎn)同 C 語言一致屡萤。

\a 響鈴
\b 退格
\f 換頁
\n 換行
\r 回車
\t 制表符
\v 垂直制表符
\' 單引號(hào) (只用在 '\'' 形式的rune符號(hào)面值中)
\" 雙引號(hào) (只用在 "..." 形式的字符串面值中)
\\ 反斜杠

為了處理大段文本(里面有很多換行,縮進(jìn)之類的東西)掸宛, Go 還支持一種字面字符串死陆,它也是字符串,不同的是旁涤,它不用 "" 雙引號(hào)表達(dá)翔曲,而是使用使用反引號(hào)代替雙引號(hào)。

const GoUsage = `Go is a tool for managing Go source code.
Usage:
go command [arguments]
...`

字符串是一種最為常見的數(shù)據(jù)類型劈愚,后面還會(huì)有專門的地方涉及到它瞳遍。

指針

指針是一種直接存儲(chǔ)變量的內(nèi)存地址的數(shù)據(jù)類型。一個(gè)指針對(duì)應(yīng)變量在內(nèi)存中的存儲(chǔ)位置菌羽。并不是每一個(gè)值都會(huì)有一個(gè)內(nèi)存地址掠械,但是對(duì)于每一個(gè)變量必然有對(duì)應(yīng)的內(nèi)存地址。通過指針注祖,我們可以直接讀或更新對(duì)應(yīng)變量的值猾蒂,而不需要知道該變量的名字(如果變量有名字的話)。

Go 的指針運(yùn)算

& 返回變量存儲(chǔ)地址, 
// &a; 將給出變量的實(shí)際地址是晨。
* 取出指針變量值肚菠,
// *a; 是一個(gè)取出該地址的值

注意 Go 的指針不允許指針的 ++,--運(yùn)算。 為什么要有指針呢罩缴,因?yàn)橹羔樂奖阄梅辏瑫r(shí)也是為了降低開銷层扶。Go 在處理簡單的數(shù)據(jù)類型時(shí),數(shù)據(jù)之間的轉(zhuǎn)移和傳遞是通過復(fù)制做到的烙荷,就是你把一個(gè)數(shù)據(jù)傳遞給另一個(gè)函數(shù)镜会,那么 Go 會(huì)幫你復(fù)制一份數(shù)據(jù)過去,當(dāng)遇到該數(shù)據(jù)特別大的時(shí)候终抽,又要頻繁傳遞的時(shí)候戳表,就要進(jìn)行大量的復(fù)制,影響性能昼伴。但用指針就不存在該問題匾旭,因?yàn)橹羔樦槐4嬷赶蚰硞€(gè)數(shù)據(jù)的地址,如果需要亩码,找那個(gè)地址的數(shù)據(jù)直接去改季率,不要反復(fù)復(fù)制了。

func main() {
    var oneint int
    var oneptr = &oneint
    var anoptr *int = new(int) //*
    
    fmt.Println(oneint)
    oneint++
    fmt.Println(*oneptr)
    *oneptr += 2
    fmt.Println(oneint)
    fmt.Println(anoptr)
    fmt.Println(*anoptr)
    *anoptr = oneint
    fmt.Println(*anoptr)
}
/* Result:
0 oneint 的默認(rèn)值
1 oneint++
3 *oneptr += 2
0xc00000a090 //anoptr 地址
0 //*anoptr 的默認(rèn)值
3 // *anoptr
*/

上面*標(biāo)處描沟,可利用自動(dòng)推斷為 var anoptr = new(int),此處利用new分配內(nèi)存空間鞭光,否則第四行輸出為nil吏廉,因?yàn)樗兄羔樀哪J(rèn)值為nil,就是空惰许,沒有指向任何地址席覆。對(duì) nil 地址操作 *anoptr = oneint 將引發(fā)panic,這是指針使用的大忌汹买。

Go 類型之間轉(zhuǎn)換

Go 有著非常嚴(yán)格的強(qiáng)類型特征佩伤。Go 沒有自動(dòng)類型提升或類型轉(zhuǎn)換。

package main
import (  
    "fmt"
)
func main() {  
    i := 55      //int
    j := 67.8    //float64
    sum := i + j //不允許 int + float64
    fmt.Println(sum)
}

那該怎么辦……顯式轉(zhuǎn)換晦毙,全部需要顯式轉(zhuǎn)換

package main

import (  
    "fmt"
)

func main() {  
    i := 55      //int
    j := 67.8    //float64
    sum := i + int(j) //轉(zhuǎn)換j為整數(shù)型生巡,當(dāng)然精度就沒有了
    fmt.Println(sum)
}

// 或者
func main() {  
    i := 55      //int
    j := 67.8    //float64
    sum :=j +float64(i) // 轉(zhuǎn)換I到浮點(diǎn)數(shù)
    fmt.Println(sum)
}

把一種類型轉(zhuǎn)換為另一種類型的方法就是 T(v), T為新的類型,例如float64(i)见妒。

Go 符合數(shù)據(jù)類型

數(shù)組

數(shù)組是一個(gè)由固定長度的特定類型元素組成的序列孤荣,一個(gè)數(shù)組可以由零個(gè)或多個(gè)元素組成。

數(shù)組的聲明:var arrayname [n]type // n為確定的數(shù)字须揣,type為簡單的數(shù)據(jù)類型

var stringarray [3]string = [3]string{"Are", "Good", "Yor"} 
// 最為繁重的聲明方式盐股,可以簡化為 var stringarray = [3]string{"Are", "Good", "Yor"} 
for index, value := range stringarray {
    fmt.Printf("%d\t%s\n", index, value)
}
// range函數(shù)會(huì)遍歷stringarray,返回兩個(gè)值耻卡,第一個(gè)是索引疯汁,第二個(gè)是數(shù)值
/* Result
0       Are
1       Good
2       Yor
*/

var uintarray [5]uint
// 只聲明,不初始化
uintarray[0] = 10
uintarray[4] = 100
for i, v := range uintarray {
    fmt.Printf("%d\t%d\n", i, v)
}
/*Result
0       10
1       0
2       0
3       0
4       100
*/  沒有賦值的位置都是 0
floatarray := [...]float32{1:2.17,5: 3.14}
// 包括個(gè)數(shù)也自動(dòng)推導(dǎo)
for _, v := range floatarray {
    fmt.Printf("%g\n", v)
}
// 如果對(duì)索引位置不感興趣卵酪,就用下劃線去代替一個(gè)有意義的名稱幌蚊,這樣就會(huì)丟棄掉那個(gè)值
/* Result
0       0
1       2.17
2       0
3       0
4       0
5       3.14
*/

數(shù)組的長度是不變的秸谢,在聲明就已經(jīng)指定,而且 Go 認(rèn)為 [3]int, [4]int 是兩種不同的數(shù)據(jù)類型霹肝,無法比較估蹄。初始化過程中,可以只初始化想初始化的位置沫换,用{index:value}來指定臭蚁,如果{}給定的值數(shù)量小于[]指定的量,則認(rèn)為初始化前面的幾個(gè)數(shù)讯赏,后面為零垮兑。數(shù)組長度可以使用 len(array) 來獲得,訪問數(shù)據(jù)內(nèi)的元素漱挎,可以通過 array[index] 系枪,即數(shù)組名[索引位置]來訪問,索引位置可以是單個(gè)數(shù)字磕谅,也可以是[start:end] 私爷。數(shù)組是值類型,就是傳遞數(shù)組是通過傳遞數(shù)組的副本做到的膊夹。數(shù)組的遍歷使用 for 下標(biāo)索引或 range 索引都可以衬浑。

這里需要說一下下標(biāo)的使用方法,一般來說 array[index] 是訪問單元素放刨, array[start:end] 是訪問數(shù)組從 start開始到 end-1的那個(gè)元素工秩,如果 start不寫,默認(rèn)從 0 開始进统,如果 end 不寫助币,默認(rèn)到 len()-1的位置,如果都不寫螟碎,那就是 0:len()-1 也就是全部元素眉菱。

其實(shí)還可以創(chuàng)建多維數(shù)組,聲明方法為 var mularray [row][vol]Type 來實(shí)現(xiàn)抚芦,遍歷方式就是嵌套循環(huán)range倍谜,此處不再展開。

func print2darray(a [m][n]string) {
    for row, rv := range a {
        for vol, rvv := range rv {
            fmt.Printf("%d\t%d\t%s\n",row,vol,rvv)
        }
        fmt.Printf("\n")
    }
}

切片/Slice

Slice(切片)代表變長的序列叉抡,序列中每個(gè)元素都有相同的類型尔崔。一個(gè)切片類型一般寫作[]T,其中T代表切片中元素的類型褥民;切片的語法和數(shù)組很像季春,只是沒有固定長度而已。切片是由數(shù)組建立的一種方便消返、靈活且功能強(qiáng)大的包裝(Wrapper)载弄。切片本身不擁有任何數(shù)據(jù)耘拇。它們只是對(duì)現(xiàn)有數(shù)組的引用。一個(gè)切片有三個(gè)要素要關(guān)心宇攻,切片頭(指針)惫叛,長度(len),容量(cap)逞刷。

切片的創(chuàng)建有兩種方式

  1. 從某個(gè)數(shù)組創(chuàng)建嘉涌,s := array[start:end] 0<= start < end <= len(array)-1
  2. 不關(guān)心數(shù)組,s := make([]Type, len, cap) or s:=[]int{v1,v2,...,vn} 夸浅,也不是不想關(guān)心仑最,是關(guān)心不到,反正能正常遍歷各個(gè)值帆喇,為什么要關(guān)心數(shù)組呢警医。

先說第二種方式:

因?yàn)闆]有對(duì)照數(shù)組,單純的說切片特性優(yōu)良坯钦。當(dāng)你創(chuàng)建一個(gè)切片后预皇,你依然可以像數(shù)組一樣,使用下標(biāo)訪問每個(gè)元素葫笼,使用 range 遍歷整個(gè)切片深啤。

既然說了可以變長,那就一定可以添加元素路星,使用 append 函數(shù)追加元素,格式為s = append(s, v1, v2, ...) or s = append(s, s1...) 就是說 append 函數(shù)可以在一個(gè)切片后面追加同類型的元素诱桂,或一個(gè)同類型的切片洋丐。當(dāng)append元素的數(shù)量超過原有切片的容量時(shí),Go 會(huì)自動(dòng)擴(kuò)大容量挥等,擴(kuò)容多少友绝,由一套復(fù)雜的算法決定,可不用關(guān)心肝劲。

從一個(gè)切片上迁客,還可以繼續(xù)創(chuàng)建子切片,subs := s[start:end]0<= start < end <= len(array)-1 辞槐,使用和切片相同掷漱。

理解第二種,就要回來說第一種了榄檬,有點(diǎn)復(fù)雜卜范,看例子說明。

var uintarray [10]uint
uintarray[0] = 0
uintarray[2] = 100
uintarray[4] = 200
uintarray[6] = 300
uintarray[8] = 400
fmt.Println(uintarray, len(uintarray)) // [0 0 100 0 200 0 300 0 400 0] 10

uints := uintarray[1:3]
fmt.Println(uints, len(uints), cap(uints)) //[0 100] 2 9
fmt.Println(uints[0:3], len(uints[0:3]), cap(uints[0:3])) // [0 100 0] 3 9
fmt.Println(uints[1:4], len(uints[1:4]), cap(uints[1:4])) // [100 0 200] 3 8

uints[1] = 250
fmt.Println(uints[1:4], len(uints), cap(uints)) // [250 0 200] 3 8

uints[2] = 350 // panic鹿榜,index out of range

uints = append(uints, 600, 700)
fmt.Println(uints, len(uints), cap(uints)) // [0 250 600 700] 4 9
fmt.Println(uintarray, len(uintarray))     // [0 0 250 600 700 0 300 0 400 0] 10
// 切片是對(duì)數(shù)組的引用海雪,所以通過切片可以修改數(shù)組的值

uints = append(uints, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400)
fmt.Println(uints, len(uints), cap(uints)) 
// [0 250 600 700 600 700 800 900 1000 1100 1200 1300 1400] 13 18
fmt.Println(uintarray, len(uintarray))     
//[0 0 250 600 700 0 300 0 400 0] 10
// 你不是說锦爵,修改切片,數(shù)組就相應(yīng)變了嗎奥裸!怎么這次沒有變险掀? 因?yàn)椤?因?yàn)檫@次添加的數(shù)太多了,超過切片容量湾宙,Go 重新創(chuàng)建了一個(gè)底層數(shù)組樟氢,復(fù)制了原有切片位置的值,添加新的值后返回了新的底層數(shù)組创倔。而原有的數(shù)組被拋棄了嗡害,但我們有一個(gè)可以訪問原有數(shù)組的變量存在,所以發(fā)現(xiàn)數(shù)沒有變化畦攘。

ints := make([]int, 3, 3)
fmt.Println(ints, len(ints), cap(ints)) // [0 0 0] 3 3

aints := []int{1, 2, 3}
fmt.Println(aints, len(aints), cap(aints)) // [1 2 3] 3 3

sumints := append(ints, aints...) 
fmt.Println(sumints, len(sumints), cap(sumints)) // [0 0 0 1 2 3] 6 6

subsumints := sumints[1:3]
fmt.Println(subsumints, len(subsumints), cap(subsumints)) //[0 0] 2 5

subsumints2 := sumints[:5]
fmt.Println(subsumints2, len(subsumints2), cap(subsumints2)) //[0 0 1 2 3] 5 5
// 創(chuàng)建的子切片長度比原有切片長度要大霸妹,但容量是一樣

所以,一個(gè)切片一旦創(chuàng)建知押,無論何種方式創(chuàng)見的切片叹螟,其指針位置就確定了,指針前面的數(shù)據(jù)就永遠(yuǎn)不能訪問了台盯;在切片容量范圍內(nèi)罢绽,可以創(chuàng)建子切片,訪問到由于容量所有數(shù)據(jù)静盅;對(duì)于append 可以增加切片容量良价。切片對(duì)于底層數(shù)組是引用方式,修改切片就修改了底層數(shù)組蒿叠,切片還有一種操作叫做 復(fù)制(copy)明垢,copy(dst, source) 從 source 復(fù)制數(shù)據(jù)到一個(gè) dst 切片,如果想完整復(fù)制 source市咽,則必須保證 dst 切片容量大于等于 source痊银,否則只會(huì)復(fù)制dst容量的數(shù)據(jù)。

Map

Map有多種譯名:字典施绎,哈希表溯革,集合......,其實(shí)都可以谷醉,因?yàn)?Map 是這樣一種結(jié)構(gòu)致稀,非空的Map 里面是一系列鍵值對(duì)(鍵到值的映射),類似于一本字典孤紧;它的鍵是不允許重復(fù)的豺裆,所以它像一個(gè)集合;哈希表是 Map 的實(shí)現(xiàn)方式,它用哈希算法計(jì)算存儲(chǔ)鍵臭猜,使得它可以對(duì)給定的key可以在常數(shù)時(shí)間復(fù)雜度內(nèi)檢索躺酒、更新或刪除對(duì)應(yīng)的value。由于不同的哈希算法生成的排序方式不同蔑歌,所以Map是無序羹应,更為特殊的,在 Go 中次屠,它是故意被設(shè)計(jì)為亂序的园匹,要求程序算法不能依賴于Map遍歷的順序。如果像排序劫灶,最好顯式的獲取Map的鍵裸违,按照需要方式排序,然后再按照鍵去遍歷本昏。Map 是按照引用傳遞的供汛,意思就是無論你在哪里修改一個(gè)Map,其都是對(duì)一個(gè)Map進(jìn)行修改涌穆。Map 無法使用 == 比較怔昨,除非是 MapName == nil

Map 聲明

var mapname map[Ktype]Vtype{Key:Value}

var monthTable map[int]string 創(chuàng)建了一個(gè)從數(shù)字到月份名字的Map

var monthTable map[string]int 創(chuàng)建了一個(gè)從月份名字到數(shù)字的Map

如果沒有利用{Key:Value} 初始化map, 則聲明的Map默認(rèn)值為 nil。如果你想添加元素到 nil map 中宿稀,會(huì)觸發(fā)運(yùn)行時(shí) panic趁舀。因此 map 必須使用 make 函數(shù)初始化。

初始化 monthTable = make(map[int]string)祝沸,所以多數(shù)時(shí)候矮烹,聲明和初始化是一起進(jìn)行的,monthTable := make(map[int]string)

Map 的查詢

Value,ok = MapName[key]

如果可以在給定的Map中查詢到指定 key 的值罩锐,那么返回 value 和 true 擂送,反之,返回值的默認(rèn)零值和 false.

Map 鍵值的添加

MapName[key] = value

如果 Map 初始化過唯欣,那么添加就是即可,如果沒有初始化搬味,panic報(bào)錯(cuò)

Map 鍵值的刪除

delete(key, MapName)

如果Map 中存在該key境氢,則刪除,如果不存在碰纬,上述操作也是安全操作萍聊,最終結(jié)果就是一定沒有該鍵。

package main

import (
    "fmt"
)

func main() {
    var StudentScore map[string]int
    // var StudentScore map[int]string
    fmt.Println(StudentScore, StudentScore == nil, len(StudentScore))

    StudentScore = make(map[string]int)
    fmt.Println(StudentScore, StudentScore == nil, len(StudentScore))

    StudentScore["Xiaoming"] = 98
    StudentScore["Xiaohong"] = 99
    StudentScore["Xiaogang"] = 95
    StudentScore["Xiaohei"] = 0

    fmt.Println("Xiaoming Score:", StudentScore["Xiaoming"])
    fmt.Println(StudentScore, StudentScore == nil, len(StudentScore))

    for k, v := range StudentScore {
        fmt.Println(k, ":", v)
    }

    fmt.Println("Xiaohei Score:", StudentScore["Xiaohei"])
    fmt.Println("JackFly Score:", StudentScore["JackFly"])
    lookupScore("Xiaohei", StudentScore)
    lookupScore("JackFly", StudentScore)

    setScore("JackFly", 100, StudentScore)
    fmt.Println("JackFly Score:", StudentScore["JackFly"])
    setScore("Xiaohei", 100, StudentScore)
    fmt.Println("Xiaohei Score:", StudentScore["Xiaohei"])

    delScore("Xiaohei", StudentScore)
    setScore("Xiaohei", 99, StudentScore)
    fmt.Println("Xiaohei Score:", StudentScore["Xiaohei"])

}

func lookupScore(s string, table map[string]int) {
    Score, ok := table[s]
    if ok {
        fmt.Printf("%s Score: %d, ok Status:%t\n", s, Score, ok)
    } else {
        fmt.Printf("There is no %s Score in Table. ok Status:%t\n", s, ok)
    }
}

func setScore(s string, score int, table map[string]int) {
    if table == nil {
        table = make(map[string]int)
    }
    Score, ok := table[s]
    if ok {
        fmt.Printf("%s already had a score:%d record in table.\n", s, Score)
    } else {
        table[s] = score
        fmt.Printf("%s Score:%d had set in table successfully.\n", s, score)
    }
}

func delScore(s string, table map[string]int) {
    Score, ok := table[s]
    if ok {
        delete(table, s)
        fmt.Printf("%s score:%d had been delete in table.\n", s, Score)
    } else {
        fmt.Printf("There is no %s score record in talbe.\n", s)
    }
}
/*
map[] true 0
map[] false 0 // 初始化后就不是nil
Xiaoming Score: 98
map[Xiaohong:99 Xiaogang:95 Xiaohei:0 Xiaoming:98] false 4 // len可以給出map長度
Xiaoming : 98
Xiaohong : 99
Xiaogang : 95
Xiaohei : 0
Xiaohei Score: 0
JackFly Score: 0
Xiaohei Score: 0, ok Status:true
There is no JackFly Score in Table. ok Status:false
JackFly Score:100 had set in table successfully.
JackFly Score: 100
Xiaohei already had a score:0 record in table.
Xiaohei Score: 0
Xiaohei score:0 had been delete in table.
Xiaohei Score:99 had set in table successfully.
Xiaohei Score: 99
*/

結(jié)構(gòu)體

結(jié)構(gòu)體是一種聚合的數(shù)據(jù)類型悦析,是由零個(gè)或多個(gè)任意類型的值聚合成的新數(shù)據(jù)類型寿桨,以滿足描述算法或?qū)ο蟮氖褂妹枋鲆螅渲薪Y(jié)構(gòu)體的每個(gè)值稱為結(jié)構(gòu)體的成員。

結(jié)構(gòu)體的定義與聲明 亭螟, 結(jié)構(gòu)體內(nèi)成員的聲明順序是有意義的挡鞍,不同順序成員的結(jié)構(gòu)體是不同的結(jié)構(gòu)體。成員數(shù)據(jù)類型可以是前面的任意一種數(shù)據(jù)類型预烙,甚至可以是另一個(gè)結(jié)構(gòu)體(但不可以是自己)墨微。

// 結(jié)構(gòu)體定義
type StructName struct{
    feildName1 TypeA
    feildName2, feildName3 TypeB
    feildName3, TypeC
} // 帶有成員名稱(字段)的定義方式
type StructName struct{
    TypeA
    TypeB
    TypeC
} // 不帶字段的定義方式
// 結(jié)構(gòu)體聲明
StructureV := StructureName{
    feildName1: Value1,
    feildName2: Value2,
    feildName3: Value3,// 不一定要全部賦值扁掸,如果不賦值翘县,Go 默認(rèn)初始化為0
}
StructureV := StructureName{Value1, Value2, Value3}//由此注意結(jié)構(gòu)體的聲明成員的的順序很重要,

創(chuàng)建匿名的結(jié)構(gòu)體
StructureV :=struct{
    feildName1 TypeA
    feildName2, feildName3 TypeB
    feildName3谴分, TypeC
}{
    feildName1: Value1,
    feildName2: Value2,
    feildName3: Value3,
}

結(jié)構(gòu)體成員的訪問

訪問結(jié)構(gòu)體的成員是通過. 來操作的锈麸,即 structV.fieldNameA ,用來讀寫都是可以的

結(jié)構(gòu)體取地址及地址訪問

結(jié)構(gòu)體可以取地址牺蹄,結(jié)構(gòu)體的成員也可以單獨(dú)取地址忘伞,通常通過地址訪問成員,需要先對(duì)結(jié)構(gòu)體使用 * 取值運(yùn)算钞馁,但 Go 會(huì)自動(dòng)對(duì)結(jié)構(gòu)體地址進(jìn)行取值 虑省,簡化了結(jié)構(gòu)體的表達(dá),如下:structptr := &structName{}, (*structptr).fieldName 等價(jià)于 structptr.feildName

結(jié)構(gòu)體內(nèi)包含另一個(gè)結(jié)構(gòu)體

structName{substructName, fnc typec}時(shí)僧凰,如果另一個(gè)結(jié)構(gòu)體substructName{fna typea, fnb typeb}沒有名稱探颈,那么Go 會(huì)提升字段,就是將substructName 中的成員训措,當(dāng)成自己的成員伪节,可以通過. 直接訪問。但是對(duì)于有名稱的結(jié)構(gòu)體绩鸣,則必須使用雙重點(diǎn)號(hào)訪問子結(jié)構(gòu)體的成員怀大。structName{sub substructName, fnc type}, structName.substructName.fna

結(jié)構(gòu)體雖然沒有辦法包含自身在內(nèi),但是可以包含自身的指針在內(nèi)呀闻。

package main

import (
    "fmt"
)

type address struct {
    city, road string
    number     int
    Neighbor   *address
}

type student struct {
    Name   string
    gender bool // true for boy, false for girl
    age    int
    hight  float32
}
type studentDetail struct {
    student
    homeAddr address
}

func main() {
    xiaoming := student{"xiaoming", true, 10, 1.1}
    fmt.Println(xiaoming)
    fmt.Printf("%+v\n", xiaoming)
    printfstu(xiaoming)

    xiaominginfo := &xiaoming
    fmt.Println((*xiaominginfo).gender)
    fmt.Println(xiaominginfo.age)

    fmt.Println("A Year Later... ")
    xiaominginfo.age++
    xiaominginfo.hight = 1.2
    printfstu(xiaoming)

    xiaomingDetail := studentDetail{
        student: xiaoming,
        homeAddr: address{
            city:   "Beijing",
            road:   "ChangAn",
            number: 1,
            //Neighbor 是可以被省略的
        },
    }
    fmt.Printf("%+v\n", xiaomingDetail)
    printfstuD(&xiaomingDetail)

    xiaohongDetail := &studentDetail{student{"xiaohong", false, 10, 1.1}, address{"Beijing", "Changan", 2, &xiaomingDetail.homeAddr}} // 地址不可省略

    fmt.Println("xiaohong Age:", xiaohongDetail.age)
    fmt.Println("xiaohong home address:", xiaohongDetail.homeAddr.road, xiaohongDetail.homeAddr.number)

    fmt.Println("xiaohong Neighbor:", *xiaohongDetail.homeAddr.Neighbor)

}

func printfstu(stu student) {
    gendermap := map[bool]string{
        false: "girl",
        true:  "boy",
    }
    fmt.Printf("------------\n%s\nGender:%s\nAge:%d\nHight:%g\n", stu.Name, gendermap[stu.gender], stu.age, stu.hight)
}
func printfstuD(stuD *studentDetail) {
    gendermap := map[bool]string{
        false: "girl",
        true:  "boy",
    }
    fmt.Printf("------------\n%s\n - Gender: %s\n - Age: %d\n - Hight: %g\n - Address: %s, %s Road, No. %d\n", stuD.Name, gendermap[stuD.gender], stuD.age, stuD.hight, stuD.homeAddr.city, stuD.homeAddr.road, stuD.homeAddr.number)
}

由于這一節(jié)打印了很多不同類型的值化借,其格式化的方式都有所區(qū)別,可以通過這個(gè) 簡單介紹 簡單了解一下捡多,后文會(huì)有機(jī)會(huì)全面理解蓖康。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垒手,隨后出現(xiàn)的幾起案子蒜焊,更是在濱河造成了極大的恐慌,老刑警劉巖科贬,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泳梆,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)优妙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門乘综,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳞溉,你說我怎么就攤上這事瘾带。” “怎么了熟菲?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵看政,是天一觀的道長。 經(jīng)常有香客問我抄罕,道長允蚣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任呆贿,我火速辦了婚禮嚷兔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘做入。我一直安慰自己冒晰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布竟块。 她就那樣靜靜地躺著壶运,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浪秘。 梳的紋絲不亂的頭發(fā)上蒋情,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音耸携,去河邊找鬼棵癣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夺衍,可吹牛的內(nèi)容都是我干的狈谊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沟沙,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼的畴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尝胆,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎护桦,沒想到半個(gè)月后含衔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年贪染,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缓呛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杭隙,死狀恐怖哟绊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痰憎,我是刑警寧澤票髓,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站铣耘,受9級(jí)特大地震影響洽沟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜗细,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一裆操、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炉媒,春花似錦踪区、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至水援,卻和暖如春密强,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜗元。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國打工或渤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奕扣。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓薪鹦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惯豆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子池磁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,148評(píng)論 0 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理楷兽,服務(wù)發(fā)現(xiàn)地熄,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 想必大家都聽說過三十六計(jì)吧芯杀。今天我看了一本書端考,就是關(guān)于三十六計(jì)的雅潭。下面我就跟你們講解。三十六計(jì)的前六計(jì)却特》龉“...
    劉子城閱讀 294評(píng)論 0 0
  • 閑來無事時(shí)椿浓,喜歡追兩眼電視劇。最近熱播的<那年花開月圓時(shí)>自然不會(huì)錯(cuò)過闽晦。劇中周瑩有眾多男性粉絲扳碍,沈星移又英俊而倜儻...
    貪睡的木棉閱讀 382評(píng)論 2 4