GO 複合數據類型

複合數據類型

一、數組
1、聲明
   var array [count]type
2、訪問、修改
    array[index]  index在[0,len(array)-1] 範圍內取值
3、拷貝
    array 是值類型
4、多維數組
    var arrays[count][count]type
5、作爲參數傳遞(指針數組)
    func function(array *[count]type)   花費 8 字節內存
二、切片(slice)
1、創建
     1.1 通過內置函數 make() 創建切片
         slice := make([]type,count)
     1.2 通過字面量創建切片
         slice :=[]type{elemtnt1,element1,...}
     1.3 nil 和 空切片
       1.31. nil
             var nilSlice []type
       1.3.2 empty slice     
             emptySlice := make([]type,0)
             emptySlice := []type{}
2、切片的使用
   2.1 賦值
        slice[index] = value,  index在[0,len(slice)-1] 中取值
   2.2 分割
       newSlice := slice[from,to]  ,包含頭不包含尾,即 [from,to)
   2.3 擴容
     newSlice := append(slice,value1,value2,....)
   2.4 遍歷
     for  index,value range slice{}
   2.5 限制容量
    slice := souceSlice[i:j:k]
    對底層數組容量是 k 的切片 souceSlice[i:j:k] 來說,長度爲 j - i, 容量爲 k - i
   2.6 多維切片
     slice := [][]{{emement1,emement2,...},{emement1,emement2,...}}
   2.7 切片作爲函數參數傳遞
         只會複製切片本身,並不會涉及底層數組。花費 24B 內存
3、切片是數組的一個視圖(view)
    3.1 view
 4、排序
     4.1 基本書序類型
          升序
          sort.Ints(slice)
          sort.Float64s(slice)
          sore.Strings(slice)
          降序
          sort.Sort(sort.Reverse(sort.IntSlice(slice)))
          sort.Sort(sort.Reverse(sort.Float64Slice(slice)))
          sort.Sort(sort.Reverse(sort.StringSlice(slice)))
     4.2 自定義數據類型   
        對象實現 sort 包中的 Interface 接口
        type Interface interface {
            //比較對象所在切片的長度
            Len() int
            //按照某種規則排序
            Less(i, j int) bool
            //交換位置
            Swap(i, j int)
         }
三、映射 (map)
1、創建
  1.1 通過內置函數 make() 創建映射
      dictionary := make(map[type]type)
  1.2 通過字面量創建映射
      dictionary := map[type]type{"key1":"value1",....}
  1.3 nil 和 空映射
      1.3.1 nil
            var nilMap = map[type]type
      1.3.2 empty map
            emptyMap := make(map[type]type)
            emptymap := ma[type]type{}   
2、增加、修改
   map["key"] = value
3、查
     3.1  獲取值以及表示這個鍵是否存在的標誌( 推薦 )
     value,exists := map[key]
      if exist{
         fmt.println(value) 
      }
      3.2 返回鍵對應的值,判斷這個值是否是對應類型的零值。是,不存在。否則,存在
      value := map[key]
      if value != typeZero{
         fmt.println(value) 
      }
5、刪
     delete(map,key)
6、遍歷
  for key,value := range map{
     fmt.println(key,value)
  }   
  7、map 作爲參數傳遞給函數
    與切片類似  

GO 語言的 3 種複合數據類型可以讓開發者管理數據集合,這 3 種數據類型也是 GO 語言核心的一部分,在標準庫裏被廣泛運用。3 種複合數據類型分別爲: 數組、切片、映射。

一、數組
瞭解數據結構,一般從數組開始。因爲數組是切片和映射的基礎數據結構。理解數據的工作原理,有助於理解切片和映射提供的優雅強大的功能。

數組是具有相同類型的一組已經編號且長度固定的數據項序列。這個序列可以是有序的也可以是無序的,組成數組的各個變量稱爲數組的元素。

1、聲明
var array [count]type
數組一旦聲明,它存儲的數據類型和長度就都不能修改。如果需要存儲更多的元素,必須另建一個更長的數組,把原來的數組裏的值複製到新數組。
數組初始化時,其每個元素都被初始化爲對應類型的零值

func declareArrays()  {

	//1.聲明一個長度爲 5 的整型數組並初始化
	var arrays [5]int = [5]int{1,2,3,4,5}
	fmt.Printf("arrays 的長度 = %d\n",len(arrays))
	for index,value := range arrays{
		fmt.Printf("arrays[%d] = %d \n",index,value)
	}
	//2.聲明的變種(容量由初始化值的數量決定)
	arr := [...]int{1,2,3}
	fmt.Printf("arr 的長度 = %d\n",len(arr))
    
	//3.聲明的變種(初始化索引爲 0 和 4 的元素,其餘元素保持零值)
	arr1 := [5]int{0:12,4:11}
	fmt.Printf("arr1 的長度 = %d\n",len(arr1))
	for index := 0;index < len(arr1);index++ {
		fmt.Printf("arr1[%d] = %d \n",index,arr1[index])
	}
}
輸出結果:
arrays 的長度 = 5
arrays[0] = 1 
arrays[1] = 2 
arrays[2] = 3 
arrays[3] = 4 
arrays[4] = 5 

arr 的長度 = 3

arr1 的長度 = 5
arr1[0] = 12 
arr1[1] = 0 
arr1[2] = 0 
arr1[3] = 0 
arr1[4] = 11 

2、訪問、修改

2.1 訪問
通過下標訪問,下標的取值區間在 [0,len(arrays)-1],超過這個範圍訪問數組元素會發生數組越界。

func visitArrays()  {
	 arrays := [3]int{1,4,5}
	 fmt.Printf("arrays[1] = %d\n",arrays[1])
	 //數組越界: out of bounds for 3-element array
	 //fmt.Printf("arrays[3] = %d\n",arrays[3])
}

2.2 修改
數組是效率很高的數據結構,因爲數組在內存中分配是連續的,要訪問數組裏的某個單獨元素,使用[]運算符即可。

func updateArraysElement()  {

	arrays := [5]int{1,4,5}
	fmt.Printf("修改前 arrays = %v\n",arrays) 
	arrays[3] = 12
	arrays[0] = 10
	fmt.Printf("修改後 arrays = %v\n",arrays)
}

輸出結果:

修改前 arrays = [1 4 5 0 0]
修改後 arrays = [10 4 5 12 0]

3、拷貝

在 GO 語言裏,數組是值類型,這意味着數組也可以像函數一樣用在賦值操作中,變量名代表整個數組,因此同樣類型(類型相同、長度相等)的數組可以賦值給另一個數組(相當於將一組值拷貝放另外一個容器裏)。

如果賦值數組指針,只會複製指針的值,而不會複製指針所指向的值。複製操作之後,兩個數組指向同一組值。

func copyArray()  {

	arrays := [3]*string{new(string),new(string),new(string)}
	for index, value := range arrays {
		fmt.Printf("默認初始化 :arrays[%d] = %q\n",index,*value)
	}

	*arrays[0] = "red"
	*arrays[1] = "blue"
	*arrays[2] = "yellow"

	fmt.Println()
	for index, value := range arrays {
		fmt.Printf("初始化後 :arrays[%d] = %q\n",index,*value)
	}

	//拷貝並修改
	var arr [3]*string = arrays
	*arr[0] = "black"

	fmt.Println()

	for index, value := range arrays {
		fmt.Printf("修改後 :arrays[%d] = %s\n",index,*value)
	}
}

輸出結果:

默認初始化 :arrays[0] = ""
默認初始化 :arrays[1] = ""
默認初始化 :arrays[2] = ""

初始化後 :arrays[0] = "red"
初始化後 :arrays[1] = "blue"
初始化後 :arrays[2] = "yellow"

修改後 :arrays[0] = black
修改後 :arrays[1] = blue
修改後 :arrays[2] = yellow

4、多維數組
這裏不多介紹了,自行擴展。跟主流編程語言的多維數組一個意思

5、作爲參數傳遞
在 GO 語言中數組是值類型,所有的值類型變量在賦值和作爲參數傳遞時都將產生一次複製動作
如果直接將數組作爲函數參數,則在函數帶掉用時會複製一份作爲函數參數,在函數體內無法修改傳入的數組的內容,因爲函數內部操作的只是所傳入數組的一個副本。這樣的話,從性能和內存的角度來看,在函數間傳遞數組是一個開銷很大的動作,特別是當數組長度非常大時,當它作爲函數參數,都會完整複製一份並傳遞給函數。

在 64 位架構上,創建一個包含 100 萬個 int 類型元素數組,並將其傳遞給函數,需要花費 8 MB 內存。

類型 所佔字節數
int8 1
int16 2
int32 4
int64 8
float32 4
float64 8

如果,我將 數組的指針 做爲函數參數的話,只需複製 8 字節的數據而不是 8 MB 的內存數據到棧上。這個操縱更有效利用內存,不過要注意,現在傳遞的是指針,這意味着如果改變指針指向的值,會改變共享內存中的值。

func arraysAsParams()  {

	arr :=  [5]int{1,2,5}
	fmt.Printf("arr = %v\n",arr)
	editArraysFunction(&arr)
	fmt.Printf("arr = %v\n",arr)
}

func editArraysFunction(arrays *[5]int)  {
	(*arrays)[0] = 50
}

輸出結果

arr = [1 2 5 0 0]
arr = [50 2 5 0 0]

爲了解決這個問題,就需要使用切片來更好處理這類問題。

二、切片
切片是 GO 語言中比較特殊的數據結構。這種數據結構更便於使用和管理數據集合。切片是圍繞動態數組的改建構建的,可以按需自動增加和縮小。切片的動態增長是通過內置函數 append()來實現的,這個函數可以快速且高效增加切片,也可以對切片再次分割,縮小一個切片的大小。因爲切片底層內存也是在連續內存中分配的,所以切片還能獲得索引、迭代、以及爲垃圾回收優化的好處。

1、創建
GO 語言中創建切片的方式有多種,而能否確定切片的容量(capacity)是創建切片的關鍵,它決定以何種方式創建切片。
1.1 通過內置函數 make() 創建切片

func mackSlice()  {

	// 元素類型爲 int,長度爲 5,容量爲 5
	slice := make([]int,5)
	fmt.Printf("長度 = %d,容量 = %d slice = %v  \n",len(slice),cap(slice),slice)
	// 元素類型爲 string,長度爲 3,容量爲 5
	arrays := make([]string,3,5)
	fmt.Printf("長度 = %d,容量 = %d arrays = %v arrays[1] = %q\n ",len(arrays),cap(arrays),arrays,arrays[1])

}

輸出結果:

長度 = 5,容量 = 5 slice = [0 0 0 0 0]  
長度 = 3,容量 = 5 arrays = [  ] arrays[1] = ""

注意: 不允許創建 長度 > 容量 的切片,否則會報錯

    //報錯 len larger than cap in make([]int)
    unlegalSlice := make([]int,10,5)

1.2 通過字面量創建切片
和創建數組類似,只是不需要指定[] 運算符裏的值,初始的長度和容量相等,都等於初始提供元素的個數。當然,你也可以設置容量和長度,就是在初始化的時候給出所需長度和容量作爲索引。

func literalMeasureSlice()  {
	//元素類型爲 int,長度爲 3,容量爲 3
	slice1 := []int{1,10,50}
	fmt.Printf("長度 = %d,容量 = %d \n",len(slice1),cap(slice1))
	//元素類型爲 int ,長度爲 100,容量爲 100,slice2[99] = 1
	slice2 :=[]int{99:1}
	fmt.Printf("長度 = %d,容量 = %d slice[99] = %d\n",len(slice2),cap(slice2),slice2[99])
}

輸出結果:

長度 = 3,容量 = 3 
長度 = 100,容量 = 100 slice[99] = 1

1.3 nil 和 空切片

func nilSliceAndEmptySlice()  {

	//創建一個 nil 整型切片
	var nilSlice []int
	if nilSlice == nil {
		fmt.Println(" nilSlice is nil slice")
	}

	//創建空切片。空切片底層數組包含 0 個元素,也沒有分配任何控件,常用於表示空集合(例如:數據庫查詢返回0個查詢結果)
	emptySlice1 := []int{}
	emptySlice2 := make([]int,0)
	if  emptySlice1 != nil && emptySlice2 != nil {
		fmt.Println("emptySlice1 and emptySlice2 is empty slice")
	}
}

備註 : nil 切片和普通切片一樣,調用內置函數 append、len 和 cap 的效果都是一樣的。

2、切片的使用

2.1 賦值
給切片的某個元素賦值和給數組的某個元素賦值在方法上完全一樣,使用[]運算符就可以改變某個元素的值。

func sliceElementAssignValue()  {
	slice := []int{1,2,3}
	slice[0] = 10
	fmt.Println(slice) //[10 2 3]
}

2.2 分割
切片之所以稱爲切片,是因爲創建一個新切片就是把底層數組切出一部分出來。他們共享同一段底層數組,但通過不同的切片會看到底層數組的不同部分。

func sliceSplit() {
	slice := []int{10, 20, 30, 40, 50}
	newSlice := slice[1:3] //[1,3)
	//
	newSlice2 := slice[2:4:4]
	fmt.Printf("slice = %v,newSlicev = %v,newSlice2 = %v \n",slice,newSlice,newSlice2)
	fmt.Printf("len(slice) = %d,cap(slice) = %d \n",len(slice),cap(slice))
	fmt.Printf("len(newSlice) = %d,cap(newSlice) = %d \n",len(newSlice),cap(newSlice))
	fmt.Printf("len(newSlice2) = %d,cap(newSlice2) = %d \n",len(newSlice2),cap(newSlice2))
	//切片只能訪問到其長度內的元素。試圖訪問超出其長度的元素會發生運行時異常 => index out of range [2] with length 2
	//fmt.Println(newSlice[2])
}

輸出結果:

slice = [10 20 30 40 50],newSlicev = [20 30],newSlice2 = [30 40] 
len(slice) = 5,cap(slice) = 5 
len(newSlice) = 2,cap(newSlice) = 4 
len(newSlice2) = 2,cap(newSlice2) = 2 

第一個切片能夠訪問底層數組全部 5 個元素,不過之後的 newSlice 就訪問不到。對於 newSlice ,底層數組的容量只有 4 個元素。newSlice無法訪問到他所指向的底層數組的第一個元素。換言之, 對於newSlice 來說,slice[0] 是不存在的。

如果兩個切片共享同一底層數組,如果一個切片修改了底層數組共享部分,另外一個切片也會受影響。

func modifyShareArraysOfSlice()  {

	slice := []int{10,20,30,40,50}
	newSlice :=  slice[1:3]
	slice[1] = 3
	 //屏蔽的代碼打開, slice[2] = 3
	//newSlice[1] = 3
	fmt.Printf("slice = %v,newSlice = %v \n",slice,newSlice)
	//slice = [10 20 3 40 50],newSlice = [20 3] 
}

總的來說 : 切片有額外容量是好的,但是如果不能把容量合併到切片的長度裏,這些容量就沒有用處。

2.3 擴容
相對於數組而言,使用切片的一個好處就是可以按需增加數據集合的容量。GO 語言內置的append()函數會處理增加長度時所有的細節。

如果切片數組沒有足夠的可用容量,append()函數會創建一個新的底層數組,將被引用的現有值複製到新數組裏,再追加新的值。

函數append()會智能處理底層數組的容量,使其增長。在切片容量小於 1000 個元素,總是成倍增加容量。一旦元素超過 1000 ,容量的增長因子會設 爲 1.25 ,也就是每次增加 25% 容量。

func sliceAppend(){
	slice := []int{10,20,30,40,50}
	newSlice  := slice[1:4] //[1,4)
	fmt.Printf("len(newSlice) = %d,cap(newSlice) = %d,newSlice = %v\n",len(newSlice),cap(newSlice),newSlice)
	//切片只能訪問到其長度內的元素。視圖訪問超出其長度的元素會發生運行時異常 => index out of range [3] with length 3
	//fmt.Println(newSlice[3])
	//通過 append()函數 向 newSlice追加新元素,原來 slice[4] = 4。原因: 共用同一底層數組
	newSlice = append(newSlice, 4)
	fmt.Printf("len(newSlice) = %d,cap(newSlice) = %d,newSlice = %v\n",len(newSlice),cap(newSlice),newSlice)
	// 當繼續追加時,底層數組沒有足夠的容量,append()會創建一個新的底層數組,將現有的值拷貝到新數組裏面再追加新的值,容量爲原來的2倍
	newSlice = append(newSlice, 5)
	fmt.Printf("len(newSlice) = %d,cap(newSlice) = %d,newSlice = %v\n",len(newSlice),cap(newSlice),newSlice)
	//此時: 上面的 slice  和 newSlice 已經不公用同一底層數組,所以 newSlice[3] = 50,並不會修改 slice[3] 的值
	newSlice[3] = 60
	fmt.Printf("slice = %v,newSlice = %v\n",slice,newSlice)
}

輸出結果:

len(newSlice) = 3,cap(newSlice) = 4,newSlice = [20 30 40]
len(newSlice) = 4,cap(newSlice) = 4,newSlice = [20 30 40 4]
len(newSlice) = 5,cap(newSlice) = 8,newSlice = [20 30 40 4 5]
slice = [10 20 30 40 4],newSlice = [20 30 40 60 5]

2.4 遍歷
有一個注意點: range 創建的是每個元素的副本,而不是返回該元素的引用。如果要獲取每一個元素的地址,使用&slice[index]

func  rangeSlice()  {

	slice := []int{10,20,30,40,50}
	for index,value := range slice {
		fmt.Printf("value = %d,valueAddr = %X,ElemAddr = %X\n",value,&value,&slice[index])
	}
}

輸出結果:

value = 10,valueAddr = C000016050,ElemAddr = C00001E120
value = 20,valueAddr = C000016050,ElemAddr = C00001E128
value = 30,valueAddr = C000016050,ElemAddr = C00001E130
value = 40,valueAddr = C000016050,ElemAddr = C00001E138
value = 50,valueAddr = C000016050,ElemAddr = C00001E140

2.5 限制容量
在創建切片時,還可以使用之前沒有提及的第三個索引選項。第三個索引選項可以用來控制新切片的容量,其目的不是要增加容量,而是限制容量。
通俗來說 : 對於底層數組 容量是 k 的切片 slice[i:j:k] 來說,長度爲 j - i,容量爲 k - i。允許限制新切片的容量爲底層數組提供了一定的報讀,更好的控制追加操作。

func limitSliceCap()  {
	slice := []string{"apple","orange","plum","banana","grape"}
	fmt.Printf("cap(slice) = %d \n",cap(slice))
	newSlice := slice[2:3:3] // [2:3:5] maxcap = 3
	fmt.Printf("cap(newSlice) = %d \n",cap(newSlice))
	//限制容量後,向 newSlice 擴容,會創建新的底層數組,不會改變原切片的值。
	newSlice = append(newSlice, "kiwi")
	fmt.Printf("newSlice = %v,slice = %v \n ",newSlice,slice)
}

輸出結果:

cap(slice) = 5 
cap(newSlice) = 1 
newSlice = [plum kiwi],slice = [apple orange plum banana grape] 

2.6 多維切片
跟多維數組一個意思,這裏不展開,自行了解

2.7 切片作爲函數參數傳遞
還函數間傳遞切片就是要在函數間以值的方式傳遞切片。由於切片的尺寸很小,在函數間複製和傳遞切片成本很低,只需要複製切片,按照想要的方式修改數據,然後傳回一份新的切片副本。

func sliceAsParams()  {
	slice := []int{10,20,30,40,50}
	slice = editSlice(slice)
	fmt.Println(slice)
}

func editSlice(slice []int) (newSlice []int)  {
	if slice == nil{
		 return []int{}
	}else {
		slice[0] = 0
		return slice
	}
}

3、切片是數組的一個視圖(view)

總結 : 切片是數組的一個 view

4、排序

4.1 基本數據類型

類型 升序 降序
int sort.Ints(slice) sort.Sort(sort.Reverse(sort.IntSlice(slice)))
float sort.Float64s(slice) sort.Sort(sort.Reverse(sort.Float64Slice(slice)))
string sort.Strings(slice) sort.Sort(sort.Reverse(sort.StringSlice(slice)))
func sortSliceOfInt()  {

	fmt.Println()
	//升序
	intSlice := []int{3,20,2,15,8}
	sort.Ints(intSlice)
	fmt.Printf("sort.Ints(intSlice) = %v \n",intSlice)

	//降序
	sort.Sort(sort.Reverse(sort.IntSlice(intSlice)))
    fmt.Printf("intSlice.Reverse = %v \n",intSlice)

}

func sortSliceOfFloat()  {

	fmt.Println()
	//升序
	floatSlice := []float64{1.0,0.05,2.4,1.7,0.01}
	sort.Float64s(floatSlice)
	fmt.Printf("sort.Float64s(floatSlice) = %v \n",floatSlice)

	//降序
	sort.Sort(sort.Reverse(sort.Float64Slice(floatSlice)))
	fmt.Printf("floatSlice.Reverse = %v \n",floatSlice)

}

func sortSliceOfString()  {

	fmt.Println()
	//升序
	stringSlice := []string{"jack","china","love","hello","childred"}
	sort.Strings(stringSlice)
	fmt.Printf("sort.Strings(stringSlice) = %v\n",stringSlice)

	//降序
	sort.Sort(sort.Reverse(sort.StringSlice(stringSlice)))
	fmt.Printf("stringSlice.Reverse = %v \n",stringSlice)
}

輸出結果:

sort.Ints(intSlice) = [2 3 8 15 20] 
intSlice.Reverse = [20 15 8 3 2] 

sort.Float64s(floatSlice) = [0.01 0.05 1 1.7 2.4] 
floatSlice.Reverse = [2.4 1.7 1 0.05 0.01] 

sort.Strings(stringSlice) = [childred china hello jack love]
stringSlice.Reverse = [love jack hello china childred] 

4.2 自定義類型
對自定義類型排序(例如:結構體) 只要實現 sort 包中的 Interface 接口即可。該接口中有 3 個方法

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less reports whether the element with
	// index i should sort before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

看個例子就明白。該部分內容涉及到結構體和接口。看不明白的話先跳過吧。
需求: 給定一組學生,按照姓名升序排序,如果姓名相同,按照年齡升序排序。

分析: 首先要有學生對象(結構體),放入切片。因爲要對切片中的對象進行排序,所以對象必須實現Interface 接口中的 3 個方法。

type Persons []Person
type Person struct {
	//首字母大寫表示 public,小寫表示 private
	Name string
	Age int
}

func (p Persons) Len() int {
     //返回對象所在切片的長度
    return len(p)
}

func (p  Persons) Less(i, j int) bool {
	/**
	  按照姓名升序排序,如果姓名一致,按照年齡升序排序
	  第一條件: 姓名
	  次要條件: 年齡
	 */
	if p[i].Name == p[j].Name {
		return p[i].Age < p[j].Age
	}else {
		return p[i].Name < p[j].Name
	}
}

func (p Persons) Swap(i, j int) {
	//交換位置
	temp := p[i]
	p[i] = p[j]
	p[j] = temp
}

func sortSliceOfCustType()  {

	persons := Persons{
		{
			Name: "test2",
			Age: 21,
		},
		{
			Name: "test1",
			Age: 20,
		},
		{
			Name: "test1",
			Age: 22,
		},

	}
	fmt.Printf("排序前 persons = %v\n",persons)
	sort.Sort(persons)
	fmt.Printf("排序後 persons = %v\n",persons)
}

輸出結果:

排序前 persons = [{test2 21} {test1 20} {test1 22}]
排序後 persons = [{test1 20} {test1 22} {test2 21}]

三、映射
映射是一種數據結構,用於存儲一系列無序的鍵值對。除了 切片、函數、包含切片的結構類型不能作爲映射的鍵(因爲具有引用語義),其他類型均可。
1、創建

使用 內置函數 make 和 字面量 都是創建映射常用的方法,後者更爲常用。

func initMap()  {

	// 聲明一個 nil map
	var nilmap map[string]string
	if nilmap == nil{
		fmt.Println("nilMap is a nil map")
	}
	//nil map 不能用於存儲鍵值對,否則會發生運行時錯誤 assignment to entry in nil map
	//nilMap["key"] = "value"

	//通過內置函數創建
	dictionary1 := make(map[string]string)
	//爲 空字典 加入一組鍵值對
	dictionary1["name"] = "jack"

	//通過字面量創建並初始化一組鍵值對
	dictionary2 := map[string]string{"name":"tom"}
	fmt.Printf("dictionary1 = %v,dictionary2 = %v\n",dictionary1,dictionary2)
}

輸出結果:

nilMap is a nil map
dictionary1 = map[name:jack],dictionary2 = map[age:19 name:tom]

備註:
1、上面的 nil map 不能用於顯示添加鍵值對,但是可以拿變量直接賦值。即 nilmap = dictionary1
2、nil 不能用作映射的 value

2、增/改
爲映射添加鍵值對是通過map[key] = value 的形式進行添加,其中,如果 key 在映射中存在,那麼直接修改原來對應的 value

func addOrRevrseMap()  {
	
	dictionary := map[string]string{}
	//添加鍵值對
	dictionary["name"] = "jack"
	dictionary["age"] = "18"
	dictionary["height"] = "183.5"
	// key = name 存在,直接替換(修改)
	dictionary["name"] = "tom"
	fmt.Println(dictionary)
}

輸出結果:

map[age:18 height:183.5 name:tom]

3、查
查詢 映射是否存在某一個鍵存在。有兩種方式。

3.1 獲取值以及表示這個鍵是否存在的標誌(推薦)

3.2 只返回對應的值,再判斷這個值是否爲零值。侷限:只能用於映射的值都是非零值的情況

func searchKeyIsExistInMap()  {

	dictionary := map[string]string{"name":"jack","age":"18","height":""}
	//1.獲取值以及這個鍵是否存在的標誌位
	value,exist := dictionary["name"]
	if exist {
		fmt.Printf("dictionary[\"name\"] = %s \n",value)
	}
	//2.判斷鍵對應的返回值是否爲對應類型非零值(僅適用鍵都是非零值的情況)。下面的判斷明顯有問題,所以推薦使用第一種
	height := dictionary["height"]
	if height == "" {
		fmt.Println("dictionary 不存在 height 的 key")
	}
	//備註: 在 GO 語言中,通過鍵來索引映射,即便這個鍵不存在也會返回該值對應類型的零值 
	weight := dictionary["weight"]
	fmt.Printf("dictionary[\"weight\"] = %q \n",weight)
}

輸出結果:

dictionary["name"] = jack 
dictionary 不存在 height 的 key
dictionary["weight"] = "" 

4、刪
刪除 map 中某組鍵值對通過內置函數 delete(map,key)。如果 key 存在,那麼刪除; key 不存在,什麼也不發生。對於 一個 nil 映射 發送 delete 消息,啥也不幹。

func  deleteKeyInMap()  {

	dictionary := map[string]string{"name":"jack","age":"20"}
	delete(dictionary,"name")
	delete(dictionary,"weight")
	fmt.Println(dictionary)

   var dict map[string]string
	if dict == nil{
		fmt.Println("nil map")
	}
	delete(dict,"name")
	fmt.Println(dict)
}

輸出結果:

map[age:20]
map[]

6、遍歷
這個在前面 講 Range 關鍵字的時候已經聊過了

func  forRangeMap()  {
	dictionary := map[string]string{"name":"jack","age":"22"}
	fmt.Printf("dictionary 擁有 %d 對鍵值對 \n",len(dictionary))
	allKeys := []string{}
	allValues := []string{}
	for key,value := range dictionary {
		fmt.Printf("dictionary[%s] = %s \n", key, value)
		allKeys = append(allKeys,key)
		allValues = append(allValues,value)
	}
	fmt.Printf("allKeys = %v,allValuers = %v\n",allKeys,allValues)
}

輸出結果:

dictionary 擁有 2 對鍵值對 
dictionary[name] = jack 
dictionary[age] = 22 
allKeys = [name age],allValuers = [jack 22]

7、將映射傳遞給函數
在函數間傳遞映射並不會製造出該映射的一個副本。實際上,當傳遞映射給一個函數並對這個映射進修改時,所有對這個映射的引用都會查覺這個修改。

func mapAsParams()  {
	
	dictionary := map[string]string{"name":"jack","age":"22"}
	fmt.Printf("修改前:%v\n",dictionary)
	editMap(dictionary,"age")
	fmt.Printf("修改後:%v\n",dictionary)
}

func editMap(dictionary map[string]string,key string)  {
	delete(dictionary,key)
}

輸出結果:

修改前:map[age:22 name:jack]
修改後:map[name:jack]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章