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)
}

猜你喜欢

转载自blog.csdn.net/ao__ao/article/details/83957717