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