map(字典、映射)
- map翻译过来就是字典或者映射, 可以把map看做是切片的升级版
- 切片是用来存储一组相同类型的数据的, map也是用来存储一组相同类型的数据的
- 在切片中我们可以通过索引获取对应的元素, 在map中我们可以通过key获取对应的元素
- 切片的索引是系统自动生成的,从0开始递增. map中的key需要我们自己指定
- 只要是可以做==、!=判断的数据类型都可以作为key(数值类型、字符串、数组、指针、结构体、接口)
- map的key的数据类型不能是:slice、map、function
- map和切片一样容量都不是固定的, 当容量不足时底层会自动扩容
- map格式:
var dic map[key数据类型]value数据类型
package main
import "fmt"
func main() {
var dic map[int]int = map[int]int{0:1, 1:3, 2:5}
fmt.Println(dic) // map[0:1 1:3 2:5]
// 获取map中某个key对应的值
fmt.Println(dic[0]) // 1
// 修改map中某个key对应的值
dic[1] = 666
fmt.Println(dic) // map[0:1 1:666 2:5]
}
package main
import "fmt"
func main() {
var dict map[string]string = map[string]string{"name":"lnj", "age":"33", "gender":"male"}
fmt.Println(dict)// map[name:lnj age:33 gender:male]
}
package main
import "fmt"
func main() {
dict := map[string]string{"name":"lnj", "age":"33", "gender":"male"}
fmt.Println(dict)// map[name:lnj age:33 gender:male]
}
- 方式二:通过make函数创建
make(类型, 容量)
package main
import "fmt"
func main() {
var dict = make(map[string]string, 3)
dict["name"] = "lnj"
dict["age"] = "33"
dict["gender"] = "male"
fmt.Println(dict)// map[age:33 gender:male name:lnj]
}
package main
import "fmt"
func main() {
var dict = make(map[string]string)
dict["name"] = "lnj"
dict["age"] = "33"
dict["gender"] = "male"
fmt.Println(dict)// map[age:33 gender:male name:lnj]
}
package main
import "fmt"
func main() {
// map声明后不能直接使用, 只有通过make或语法糖创建之后才会开辟空间,才能使用
var dict map[string]string
dict["name"] = "lnj" // 编译报错
dict["age"] = "33"
dict["gender"] = "male"
fmt.Println(dict)
}
package main
import "fmt"
func main() {
var dict = make(map[string]string)
fmt.Println("增加前:", dict) // map[]
dict["name"] = "lnj"
fmt.Println("增加后:", dict) // map[name:lnj]
}
package main
import "fmt"
func main() {
var dict = map[string]string{"name":"lnj", "age":"33", "gender":"male"}
fmt.Println("修改前:", dict) // map[name:lnj age:33 gender:male]
dict["name"] = "zs"
fmt.Println("修改后:", dict) // map[age:33 gender:male name:zs]
}
- 删除: 可以通过Go语言内置delete函数删除指定元素
package main
import "fmt"
func main() {
var dict = map[string]string{"name":"lnj", "age":"33", "gender":"male"}
fmt.Println("删除前:", dict) // map[name:lnj age:33 gender:male]
// 第一个参数: 被操作的字典
// 第二个参数: 需要删除元素对应的key
delete(dict, "name")
fmt.Println("删除后:", dict) // map[age:33 gender:male]
}
- 查询: 通过ok-idiom模式判断指定键值对是否存储
package main
import "fmt"
func main() {
var dict = map[string]string{"name":"lnj", "age":"33", "gender":"male"}
//value, ok := dict["age"]
//if(ok){
// fmt.Println("有age这个key,值为", value)
//}else{
// fmt.Println("没有age这个key,值为", value)
//}
if value, ok := dict["age"]; ok{
fmt.Println("有age这个key,值为", value)
}
}
-
map遍历
- 注意: map和数组以及切片不同,map中存储的数据是无序的, 所以多次打印输出的顺序可能不同
var dict = map[string]string{"name":"lnj", "age":"33", "gender":"male"}
for key, value := range dict{
fmt.Println(key, value)
}
结构体
- Go语言中的结构体和C语言中结构体一样, 都是用来保存一组
不同类型的数据
- Go语言中的结构体和C语言中结构体一样, 都需要先定义结构体类型再利用结构体类型定义结构体变量
- 定义结构体类型
type 类型名称 struct{
属性名称 属性类型
属性名称 属性类型
... ...
}
type Studentstruct {
name string
age int
}
- 创建结构体变量的两种方式
- 方式一: 先定义结构体类型, 再定义结构体变量
- 和C语言中的结构体一样, 如果结构体类型需要多次使用, 那么建议先定义类型再定义变量
package main
import "fmt"
func main() {
type Student struct {
name string
age int
}
// 完全初始化
var stu1= Student{"lnj", 33}
fmt.Println(stu1)
// 部分初始化
// 部分初始化必须通过 属性名称: 方式指定要初始化的属性
var stu2 = Student{name:"lnj"}
fmt.Println(stu2)
}
- 方式二: 定义结构体类型同时定义结构体变量(匿名结构体)
- 和C语言中的结构体一样, 如果结构体类型只需要使用一次, 那么建议定义类型同时定义变量
package main
import "fmt"
func main() {
// 注意: 这里不用写type和结构体类型名称
var stu2 = struct {
name string
age int
}{
name: "lnj",
age: 33,
}
fmt.Println(stu2)
}
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
var stu= Student{"lnj", 33}
// 获取结构体中某个属性对应的值
fmt.Println(stu.name)
// 修改结构体中某个属性对应的值
stu.name = "zs"
fmt.Println(stu)
}
- 和slice、map不同的是, 只要定义了结构体变量就可以使用结构体变量
- 默认情况下结构体中的所有属性都是属性对应类型的默认值
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
var stu Student // 相当于 var stu = Student{}
fmt.Println(stu) // { 0}
stu.name = "lnj" // 不会报错
stu.age = 33
fmt.Println(stu) // {lnj 33}
}
- 复杂结构体成员
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
type Demo struct {
age int // 基本类型作为属性
arr [3]int // 数组类型作为属性
sce []int // 切片类型作为属性
mp map[string]string // 字典类型作为属性
stu Student // 结构体类型作为属性
}
var d Demo = Demo{
33,
[3]int{1, 3, 5},
[]int{2, 4, 6},
map[string]string{"class":"one"},
Student{
"lnj",
33,
},
}
fmt.Println(d) // {33 [1 3 5] [2 4 6] map[class:one] {lnj 33}}
}
- slice、map类型属性默认值是nil,不能直接使用
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
type Demo struct {
age int // 基本类型作为属性
arr [3]int // 数组类型作为属性
sce []int // 切片类型作为属性
mp map[string]string // 字典类型作为属性
stu Student // 结构体类型作为属性
}
// 定义结构体变量
var d Demo
// 可以直接使用基本类型属性
d.age = 33
// 可以直接使用数组类型属性
d.arr[0] = 666
// 不可以直接使用切片类型属性
//d.sce[0] = 888 // 编译报错
d.sce = make([]int, 5) // 先创建
d.sce[0] = 888 // 后使用
// 不可以直接使用字典类型属性
//d.mp["class"] = "one" // 编译报错
d.mp = make(map[string]string)// 先创建
d.mp["class"] = "one"// 后使用
// 可以直接使用结构体类型属性
d.stu.name = "lnj"
fmt.Println(d) // {33 [666 0 0] [888 0 0 0 0] map[class:one] {lnj 0}}
}
- 结构体类型转换
- 属性名、属性类型、属性个数、排列顺序都是类型组成部分
- 只有属性名、属性类型、属性个数、排列顺序都相同的结构体类型才能转换
package main
import "fmt"
func main() {
type Person1 struct {
name string
age int
}
type Person2 struct {
name string
age int
}
type Person3 struct {
age int
name string
}
type Person4 struct {
nm string
age int
}
type Person5 struct {
name string
age string
}
type Person6 struct {
age int
name string
gender string
}
var p1 Person1 = Person1{"lnj", 33}
var p2 Person2
// 类型名称不一样不能直接赋值(Person1、Person2)
//p2 = p1
// 虽然类型名称不一样, 但是两个类型中的`属性名称`、`属性类型`、`属性个数`、`排列顺序`都一样,所以可以强制转换
p2 = Person2(p1)
fmt.Println(p2)
// 两个结构体类型中的`属性名称`、`属性类型`、`属性个数`都一样,但是`排列顺序`不一样,所以不能强制转换
//var p3 Person3
//p3 = Person3(p1)
//fmt.Println(p3)
// 两个结构体类型中的`属性类型`、`属性个数`、`排列顺序`都一样,但是`属性名称`不一样,所以不能强制转换
//var p4 Person4
//p4 = Person4(p1)
//fmt.Println(p4)
// 两个结构体类型中的`属性名称`、`属性个数`、`排列顺序`都一样,但是`属性类型`不一样,所以不能强制转换
//var p5 Person5
//p5 = Person5(p1)
//fmt.Println(p5)
// 两个结构体类型中的`属性名称`、`属性类型`、`排列顺序`都一样,但是`属性个数`不一样,所以不能强制转换
//var p6 Person6
//p6 = Person6(p1)
//fmt.Println(p6)
}
- 匿名属性
- 没有指定属性名称,只有属性的类型, 我们称之为匿名属性
- 任何
有命名的数据类型
都可以作为匿名属性(int、float、bool、string、struct等)
package main
import "fmt"
func main() {
type Person struct {
int
float32
bool
string
}
// 不指定名称初始化
per1 := Person{3, 3.14, false, "lnj"}
fmt.Println(per1)
// 可以把数据类型作为名字显示初始化
per2 := Person{
int: 3,
float32: 3.14,
bool: true,
string: "lnj",
}
fmt.Println(per2)
// 可以把数据类型当做属性名称操作结构体
per2.int = 666
fmt.Println(per2.int) // 666
}
- Go语言中最常见的匿名属性是用
结构体类型作为匿名属性
package main
import "fmt"
func main() {
type Person struct {
name string
age int
}
type Student struct {
Person // 匿名属性
class string
}
stu := Student{
Person{"lnj", 33},
"学前一班",
}
fmt.Println(stu) // {{lnj 33} 学前一班}
}
- 如果结构体作为匿名属性, 想访问匿名属性的属性有两种方式
package main
import "fmt"
func main() {
type Person struct {
name string
age int
}
type Student struct {
Person // 匿名属性
class string
}
stu := Student{
Person{"lnj", 33},
"学前一班",
}
fmt.Println(stu) // {{lnj 33} 学前一班}
// 方式一: 先找到匿名属性,再访问匿名属性中的属性
stu.Person.name = "zs"
fmt.Println(stu) // {{zs 33} 学前一班}
// 方式二: 直接访问匿名属性中的属性
// 系统会先查找当前结构体有没有名称叫做name的属性
// 如果没有会继续查找匿名属性中有没有名称叫做name的属性
stu.name = "ww"
fmt.Println(stu) // {{ww 33} 学前一班}
}
- 注意点: 如果多个匿名属性的属性名称相同,那么不能通过方式二操作,只能通过方式一
package main
import "fmt"
func main() {
type Person struct {
name string
age int
}
type Class struct {
name string
time string
}
type Student struct {
Person // 匿名属性
Class // 匿名属性
}
stu := Student{
Person{"lnj", 33},
Class{"学前一班", "2020-12-12"},
}
fmt.Println(stu) // {{lnj 33} {学前一班 2020-12-12}}
// 编译报错, 系统搞不清楚要找哪个name
//stu.name = "zs"
stu.Person.name = "zs"
stu.Class.name = "小学一年级"
fmt.Println(stu) // {{zs 33} {小学一年级 2020-12-12}}
}
package main
import "fmt"
func main() {
type Person struct {
name string
}
type Student struct {
per Person
age int
}
var stu Student = Student{Person{"lnj"}, 18}
//fmt.Println(stu.name) // 报错
fmt.Println(stu.per.name) // 必须通过属性进一步查找
fmt.Println(stu.age)
}
- 注意点: 如果匿名属性是一个结构体类型, 那么这个结构体类型不能是自己
package main
import "fmt"
func main() {
type Person struct {
Person // 错误
name string
}
type Student struct {
*Student // 正确, 链表
age int
}
}