go语言基础(三):map、结构体
1. map
- 语法:
var map的名字 map[键类型]值类型 = make(map[键类型]值类型)
map的名字 := make(map[键类型]值类型)
- 如果不写后面的make的话会报错
map在定义后不需要给它指定大小就能进行使用,和切片这一点不同要区分
对于map直接使用map名[键值]=数据值
,就可以进行数据的添加
func main() {
var m map[int]string = make(map[int]string)
m[0] = "alin"
m[1] = "linda"
m[666] = "lili"
// 每次打印时运行结果都不一样,原因:map的存储是无序的,所以只能使用范围遍历
fmt.Println(m)
// map的遍历
// 范围遍历
for k, v := range m {
fmt.Println("key =", k, "value =", v)
}
}
- 注意:map的存储是无序的,所以在遍历的时候只能使用范围遍历
1.1 map的存储格式
链式存储,每一个map数据除了键和值以外还包括一个指向下一个元素的指针。
1.2 map的初始化和赋值
-
map的key必须支持== 和 !=操作, 不能支持函数、切片、map
-
在初始化中键必须是唯一的,不能重复,如果写了重复的key会报错
-
map的名字就是它的首地址
-
map不能使用cap,只能使用len,求键值对的个数
func main01() {
m := map[int]string{111: "alin", 222: "赵四", 333: "刘能"}
fmt.Println(m)
// 重置键对应的值
m[222] = "广坤"
fmt.Println(m)
// 打印map中的值,通过key找value,不能通过key找到value
fmt.Println(m[111])
}
func main() {
m := map[int]string{101: "alin", 102: "刘能"}
// map的名字就是一个map的地址
fmt.Printf("%p\n", m)
// 注意不能对map中的value取地址,value相当于是一个字面量,不能取地址
// fmt.Printf("%p\n", &m[1]) // 报错
// 打印长度
fmt.Println(len(m))
}
小练习1:统计字符串中各个字符出现的次数
func main() {
str := "hello world"
m := make(map[byte]int)
for i:=0; i<len(str); i++ {
m[str[i]]++
}
for k, v := range m {
fmt.Printf("%c:%d\n", k, v)
}
}
小练习2:双色球
有红球和蓝球两种:红球数字在1-33,选择6个不允许重复;蓝球数字在1-16,只选一个
func main() {
rand.Seed(time.Now().UnixNano())
m := make(map[int]int)
for len(m) != 6 {
m[rand.Intn(33)+1] = 0
}
fmt.Print("红球:")
for k, _ := range m {
fmt.Print(k, " ")
}
fmt.Println()
fmt.Println("蓝球:", rand.Intn(16)+1)
}
1.3 map中的键和值
map名[键值]
这个可以把它理解为是一个函数调用类似,它有两个返回值,第一个是value,第二个是一个bool类型的值,表示是否存在对应这个键值的value。默认只使用一个返回值的话,是第一个值,忽略第二个值。
func main() {
m := map[int]string{111: "alin", 222: "赵四", 333: "刘能"}
value, ok := m[111]
if ok {
fmt.Println("value =", value)
} else {
fmt.Println("no this key")
}
fmt.Println(m[111])
}
- map中的删除操作使用delete函数完成
- 注意链式存储的删除会修改被删除项的前一项的指向
func main() {
m := map[int]string{111: "alin", 222: "赵四", 333: "刘能"}
fmt.Println(m)
// 删除map中对应键值的元素,参1:map的名字,参2:要删除的key
delete(m, 222)
fmt.Println(m)
}
1.4 map做函数参数
map做函数参数是值传递,但是map名是一个地址,所以相当于了地址传递
func test(m map[int]string) {
m[101] = "刘能"
m[111] = "alin"
}
func main() {
m := make(map[int]string)
m[101] = "刘备"
fmt.Println(m)
// map名是一个地址,做了值传递后相当于传进去了map的地址,在函数内修改的也是这个map的内容
test(m)
fmt.Println(m)
}
2. 结构体
2.1 结构体的定义和赋值
语法:
type 结构体名 struct {
成员1 成员数据类型
成员2 成员数据类型
...
}
type student struct {
name string
id int
age int
sex byte
}
func main() {
// 结构体变量的定义和使用
var stu student
stu.name = "alin"
stu.id = 29
stu.age = 21
stu.sex = 'm'
fmt.Println(stu)
s := student{"alin", 1, 21, 'm'}
fmt.Println(s)
fmt.Println(unsafe.Sizeof(stu.name))
// 计算结构体的大小时,会发生内存对齐
fmt.Println(unsafe.Sizeof(stu))
s1 := s
s.name = "lili"
// 当发生结构体之间的赋值操作时,它是值传递,改变旧的值不会影响之前的
fmt.Println(s)
fmt.Println(s1)
}
// 结果
{alin 29 21 109}
{alin 1 21 109}
16
40
{lili 1 21 109}
{alin 1 21 109}
注意:在同一个包下结构体不能重名
2.2 结构体类型切片和数组
- 结构体类型数组
type student1 struct {
name string // 16
id int // 8
age int // 8
sex byte // 1
}
func main() {
// 定义结构体类型数组(数组和切片的区别就是在方括号中有指定大小)
var arr [5]student1 = [5]student1{
{"alin", 1001, 21, 'm'},
{"lili", 1002, 22, 'f'},
{"xiaoming", 1003, 23, 'm'},
{"jiabao", 1004, 25, 'm'},
{"yangshuai", 1005, 24, 'f'}, // 这里的逗号如果不写的话大括号要放到这行,不美观
}
//arr[0].name = "alin"
//arr[0].id = 1001
//arr[0].age = 21
//arr[0].sex = 'm'
fmt.Println(arr)
fmt.Println(unsafe.Sizeof(arr)) // 200 结果是所有元素的占内存大小和
fmt.Println(len(arr)) // 5 数组中的元素个数
}
- 结构体类型切片
func main() {
var slice []student1 = []student1{
{"alin", 1001, 21, 'm'},
{"lili", 1002, 22, 'f'},
{"xiaoming", 1003, 23, 'm'},
{"jiabao", 1004, 25, 'm'},
{"yangshuai", 1005, 24, 'f'}, // 这里的逗号如果不写的话大括号要放到这行,不美观
}
slice = append(slice, student1{"yuepeng", 1006, 26, 'm'})
fmt.Println(slice)
fmt.Println(len(slice)) // 6
fmt.Println(cap(slice)) // 10
}
注意:结构体类型变量是不能够进行大于小于等操作的,因为没有指定比较的方式无法进行比较,所以在比较的时候要指定成员变量的大小比较。
2.3 结构体变量做map的value
type student3 struct {
name string // 16
id int // 8
age int // 8
sex byte // 1
}
func main() {
m := map[int]student3{1001:student3{"alin", 1001, 21, 'm'}}
m[1002] = student3{"xiaoming", 18, 1002, 'm'}
fmt.Println(m)
}