5、map&struct

1、map

map是哈希表的引用,是一個無序的key/value對的集合,其中所有的key都是不同的,通過給定的key檢索、更新和刪除對應的value。map中所有的key都有相同的類型,所以的value也有着相同的類型,但是key和value之間可以是不同的數據類型。其中K對應的key必須是支持==比較運算符的數據類型,所以map可以通過測試key是否相等來判斷是否已經存在。

1.1 定義格式

     var  map_name map[key_type]value_type
     //如果你錯誤的使用 new() 分配了一個引用對象,你會獲得一個空引用的指針,相當於聲明瞭一個未初始化的變量並且取了它的地址。
     map_name :=make(map[key_type]value_type)
     map_naem :=map[key_type]value_type{}
     map_name := make(map[string]map[string]bool)/*Map的value類型也可以是一個聚合類型,比如是一個map或slice。
     當我們要處理unix機器上的所有進程,以父進程(pid 爲整形)作爲 key,所有的子進程以所有子進程
     的 pid 組成的切片)作爲 value。通過將 value 定義爲 []int 類型或者其他類型的切片,就可以優雅的解決這個問題。*/

1.2 初始化

/*map有初始容量,當 map 增長到容量上限的時候,如果再增加新的 key-value 對,map 的大小會自動加 1。
所以出於性能的考慮,對於大的 map 或者會快速擴張的 map,即使只是大概知道容量,也最好先標明。*/
map_name :=make(map[string]int,100)
map_name[key1]=value1
map_name=map[key_type]value_type{key1:value1 , key2:value2}
value1 :=map_name[key1]

1.3一般用法

myMap :=map[string]int{"雪":1}
fmt.Println(myMap["雪"])//1
delete(myMap,"雪")//內置的delete可以刪除元素,如果 key 不存在,該操作不會產生錯誤。
/*如果一個查找失敗將返回value類型對應的零值,例如,即使map中不存在“bob”下面的代碼也可以正常工作,
因爲ages["bob"]失敗時將返回0。而且x += y和x++等簡短賦值語法也可以用在map上*/
myMap["雪"]+=1
/*map中的元素並不是一個變量,因此我們不能對map的元素進行取址操作,禁止對map元素取址的原
因是map可能隨着元素數量的增長而重新分配更大的內存空間,從而可能導致之前的地址無效。map是可以動態增長的。*/
a = &myMap["雪"]
/*遍歷map中全部的key/value對,Map的迭代順序是不確定的,並且不同的哈希函數實現可能導致不同的遍歷順序。
在實踐中,遍歷的順序是隨機的,每一次遍歷的順序都不相同。這是故意的,每次都使用隨機的遍歷順序可以強制
要求程序不會依賴具體的哈希函數實現。*/
for name,v :=range myMap{
	fmt.Printf("%s\t%d\n",name,v)
}
/*在這種場景下,map的下標語法將產生兩個值;第二個是一個布爾值,用於報告元素是否真的存在。
布爾變量一般命名爲ok,特別適合馬上用於if條件判斷部分。*/
if name, ok := myMap["雪"]; !ok { /* ... */ }
func equal(x,y map[string]int) bool {//判斷兩個map是否包含相同的key和value
	if len(x) !=len(y){
	return false
	}
	for xk,xv :=range x{
	if yv,ok :=y[xk];!ok||yv!=xv{
	return false
	}
	}
	return true
}
/**/

1.4map類型的切片

package main
import "fmt"
func main() {//必須使用兩次 make() 函數,第一次分配切片,第二次分配 切片中每個
map 元素
// Version A:
	items := make([]map[int]int, 5)
	for i:= range items {
	items[i] = make(map[int]int, 1)
	items[i][1] = 2	}
	fmt.Printf("Version A: Value of items: %v\n", items)
// Version B: NOT GOOD!
	items2 := make([]map[int]int, 5)
	for _, item := range items2 {
	item = make(map[int]int, 1) // item is only a copy of the slice element.
	item[1] = 2 // This 'item' will be lost on the next iteration.
	}
	fmt.Printf("Version B: Value of items: %v\n", items2)
}

1.5map排序
爲 map 排序,需要將 key(或者 value)拷貝到一個切片,再對切片排序(使用 sort 包),然後可以使用切片的 for-range 方法打印出所有的 key 和 value。

// the telephone alphabet:
package main
import (
"fmt"
"sort"
)
var (
barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,
"delta": 87, "echo": 56, "foxtrot": 12,
"golf": 34, "hotel": 16, "indio": 87,
"juliet": 65, "kili": 43, "lima": 98}
)
func main() {
	fmt.Println("unsorted:")
	for k, v := range barVal {
	fmt.Printf("Key: %v, Value: %v / ", k, v)
	}
	keys := make([]string, len(barVal))
	i := 0
	for k, _ := range barVal {
	keys[i] = k
	i++
	}
	sort.Strings(keys)
	fmt.Println()
	fmt.Println("sorted:")
	for _, k := range keys {
	fmt.Printf("Key: %v, Value: %v / ", k, barVal[k])
	}
}

2、struct結構體

結構體是一種聚合的數據類型,是由零個或多個任意類型的值聚合成的實體。
2.1 一般定義格式與初始化

//定義格式,如果結構體成員名字是以大寫字母開頭的,那麼該成員就是導出的;這是Go語言導出規則決定的。
type myStruct struct {a, b int}
type myStruct struct {
a type1
b,c type2
...
}
//初始化
myStruct1:={v1,v2,v3}
myStruct1:={a:v1,b:v2,c:v3}
//使用new,表達式 new(Type)和&Type{}是等價的。
var p *myStruct=new(myStruct)
p :=new(myStruct)

2.2基本操作

//訪問、賦值、對成員取地址
myStruct1.a//通過點訪問
myStruct1.a=2//賦值
p :=&myStruct1.a//取地址
*p=2+*p//指針訪問
var sp *struct =&myStruct1
sp.a=0//與下面相等
*sp.a=0
//一個命名爲S的結構體類型將不能再包含S類型的成員:因爲一個聚合的值不能包含它自身。(該限制同樣適應於數組。)
//但是S類型的結構體可以包含*S指針類型的成員,這可以讓我們創建遞歸的數據結構,比如鏈表和樹結構等。
//使用一個二叉樹來實現一個插入排序
type tree struct { 
value int 
left, right *tree } 
// Sort sorts values in place. 
func Sort(values []int) { 
	var root *tree 
	for _, v := range values { 
	root = add(root, v) } 
	appendValues(values[:0], root) } 
// appendValues appends the elements of t to values in order 
// and returns the resulting slice. 
func appendValues(values []int, t *tree) []int { 
	if t != nil { 
	values = appendValues(values, t.left) 
	values = append(values, t.value) 
	values = appendValues(values, t.right) } 
	return values 
}
func add(t *tree, value int) *tree { 
	if t == nil { 
	// Equivalent to return &tree{value: value}. 
	t = new(tree) 
	t.value = value 
	return t } 
	if value < t.value { 
	t.left = add(t.left, value) 
	} else {
	 t.right = add(t.right, value) 
	 } 
	 return t 
}
//結構體的比較:如果結構體的全部成員都是可以比較的,那麼結構體也是可以比較的,
//那樣的話兩個結構體將可以使用==或!=運算符進行比較。

2.3結構體嵌入和匿名成員

type point struct { 
X, Y int }
type Circle struct { 
Point 
Radius int }
type Wheel struct { 
Circle 
Spokes int }
//對於匿名嵌入的特性,我們可以直接訪問葉子屬性而不需要給出完整的路徑,即是point是小寫(在包內部)
//但顯式形式訪問這些葉子成員的語法依然有效
var w Wheel
w.X = 8 // equivalent to w.Circle.point.X = 8
//結構體字面值並沒有簡短表示匿名成員的語
w = Wheel{8, 8, 5, 20}// compile error: unknown fields
w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields
//結構體字面值必須遵循形狀類型聲明時的結構
w = Wheel{Circle{Point{8, 8}, 5}, 20}
w = Wheel{ 
Circle: Circle{ 
	Point: Point{X: 8, Y: 8}, Radius: 5,
	 }, 
	 Spokes: 20, // NOTE: trailing comma necessary here (and at Radius) 
}

2.3結構體標籤
結構體中的字段除了有名字和類型外,還可以有一個可選的標籤(tag):它是一個附屬於字段的字符串,可以是文檔或其他的重要標記。標籤的內容不可以在一般的編程中使用,只有包 reflect 能獲取它。

package main

import (
	"fmt"
	"reflect"
)

type TagType struct { // tags
	field1 bool   "An important answer"
	field2 string "The name of the thing"
	field3 int    "How much there are"
}

func main() {
	tt := TagType{true, "Barak Obama", 1}
	for i := 0; i < 3; i++ {
		refTag(tt, i)
	}
}

func refTag(tt TagType, ix int) {
	ttType := reflect.TypeOf(tt)
	ixField := ttType.Field(ix)
	fmt.Printf("%v\n", ixField.Tag)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章