Golang学习 Day_09

map

map是key-value数据结构,又称为字段或者关联数组,雷士其他编程语言的集合,在编程中经常使用到。

语法

var map变量名 map[keytype]valuetype

golang中的map,的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是只包含前面几个类型的接口、结构体、数组。通常为int,string

valuetype的类型和key基本一样,通常为数字(整数、浮点数),string,map,struct

map声明

声明是不会分配内存的,初始化需要make,分配内存后才能赋值和使用

package main


import "fmt"


func main() {
    // map的声明,注意事项
    var a map[string]string
    // 空map不能直接赋值
    // 在使用map前,需要先make,make的作用就是给map分配数据空间
    a = make(map[string]string, 10)
    a["num1"] = "宋江"
    a["num2"] = "吴用"
    fmt.Println(a)
    // key不能重复
    a["num1"] = "武松"
    fmt.Println(a)
    // value可以从夫
    a["num3"] = "吴用"
    fmt.Println(a)
}
  1. map在使用前一定要make

  1. map的key是不能重复,如果重复了,则以最后这个key-value为准

  1. map的value是可以相同的

  1. map的key-value是无序的

  1. make内置函数

使用方式

// 方式一
var a map[string]string
// 空map不能直接赋值
// 在使用map前,需要先make,make的作用就是给map分配数据空间
a = make(map[string]string, 10)
a["num1"] = "宋江"
a["num2"] = "吴用"
fmt.Println(a)
// 方式2
cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)
// 第三种方式
heros := map[string]string{
    "hero1": "宋江",
    "hero2": "卢俊义",
}
fmt.Println("heroes=", heros)
案例
package main


import "fmt"


func main() {
    // // map的声明,注意事项
    // var a map[string]string
    // // 空map不能直接赋值
    // // 在使用map前,需要先make,make的作用就是给map分配数据空间
    // a = make(map[string]string, 10)
    // a["num1"] = "宋江"
    // a["num2"] = "吴用"
    // fmt.Println(a)
    // // key不能重复
    // a["num1"] = "武松"
    // fmt.Println(a)
    // // value可以从夫
    // a["num3"] = "吴用"
    // fmt.Println(a)


    // 方式一
    var a map[string]string
    // 空map不能直接赋值
    // 在使用map前,需要先make,make的作用就是给map分配数据空间
    a = make(map[string]string, 10)
    a["num1"] = "宋江"
    a["num2"] = "吴用"
    fmt.Println(a)


    // 方式2
    cities := make(map[string]string)
    cities["no1"] = "北京"
    cities["no2"] = "天津"
    cities["no3"] = "上海"
    fmt.Println(cities)


    // 第三种方式
    heros := map[string]string{
        "hero1": "宋江",
        "hero2": "卢俊义",
    }
    fmt.Println("heroes=", heros)


    studentMap := make(map[string]map[string]string)


    studentMap["stu01"] = make(map[string]string, 3)
    studentMap["stu01"]["name"] = "tom"
    studentMap["stu01"]["sex"] = "男"
    studentMap["stu01"]["address"] = "北京长安街~"


    studentMap["stu02"] = make(map[string]string, 3)
    studentMap["stu02"]["name"] = "mary"
    studentMap["stu02"]["sex"] = "女"
    studentMap["stu02"]["address"] = "上海黄浦街~"


    fmt.Println(studentMap)
    fmt.Println(studentMap["stu01"])
    fmt.Println(studentMap["stu01"]["address"])
}

map增删改查

  1. 因为key已经存在,重新赋值相当于修改

cities := make(map[string]string)
cities["no1"] = "北京"
cities["no2"] = "天津"
cities["no3"] = "上海"
fmt.Println(cities)


// 因为key已经存在,重新赋值相当于修改
cities["no3"] = "深圳"
fmt.Println(cities)
  1. 内置函数delet(),delete(map,key),当delet指定的key不存在时,删除不会操作,也不会报错

// 因为key已经存在,重新赋值相当于修改
cities["no3"] = "深圳"
fmt.Println(cities)
  1. 如果我们要删除map的所有key,没有一个专门的方法一次删除,可以遍历一下key,逐个删除

  1. 或者map = make(...),make一个新的,让原来的成为垃圾,被gc回收

  1. map查找

val, ok := cities["no2"]
if ok {
    fmt.Printf("有 no2 key 值为%v\n", val)
} else {
    fmt.Printf("没有no2 key\n")
}

map遍历

for k, v := range cities {
    fmt.Printf("k=%v,v=%v\n", k, v)
}
studentMap := make(map[string]map[string]string)


studentMap["stu01"] = make(map[string]string, 3)
studentMap["stu01"]["name"] = "tom"
studentMap["stu01"]["sex"] = "男"
studentMap["stu01"]["address"] = "北京长安街~"


studentMap["stu02"] = make(map[string]string, 3)
studentMap["stu02"]["name"] = "mary"
studentMap["stu02"]["sex"] = "女"
studentMap["stu02"]["address"] = "上海黄浦街~"


for k1, v1 := range studentMap {
    fmt.Println(k1 + ":")
    for k2, v2 := range v1 {
        fmt.Printf("\t %v = %v v2=%v\n", k1, k2, v2)
    }
    fmt.Println()
}

map的长度

len(map)

map切片

切片的数据类型如果是map,我们称slice of map,map切片,这样使用则map的个数可以动态变化

package main


import "fmt"


func main() {
    var monsters []map[string]string
    monsters = make([]map[string]string, 2)
    if monsters[0] == nil {
        monsters[0] = make(map[string]string, 2)
        monsters[0]["name"] = "牛魔王"
        monsters[0]["age"] = "500"
    }
    if monsters[1] == nil {
        monsters[1] = make(map[string]string, 2)
        monsters[1]["name"] = "玉兔精"
        monsters[1]["age"] = "400"
    }
    fmt.Println(monsters)


    //使用切片的append函数,可以动态的增加monstr
    newMoster := map[string]string{
        "name": "天魔",
        "age":  "200",
    }
    monsters = append(monsters, newMoster)


    fmt.Println(monsters)
}

map排序

  1. golang中没有一个专门的方法针对map的key进行排序

  1. golang中的map默认是升序的,注意也不是按照顺序存放的,每次遍历,得到的输出可能不一样

  1. golang中的map的排序,事先将key进行排序,然后根据key值遍历输出

使用细节

  1. map是引用类型,遵守引用类型的传递机制,在一个函数接收map,修改后,会直接修改原来的map

package main


import "fmt"


func modify(map1 map[int]int) {
    map1[10] = 900
}


func main() {
    map1 := make(map[int]int)
    map1[1] = 90
    map1[2] = 88
    map1[10] = 12
    map1[12] = 3
    modify(map1)
    // 说明map是引用类型
    fmt.Println(map1)
}
  1. map的容量达到后,在想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长键值对(key-value)

  1. map的value也经常使用struct类型,更适合管理复杂的数据,比如value可以为Student结构体

package main


import (
    "fmt"
)


func modify(map1 map[int]int) {
    map1[10] = 900
}


type stu struct {
    Name    string
    Age     int
    Address string
}


func main() {
    map1 := make(map[int]int)
    map1[1] = 90
    map1[2] = 88
    map1[10] = 12
    map1[12] = 3
    modify(map1)
    // 说明map是引用类型
    fmt.Println(map1)


    students := make(map[string]stu, 10)
    stu1 := stu{"Tom", 18, "北京"}
    stu2 := stu{"Marry", 18, "上海"}
    students["no1"] = stu1
    students["no2"] = stu2


    fmt.Println(students)
}

面向"对象"编程

  1. Golang支持面向对象编程(OOP),但是和传统的面向对象变成有区别,并不是纯粹的面向对象语言,

  1. Golang没有类(class),Go语言的结构体(struct)和其他编程语言的类(class)有同等的地位,可以理解为Golang是基于struct来实现OOP特性的

  1. Golang面向对象编程非常简洁,去掉了传统的OOP语言的继承、方法重载、构造函数和析构函数、隐藏的this指针等等

  1. Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名函数名字段来实现的

  1. Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过过接口(interface)关联,耦合性低,也非常灵活,在Golang中面向接口编程是非常重要的特性

结构体

  1. 将一类事物的特征提取出来,形成一个新的数据类型,就是一个结构体

  1. 通过这个个结构体,我们可以创建多个变量

案例
package main


import "fmt"


type Cat struct {
    Name  string
    Age   int
    Color string
}


func main() {
    var cat1 Cat
    cat1.Name = "tom"
    cat1.Age = 12
    cat1.Color = "blue"
    fmt.Println(cat1)
}

结构体与结构体变量(实例)的区别和联系

  1. 结构体是自定义的数据类型,代表一类事物

  1. 结构体变量(实例)是具体的,实际的,代表一个具体变量

字段/属性

  1. 从概念或叫法上看,结构体的字段=属性=field

  1. 字段是结构体的一个组成部分,一般基本数据类型、数组,也可是引用类型

注意事项
  1. 字段声明语法同变量,字段名 字段类型

  1. 字段的类型可以为:基本类型、数组或引用类型

  1. 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值

package main


import "fmt"


type Person struct {
    Name   string
    Age    int
    Scores [5]float64
    ptr    *int
    slice  []int
    map1   map[string]string
}


func main() {
    var p1 Person
    fmt.Println(p1)


    if p1.ptr == nil {
        fmt.Println("ok1")
    }
    if p1.slice == nil {
        fmt.Println("ok2")
    }
    if p1.map1 == nil {
        fmt.Println("ok3")
    }
    p1.slice = make([]int, 1)
    p1.slice[0] = 100


    fmt.Println(p1)
}
  1. 不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个。

创建结构体变量和访问结构体字段

  1. 方式一

var p1 Person
fmt.Print(p1)
  1. 方式二

var p1 Person
fmt.Print(p1)
  1. 方式三

//方式三
var p3 *Person = new(Person)
(*p3).Name = "smith"
(*p3).Age = 30
// go的设计者,为了程序员使用方柏霓,底层会对p3.Name进行处理
p3.Name = "li"
fmt.Println(p3)
  1. 方法四

var p4 *Person = &Person{}
fmt.Println(p4)

结构体的注意事项

  1. 结构体的所有字段在内存中是连续的

package main


import "fmt"


type Point struct {
    x int
    y int
}


type Rect struct {
    leftUp, rightDown Point
}


type Rect2 struct {
    leftUp, rightDown *Point
}


func main() {
    r1 := Rect{Point{1, 2}, Point{3, 4}}
    fmt.Printf("r1.leftUp.x 地址为%p\n", &r1.leftUp.x)
    fmt.Printf("r1.leftUp.y 地址为%p\n", &r1.leftUp.y)
    fmt.Printf("r1.rightDown.x 地址为%p\n", &r1.rightDown.x)
    fmt.Printf("r1.rightDown.y 地址为%p\n", &r1.rightDown.y)


    // r2有两个 *Point雷诺高兴,这两个*Point类型本身地址也是连续的
    // 但他们指向的地址不一定是连续的
    r2 := Rect2{&Point{10, 20}, &Point{30, 40}}
    fmt.Printf("r2.leftUp 地址为%p\n", &r2.leftUp)
    fmt.Printf("r2.rightDown 地址为%p\n", &r2.rightDown)
    fmt.Printf("r2.leftUp.x 地址为%p\n", &r2.leftUp.x)
    fmt.Printf("r2.leftUp.y 地址为%p\n", &r2.leftUp.y)
    fmt.Printf("r2.rightDown.x 地址为%p\n", &r2.rightDown.x)
    fmt.Printf("r2.rightDown.y 地址为%p\n", &r2.rightDown.y)
}
  1. 结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字、个数和类型)

package main


import "fmt"


type A struct {
    Num int
}
type B struct {
    Num int
}


func main() {
    var a A
    var b B
    a = A(b)
    fmt.Println(a,b)
}
  1. 结构体进行type重新定义(想当与取别名),Golang认为是新的数据类型,但是相互间可以强转

package main


import "fmt"


type Student struct {
    Name string
    Age  int
}


type Stu Student


type integer int


func main() {
    var stu1 Student
    var stu2 Stu
    stu2 = Stu(stu1)
    fmt.Println(stu1, stu2)


    var i integer = 10
    var j int = 20
    j = int(i)
    fmt.Println(i, j)
}
  1. struct的每个字段山,可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化

将struct变量进行json处理

package main


import (
    "encoding/json"
    "fmt"
)


type Monster struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Skill string `json:"skill"`
}


func main() {


    monster := Monster{"牛魔王", 500, "芭蕉扇"}

    // json.Marshal函数中使用了反射
    jsonMonster, err := json.Marshal(monster)
    if err != nil {
        fmt.Println("json 处理错误", err)
    }
    fmt.Println("jsonMonster", string(jsonMonster))
}

方法

在某些情况下,我们需要声明(定义)方法。

Golang中的方法是作用在指定数据类型上的,因此自定义类型,都可以有方法,而不是struct

type A struct{

Num int

}

func (a A)test(){

fmt.Println(a.Num)

}

  1. func (a A)test(){}表示A结构体有一方法,方法名test

  1. (a A)体现test方法是和A类型绑定的

package main


import "fmt"


type Person struct {
    Name string
}


func (p Person) test() {
    fmt.Println("test()", p.Name)
}


func main() {
    var p Person
    p.Name = "tom"
    p.test()
}

快速入门

func main() {
    var p Person
    p.Name = "tom"


    p.speak()
    p.jisuan()
    p.jisuan2(100)
    res := p.getsum(10, 20)
    fmt.Println("res=", res)
}
  1. 给Person结构体添加speak方法,输出XX是一个b( ̄▽ ̄)d 

func (p Person) speak() {
    fmt.Println(p.Name, "是一个goodman")
}
  1. 给Person结构体添加jisaun方法,可以计算1+...+1000的结果

func (p Person) jisuan() {
    res := 0
    for i := 1; i < 1000; i++ {
        res += i
    }
    fmt.Println(p.Name, "计算的结果=", res)
}
  1. 给Person结构体jisuan2方法,该方法可以接受一个数n,计算1+...+n的结果

func (p Person) jisuan() {
    res := 0
    for i := 1; i < 1000; i++ {
        res += i
    }
    fmt.Println(p.Name, "计算的结果=", res)
}


func (p Person) jisuan2(n int) {
    res := 0
    for i := 1; i < n; i++ {
        res += i
    }
    fmt.Println(p.Name, "计算的结果=", res)
}
  1. 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果

func (p Person) getsum(n1 int, n2 int) int {
    return n1 + n2
}

方法的调用和传参机制原理

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当作实参传递给方法

  1. 在通过一个变量去调用方法时,其调用机制和函数一样

  1. 不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)

案例
package main


import "fmt"


type Cricle struct {
    radius float64
}


func (c Cricle) area() float64 {
    return 3.14 * c.radius * c.radius
}


func main() {
    var c Cricle
    c.radius = 4.0
    res := c.area()
    fmt.Println("面积是=",res)
}

方法的声明(定义)

func (recrvier type) methodName(参数列表)(返回值列表){

方法体

return 返回值

}

  1. 参数列表:表示方法的输入

  1. recever type:表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型

  1. receiver type:type可以是结构体,也可以是其他自定义类型

  1. receiver:就是type类型的一个变量,比如:Person结构体的一个变量

  1. 参数列表:表示方法的输入

  1. 返回值列表:表示返回的值,可以有多个

  1. 方法的主体,表示为了实现某一功能代码块

  1. return 语句不是必须的

方法的注意事项和细节讨论

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

  1. 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理

  1. Golang中的方法是作用在指定的数据类型上的,和数据类型绑定,因此自定义类型,都可以有方法,而不仅仅是struct,比如int,float32等都可以有方法

  1. 方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问

  1. 如果一个变量实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出

方法和函数的区别

  1. 调用方式不一样

  1. 函数调用方式: 函数名(实参列表)

  1. 方法的调用方式: 变量.方法名(实参列表)

  1. 对于普通函数,接收者为值类型是,不能将指针类型的数据直接传递,反之亦然

  1. 对于方法(如struct的方法),接收者为值类型时,可以直接使用指针类型的变量调用方法,反过来同样可以。

package main


import "fmt"


type Person struct {
    Name string
}


func test01(p Person) {
    fmt.Println(p.Name)
}


func test02(p *Person) {
    fmt.Println(p.Name)
}


func (p Person) test03() {
    fmt.Println("test03() =", p.Name)
}


func main() {
    p := Person{"tom"}
    test01(p)
    test02(&p)


    p.test03()
    // 值拷贝,从形式上看是传入地址,但本质上仍是值拷贝
    (&p).test03()
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值