跟著老貓來搞GO-內建容器Map

程式設計師老貓發表於2021-11-22

前期回顧

在上面的文章中,老貓和大家分享了GO語言中比較重要的兩種資料結構,一種是陣列,另外一種是基於陣列的slice。本篇文章想要繼續和大家分享剩下的容器以及字元字串的處理。

MAP

map的定義

在Java語言中其實還有一個鍵值對的容器,叫做Map,當然在我們GO語言中也有這種資料結構,例如,下面我們們就來看一下GO語言中Map的寫法以及用法。看下一Map的語法定義,如下例子:

m :=map[string]string {
		"name":"ktdaddy",
		"age":"28",
		"job":"softEngineer",
}

關於語法的定義其實也是比較簡單的,方括號裡面的表示key值,後面的那個型別表示value的型別。上面老貓其實定義了一個map,並且給它初始化了一些資料。定義一個空的map其實也是比較簡單的,主要有下面兩種方式

m1 :=make(map[string]int) //採用make函式直接建立一個map,比較常用
var m2 map[string] int //用var函式申明的方式建立map

說實在的語法這種東西也沒法說出什麼所以然來,大家既然想要學習這個語言就要去記下它的規則,所以還是得多寫多練,長時間不去用可能就忘記了。

總結一下其定義形式map[k]V,當然我們們也可以定義它比較複雜一些的資料結構,例如map裡面套map,這種複合型的,那麼定義的最終形式就是map[K1]map[K2]V。

對map的操作

查詢元素

我們定義出一個map之後,正常的邏輯就要去操作這個map,我們們先來看一下如何遍歷一個map,我想大家比較容易想到的就是我們講array的時候用的range函式的方式。確實,我們們可以寫成這樣,我們們就以一開始定義的m這個Map為例吧

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

這樣我們就可以獲取所有的資料,當然我們也可以根據對應的Key值獲取相關的value

name :=m["name"]

但是很多時候,我們是不能知道這個key是否存在的,我們取值的時候就要先判斷當前的key是否存在,然後再去獲取值,其實我們可以這樣寫

name,ok :=m["name"]
fmt.Println(name,ok) //結果輸出ktdaddy true

很顯然,我們可以通過ok的值去判斷當前的key是否存在,然後再去獲取其中的value值。那麼結合前面所講的if條件語句,我們們就可以寫出這樣的邏輯

if name,ok :=m["name"];ok {
	fmt.Println(name)
}else {
	fmt.Println("key does not exist")
}
刪除元素

刪除元素其實是相當簡單的,用內建函式delete函式即可。我們還是承接上面的邏輯,如果發現存在這個Key值,那麼我們就從map中進行移除。具體如下

if name,ok :=m["name"];ok {
		delete(m,"name")
		name,ok = m["name"]
		fmt.Println(name,ok)
	}else {
		fmt.Println("key does not exist")
}

上述刪除之後我們們重新賦值map,這樣這個程式碼最終輸出的結果就是false。大家可以實際動手練習一下。

字串的處理

老貓想過好幾次,如何寫這個字串,想要將其單獨抽出來說感覺也不合適,因為我們們很多時候會遍歷容器中的字元,因此索性就將其合併在內建容器裡面一起分享好了。這裡主要想和大家分享的是rune這個內建型別,從前面的章節中我們都知道這個相當於是Go的char,那麼這個又怎麼來用呢?我們們先來看下面例子

s:= "hello我愛GO開發"
fmt.Println(len(s))

大家覺得這個應該會輸出多少呢?試著執行一下,答案是19,那麼為什麼呢?我們再接著轉換成位元組輸出一下

s:= "hello我愛GO開發"
fmt.Println(s)
fmt.Println(len(s))
fmt.Printf("%X\n",[]byte(s))

這樣呢,我們就獲取一段16進位制的位元組,結果如下

68656C6C6FE68891E788B1474FE5BC80E58F91

很顯然,我們還是無法看出來,我們將其格式做一下分割調整來看一下

s:= "hello我愛GO開發"
fmt.Println(s)
fmt.Println(len(s))
fmt.Printf("%X\n",[]byte(s))

for _,b:=range []byte(s) {
    fmt.Printf("%X ",b)
}

執行的結果為 :68 65 6C 6C 6F E6 88 91 E7 88 B1 47 4F E5 BC 80 E5 8F 91

數一下成對出現的16進位制數,我們們就可以發現19個位元組長度原來是這麼來的。通過分析我們不難發現一個英文佔用了兩個位元組而中文則佔用了3個位元組,例如裡面的“我”轉換之後就是:E6 88 91,那麼我們直接輸出s又會是什麼呢?

for i,ch := range s{ // ch is a rune
	 	fmt.Printf("(%d %X) ",i,ch)
}
fmt.Println()

結果為:

(0 68) (1 65) (2 6C) (3 6C) (4 6F) (5 6211) (8 7231) (11 47) (12 4F) (13 5F00) (16 53D1) 

其實通過68 65 6C 6C 6F E6 88 91 E7 88 B1 47 4F E5 BC 80 E5 8F 91 轉換成了上面這個是通過什麼轉換的呢?其實通過utf-8轉換成了unicode編碼,我們發現轉換成unicode之後,相關的中文字元程式設計了長度為4的16進位制數,那麼其實就是相當於是rune所佔的位元組長度了。

所以我們直接用rune對其進行轉換。

for i,ch :=range []rune(s){
		fmt.Printf("(%d %c)",i,ch)
}

這樣我們就得到了如下

(0 h)(1 e)(2 l)(3 l)(4 o)(5 我)(6 愛)(7 G)(8 O)(9 開)(10 發)

如此,其實就遍歷獲取到了每個字元了。

當然,如果我們要統計utf-8下的字元長度,其實我們要對其進行轉換,在此介紹兩個函式

utf8.RuneCountInString(s)
utf8.DecodeRune(bytes)

通過這兩個函式,我們們可以獲取中文字元下的具體的字元個數了。

看起來比較零碎,我們們做一下總結,當然剩下還是得大家自己寫一下。

  1. 使用range遍歷pos,rune對
  2. 使用utf8.RuneCountInString(s)獲取字元數量
  3. 使用len獲取位元組長度
  4. 使用[]byte獲取位元組

總結

本次主要和大家分享了內建容器的剩餘部分map的定義以及相關的操作使用,後面還和大家穿插了部分字串的處理方式,當然還是比較散的,後面在寫專案的過程中,我們們到時候在具體用起來,所以在此希望大家持續關注老貓,另外的也希望大家多練習,一起進步,加油。

我是老貓,更多內容,歡迎大家搜尋關注老貓的公眾號“程式設計師老貓”。

相關文章