• golang语言_2


    init函数
    每一个源文件都可以包含一个 init 函数,该函数会在 main 函数执行前,被 Go 运行框架调用,也 就是说 init 会在 main 函数前被调用。可以做初始化操作。

    func init() {
    	fmt.Println(123)
    }
    
    func main() {
    	fmt.Println(456)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    init函数的注意事项和细节
    1)如果一个文件同时包含全局变量定义,init 函数和 main 函数,则执行的流程全局变量定义->init 函数->main 函数
    匿名函数
    Go 支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只是希望使用一次,可以考 虑使用匿名函数,匿名函数也可以实现多次调用。
    (1)在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

    func main() {
    	res1 := func(n1 int, n2 int) int {
    		return n1 + n2
    	}(10, 20)
    	fmt.Println(res1)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (2)将匿名函数赋给一个变量(函数变量),再通过该变量来调用匿名函数

    func main() {
    	a := func(n1 int, n2 int) int {
    		return n1 - n2
    	}
    	res2 := a(10, 30)
    	fmt.Println(res2)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (3)全局匿名函数
    如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,可以在程序 有效

    var (
    	fun1 = func(n1 int, n2 int) int {
    		return n1 * n2
    	}
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5

    闭包
    基本介绍:闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
    1)AddUpper 是一个函数,返回的数据类型是 fun (int) int
    2)返回的是一个匿名函数, 但是这个匿名函数引用到函数外的 n ,因此这个匿名函数就和 n 形成一 个整体,构成闭包。
    3)大家可以这样理解: 闭包是类, 函数是操作,n 是字段。函数和它使用到 n 构成闭包。
    4)当我们反复的调用 f 函数时,因为 n 是初始化一次,因此每调用一次就进行累计。
    5)我们要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引 用到的变量共同构成闭包。

    func AddUpper() func(int) int {
    	var n int = 10
    	return func(x int) int {
    		n = n + x
    		return n
    	}
    }
    func main() {
    	//累加器
    	f := AddUpper()
    	fmt.Println(f(2))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ** 函数的 defer**
    在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完 毕后,及时的释放资源,Go 的设计者提供 defer (延时机制)。
    defer 的注意事项和细节
    1)当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句压入到一个栈 中, 然后继续执行函数下一个语句。
    2)当函数执行完毕后,在从 defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制),
    3)在 defer 将语句放入到栈时,也会将相关的值拷贝同时入栈
    defer的最佳实战
    1)在 golang 编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是 锁资源), 可以执行 defer file.Close() defer connect.Close()
    2)在 defer 后,可以继续使用创建资源.
    3)当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源.
    4)这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心。
    两种传递方式
    1)值传递
    2)引用传递 其实,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的 拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的 数据大小,数据越大,效率越低。
    值类型和引用类型
    1)值类型:基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct
    2)引用类型:指针、slice 切片、map、管道 chan、interface 等都是引用类型
    变量作用域
    1)函数内部声明/定义的变量叫局部变量,作用域仅限于函数内部
    2)函数外部声明/定义的变量叫全局变量,作用域在整个包都有效,如果其首字母为大写,则作用 域在整个程序有效
    3)如果变量是在一个代码块,比如 for / if 中,那么这个变量的的作用域就在该代码块
    字符串常用的系统函数
    1)统计字符串的长度,按字节 len(str)

    func main() {
    	str := "hello北"
    	fmt.Println("str len=", len(str))
    }
    
    • 1
    • 2
    • 3
    • 4

    2)字符串遍历,同时处理有中文的问题 r := []rune(str)

    	str2 := "hello北京"
    	//字符串遍历,同时处理有中文的问题 r := []rune(str)
    	r := []rune(str2)
    	for i := 0; i < len(r); i++ {
    		fmt.Printf("字符=%c\n", r[i])
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3)字符串转整数: n, err := strconv.Atoi(“12”)

    	n, err := strconv.Atoi("123")
    	if err != nil {
    		fmt.Println("转换错误", err)
    	}else {
    		fmt.Println("转成的结果是", n)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4)整数转字符串 str = strconv.Itoa(12345)

    	str = strconv.Itoa(12345)
    	fmt.Printf("str=%v, str=%T\n", str, str)
    
    • 1
    • 2

    5)字符串 转 []byte: var bytes = []byte(“hello go”)

    	var bytes = []byte("hello go")
    	fmt.Printf("bytes=%v\n", bytes)
    
    • 1
    • 2

    6)[]byte 转 字符串: str = string([]byte{97, 98, 99})

    	str = string([]byte{97, 98, 99}) 
    	fmt.Printf("str=%v\n", str)
    
    • 1
    • 2

    7)10 进制转 2, 8, 16 进制: str = strconv.FormatInt(123, 2) // 2-> 8 , 16

    	str = strconv.FormatInt(123, 2)
    	fmt.Printf("123对应的二进制是=%v\n", str)
    	str = strconv.FormatInt(123, 16)
    	fmt.Printf("123对应的16进制是=%v\n", str)
    
    • 1
    • 2
    • 3
    • 4

    8)查找子串是否在指定的字符串中: strings.Contains(“seafood”, “foo”) //true

    b := strings.Contains("seafood", "mary")
    	fmt.Printf("b=%v\n", b)
    
    • 1
    • 2

    9)统计一个字符串有几个指定的子串 : strings.Count(“ceheese”, “e”) //4

    num := strings.Count("ceheese", "e")
    	fmt.Printf("num=%v\n", num)
    
    • 1
    • 2

    10)不区分大小写的字符串比较(==是区分字母大小写的): fmt.Println(strings.EqualFold(“abc”, “Abc”)) // true

    b = strings.EqualFold("abc", "Abc")
    	fmt.Printf("b=%v\n", b) //true
    
    	fmt.Println("结果","abc" == "Abc") // false //区分字母大小写
    
    • 1
    • 2
    • 3
    • 4

    11)返回子串在字符串第一次出现的 index 值,如果没有返回-1 : strings.Index(“NLT_abc”, “abc”) // 4

    index := strings.Index("NLT_abcabcabc", "abc") // 4
    	fmt.Printf("index=%v\n",index)
    
    • 1
    • 2

    12)返回子串在字符串最后一次出现的 index,如没有返回-1 : strings.LastIndex(“go golang”, “go”)

    index = strings.LastIndex("go golang", "go") //3
    	fmt.Printf("index=%v\n",index)
    
    • 1
    • 2

    13)将指定的子串替换成 另外一个子串: strings.Replace(“go go hello”, “go”, “go 语言”, n) n 可以指 定你希望替换几个,如果 n=-1 表示全部替换

    str2 = "go go hello"
    	str = strings.Replace(str2, "go", "北京", -1)
    	fmt.Printf("str=%v str2=%v\n", str, str2)
    
    • 1
    • 2
    • 3

    14)按 照 指 定 的 某 个 字 符 , 为 分 割 标 识 , 将 一 个 字 符 串 拆 分 成 字 符 串 数 组 : strings.Split(“hello,wrold,ok”, “,”)

    //strings.Split("hello,wrold,ok", ",")
    	strArr := strings.Split("hello,wrold,ok", ",")
    	for i := 0; i < len(strArr); i++ {
    		fmt.Printf("str[%v]=%v\n", i, strArr[i])
    	} 
    	fmt.Printf("strArr=%v\n", strArr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    15)将字符串的字母进行大小写的转换: strings.ToLower(“Go”) // go strings.ToUpper(“Go”) // GO

    str = "goLang Hello"
    	str = strings.ToLower(str) 
    	str = strings.ToUpper(str) 
    	fmt.Printf("str=%v\n", str) //golang hello
    
    • 1
    • 2
    • 3
    • 4

    16)将字符串左右两边的空格去掉: strings.TrimSpace(" tn a lone gopher ntrn ")

    str = strings.TrimSpace(" tn a lone gopher ntrn   ")
    	fmt.Printf("str=%q\n", str)
    
    • 1
    • 2

    17)将字符串左右两边指定的字符去掉 : strings.Trim("! hello! “, " !”) // [“hello”] //将左右两边 ! 和 " "去掉

    str = strings.Trim("! he!llo! ", " !")
    	fmt.Printf("str=%q\n", str)
    
    • 1
    • 2

    18)将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! “, " !”) // [“hello”] //将左边 ! 和 " “去掉
    19)将字符串右边指定的字符去掉 :strings.TrimRight(”! hello! “, " !”) // [“hello”] //将右边 ! 和 " "去掉
    20)判断字符串是否以指定的字符串开头: strings.HasPrefix(“ftp://192.168.10.1”, “ftp”) // true

    b = strings.HasPrefix("ftp://192.168.10.1", "hsp") //true
    	fmt.Printf("b=%v\n", b)
    
    • 1
    • 2

    21)判断字符串是否以指定的字符串结束: strings.HasSuffix(“NLT_abc.jpg”, “abc”) //false
    时间和日期相关函数
    1)时间和日期相关函数,需要导入 time 包
    2)time.Time 类型,用于表示时间

    now := time.Now()
    	fmt.Printf("now=%v now type=%T\n", now, now)
    
    • 1
    • 2

    3)如何获取到其它的日期信息

    //2.通过now可以获取到年月日,时分秒
    	fmt.Printf("年=%v\n", now.Year())
    	fmt.Printf("月=%v\n", now.Month())
    	fmt.Printf("月=%v\n", int(now.Month()))
    	fmt.Printf("日=%v\n", now.Day())
    	fmt.Printf("时=%v\n", now.Hour())
    	fmt.Printf("分=%v\n", now.Minute())
    	fmt.Printf("秒=%v\n", now.Second())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4)格式化日期时间
    方式 1: 就是使用 Printf 或者 SPrintf

    	fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d \n", now.Year(), 
    	now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    
    	dateStr := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d \n", now.Year(), 
    	now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
    	fmt.Printf("dateStr=%v\n", dateStr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    方式二: 使用 time.Format() 方法完成:

    //格式化日期时间的第二种方式
    	fmt.Printf(now.Format("2006-01-02 15:04:05"))
    	fmt.Println()
    	fmt.Printf(now.Format("2006-01-02"))
    	fmt.Println()
    	fmt.Printf(now.Format("15:04:05"))
    	fmt.Println()
    
    	fmt.Printf(now.Format("2006"))
    	fmt.Println()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5)时间的常量

    const (
        Nanosecond  Duration = 1
        Microsecond          = 1000 * Nanosecond
        Millisecond          = 1000 * Microsecond
        Second               = 1000 * Millisecond
        Minute               = 60 * Second
        Hour                 = 60 * Minute
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6)结合 Sleep 来使用一下时间常量

    i := 0
    	for {
    		i++
    		fmt.Println(i)
    		//休眠
    		//time.Sleep(time.Second)
    		time.Sleep(time.Millisecond * 100)
    		if i == 100 {
    			break
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7)time 的 Unix 和 UnixNano 的方法

    fmt.Printf("unix时间戳=%v unixnano时间戳=%v\n", now.Unix(), now.UnixNano())
    
    • 1

    编写一段代码来统计 函数 test03 执行的时间

    func test03() {
    
    	str := ""
    	for i := 0; i < 100000; i++ {
    		str += "hello" + strconv.Itoa(i)
    	}
    }
    
    func main() {
    	//在执行test03前,先获取到当前的unix时间戳
    	start := time.Now().Unix()
    	test03()
    	end := time.Now().Unix()
    	fmt.Printf("执行test03()耗费时间为%v秒\n", end-start)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    内置函数

    1)len:用来求长度,比如 string、array、slice、map、channel
    2)new:用来分配内存,主要用来分配值类型,比如 int、float32,struct…返回的是指针
    3)make:用来分配内存,主要用来分配引用类型,比如 channel、map、slice(后面在写相关笔记)
    错误处理
    1)在默认情况下,当发生错误后(panic) ,程序就会退出(崩溃.)
    2)如果我们希望:当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行。还可 以在捕获到错误后,给管理员一个提示(邮件,短信。。。)
    3)这里引出错误处理机制
    1)Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try…catch…finally 这种处理。
    2)Go 中引入的处理方式为:defer, panic, recover
    3)这几个异常的使用场景可以这么简单描述:Go 中可以抛出一个 panic 的异常,然后在 defer 中 通过 recover 捕获这个异常,然后正常处理
    使用 defer+recover 来处理错误

    func test() {
    	//使用defer + recover 来捕获和处理异常
    	defer func() {
    		err := recover() // recover()内置函数,可以捕获到异常
    		if err != nil {  // 说明捕获到错误
    			fmt.Println("err=", err)
    			//这里就可以将错误信息发送给管理员....
    			fmt.Println("发送邮件给admin@sohu.com~")
    		}
    	}()
    	num1 := 10
    	num2 := 0
    	res := num1 / num2
    	fmt.Println("res=", res)
    }
    func main() {
    
    	//测试
    	 test()
    	 for {
    	 	fmt.Println("main()下面的代码...")
    	 	time.Sleep(time.Second)
    	 }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    自定义错误的介绍

    //函数去读取以配置文件init.conf的信息
    //如果文件名传入不正确,我们就返回一个自定义的错误
    func readConf(name string) (err error) {
    	if name == "config.ini" {
    		//读取...
    		return nil
    	} else {
    		//返回一个自定义错误
    		return errors.New("读取文件错误..")
    	}
    }
    func test02() {
    	err := readConf("config2.ini")
    	if err != nil {
    		//如果读取文件发送错误,就输出这个错误,并终止程序
    		panic(err)
    	}
    	fmt.Println("test02()继续执行....")
    }
    func main() {
    	test02()
    	fmt.Println("main()下面的代码...")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    数组的定义和内存布局
    var 数组名 [数组大小]数据类型
    var a [5]int
    赋初值 a[0] = 1 a[1] = 30 …
    数组在内存布局(重要)
    在这里插入图片描述
    1)数组的地址可以通过数组名来获取 &intArr
    2)数组的第一个元素的地址,就是数组的首地址
    3)数组的各个元素的地址间隔是依据数组的类型决定,比如 int64 -> 8 int32->4…
    数组的使用

    //四种初始化数组的方式
    	var numArr01 [3]int = [3]int{1, 2, 3}
    	fmt.Println("numArr01=", numArr01)
    
    	var numArr02 = [3]int{5, 6, 7}
    	fmt.Println("numArr02=", numArr02)
    	//这里的 [...] 是规定的写法
    	var numArr03 = [...]int{8, 9, 10}
    	fmt.Println("numArr03=", numArr03)
    
    	var numArr04 = [...]int{1: 800, 0: 900, 2:999}
    	fmt.Println("numArr04=", numArr04)
    
    	//类型推导
    	strArr05 := [...]string{1: "tom", 0: "jack", 2:"mary"}
    	fmt.Println("strArr05=", strArr05)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    数组的遍历
    for-range 结构遍历

    //演示for-range遍历数组
    	 heroes  := [...]string{"宋江", "吴用", "卢俊义"}
    	 
    	for i, v := range heroes {
    		fmt.Printf("i=%v v=%v\n", i , v)
    		fmt.Printf("heroes[%d]=%v\n", i, heroes[i])
    	}
    
    	for _, v := range heroes {
    		fmt.Printf("元素的值=%v\n", v)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    数组使用的注意事项和细节
    1)数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化

    func main() {
    	//数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化。
    	var arr01 [3]int
    	arr01[0] = 1
    	arr01[1] = 30
    	//这里会报错
    	arr01[2] = 1.1  
    	//其长度是固定的, 不能动态变化,否则报越界
    	arr01[3] = 890
    
    	fmt.Println(arr01)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2)var arr []int 这时 arr 就是一个 slice 切片,切片后面专门讲解,不急哈.
    3)数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
    4)数组创建后,如果没有赋值,有默认值(零值)

    //数组创建后,如果没有赋值,有默认值(零值)
    	//1. 数值(整数系列, 浮点数系列) =>0
    	//2. 字符串 ==> ""
    	//3. 布尔类型 ==> false
    
    	var arr01 [3]float32
    	var arr02 [3]string
    	var arr03 [3]bool
    	fmt.Printf("arr01=%v arr02=%v arr03=%v\n", arr01, arr02, arr03)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5)使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值(默认零值) 3 使用数组
    6)数组的下标是从 0 开始的
    7)数组下标必须在指定范围内使用,否则报 panic:数组越界,比如 var arr [5]int 则有效下标为 0-4
    8)Go 的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响

    func test01(arr [3]int) {
    	arr[0] = 88
    } 
    func main() {
    	//Go的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响
    
    	arr := [3]int{11, 22, 33}
    	test01(arr)
    	fmt.Println("main arr=", arr) //
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    9)如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)

    //函数
    func test02(arr *[3]int) {
    	fmt.Printf("arr指针的地址=%p", &arr)
    	(*arr)[0] = 88 //!!
    } 
    
    arr := [3]int{11, 22, 33}
    	fmt.Printf("arr 的地址=%p", &arr)
    	test02(&arr)
    	fmt.Println("main arr=", arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    10)长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度
    长度是数组类型的一部分
    数组的应用案例
    1)创建一个 byte 类型的 26 个元素的数组,分别 放置’A’-'Z‘。使用 for 循环访问所有元素并打印 出来。提示:字符数据运算 ‘A’+1 -> 'B

    //思路
    	//1. 声明一个数组 var myChars [26]byte
    	//2. 使用for循环,利用 字符可以进行运算的特点来赋值 'A'+1 -> 'B'
    	//3. 使用for打印即可
    	//代码:
    	var myChars [26]byte
    	for i := 0; i < 26; i++ {
    		myChars[i] = 'A' + byte(i) // 注意需要将 i => byte
    	}
    
    	for i := 0; i < 26; i++ {
    		fmt.Printf("%c ", myChars[i])
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2)请求出一个数组的最大值,并得到对应的下标。

    //思路
    	//1. 声明一个数组 var intArr[5] = [...]int {1, -1, 9, 90, 11}
    	//2. 假定第一个元素就是最大值,下标就0
    	//3. 然后从第二个元素开始循环比较,如果发现有更大,则交换
    	var intArr [6]int = [...]int {1, -1, 9, 90, 11, 9000}
    	maxVal := intArr[0]
    	maxValIndex := 0
    
    	for i := 1; i < len(intArr); i++ {
    		//然后从第二个元素开始循环比较,如果发现有更大,则交换
    		if maxVal < intArr[i] {
    			maxVal = intArr[i]
    			maxValIndex = i
    		}
    	}
    	fmt.Printf("maxVal=%v maxValIndex=%v\n\n", maxVal, maxValIndex)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3)请求出一个数组的和和平均值。for-range

    //思路
    	//1. 就是声明一个数组  var intArr[5] = [...]int {1, -1, 9, 90, 11}
    	//2. 求出和sum
    	//3. 求出平均值
    	//代码
    	var intArr2 [5]int = [...]int {1, -1, 9, 90, 12}
    	sum := 0
    	for _, val := range intArr2 {
    		//累计求和
    		sum += val
    	}
    	//如何让平均值保留到小数.
    	fmt.Printf("sum=%v 平均值=%v \n\n", sum, float64(sum) / float64(len(intArr2)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4)要求:随机生成五个数,并将其反转打印 , 复杂应用

    //思路
    	//1. 随机生成五个数 , rand.Intn() 函数
    	//2. 当我们得到随机数后,就放到一个数组 int数组
    	//3. 反转打印 , 交换的次数是  len / 2, 倒数第一个和第一个元素交换, 倒数第2个和第2个元素交换
    
    	var intArr3 [5]int 
    	//为了每次生成的随机数不一样,我们需要给一个seed值
    	len := len(intArr3)
    	
    	rand.Seed(time.Now().UnixNano())
    	for i := 0; i < len; i++ {
    		intArr3[i] = rand.Intn(100) //  0<=n<100
    	}
    
    	fmt.Println("交换前~=", intArr3)
    	//反转打印 , 交换的次数是  len / 2, 
    	//倒数第一个和第一个元素交换, 倒数第2个和第2个元素交换
    	temp := 0  //做一个临时变量
    	for i := 0; i < len / 2; i++ {
    		temp = intArr3[len - 1 - i]  
    		intArr3[len - 1 - i] = intArr3[i]
    		intArr3[i] = temp
    	}
    
    	fmt.Println("交换后~=", intArr3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    切片

    切片的基本介绍
    1)切片的英文是 slice
    2)切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制。
    3)切片的使用和数组类似,遍历切片、访问切片的元素和求切片长度 len(slice)都一样。
    4)切片的长度是可以变化的,因此切片是一个可以动态变化数组。
    5)切片定义的基本语法: var 切片名 []类型 比如:var a [] int

    1. slice 的确是一个引用类型
    2. slice 从底层来说,其实就是一个数据结构(struct 结构体)
      type slice struct { ptr *[2]int len int cap int }
      切片的使用
      第一种方式:定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面的案例就是这 样的。
    var intArr [5]int = [...]int{1, 22, 33, 66, 99}
    	//声明/定义一个切片
    	slice := intArr[1:3] 
    	fmt.Println("intArr=", intArr)
    	fmt.Println("slice 的元素是 =", slice) //  22, 33
    	fmt.Println("slice 的元素个数 =", len(slice)) // 2
    	fmt.Println("slice 的容量 =", cap(slice)) // 切片的容量是可以动态变化  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第二种方式:通过 make 来创建切片. 基本语法:var 切片名 []type = make([]type, len, [cap])
    参数说明: type: 就是数据类型 len : 大小 cap :指定切片容量,可选, 如果你分配了 cap,则要 求 cap>=len.

    //演示切片的使用 make
    	var slice []float64 = make([]float64, 5, 10)
    	slice[1] = 10
    	slice[3] = 20
    	//对于切片,必须make使用.
    	fmt.Println(slice)
    	fmt.Println("slice的size=", len(slice))
    	fmt.Println("slice的cap=", cap(slice))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对上面代码的小结:
    1)通过 make 方式创建切片可以指定切片的大小和容量
    2)如果没有给切片的各个元素赋值,那么就会使用默认值[int , float=> 0 string =>”” bool => false]
    3)通过 make 方式创建的切片对应的数组是由 make 底层维护,对外不可见,即只能通过 slice 去 访问各个元素.
    第 3 种方式:定义一个切片,直接就指定具体数组,使用原理类似 make 的方式

    	fmt.Println()
    	//第3种方式:定义一个切片,直接就指定具体数组,使用原理类似make的方式
    	var strSlice []string = []string{"tom", "jack", "mary"}
    	fmt.Println("strSlice=", strSlice)
    	fmt.Println("strSlice size=", len(strSlice)) //3
    	fmt.Println("strSlice cap=", cap(strSlice)) // ?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    切片的遍历
    for 循环常规方式遍历

    //使用常规的for循环遍历切片
    	var arr [5]int = [...]int{10, 20, 30, 40, 50}
    	//slice := arr[1:4] // 20, 30, 40
    	slice := arr[1:4]
    	for i := 0; i < len(slice); i++ {
    		fmt.Printf("slice[%v]=%v ", i, slice[i])
    	}
    
    	fmt.Println()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    for-range 结构遍历切片

    //使用for--range 方式遍历切片
    	for i, v := range slice {
    		fmt.Printf("i=%v v=%v \n", i, v)
    	}
    
    • 1
    • 2
    • 3
    • 4

    切片的使用的注意事项和细节讨论
    1)切片初始化时 var slice = arr[startIndex:endIndex]
    说明:从 arr 数组下标为 startIndex,取到 下标为 endIndex 的元素(不含 arr[endIndex])。
    2)切片初始化时,仍然不能越界。范围在 [0-len(arr)] 之间,但是可以动态增长.

    var slice = arr[0:end] 可以简写 var slice = arr[:end] 
    var slice = arr[start:len(arr)] 可以简写: var slice = arr[start:] 
    var slice = arr[0:len(arr)] 可以简写: var slice = arr[:]
    
    • 1
    • 2
    • 3

    3)cap 是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
    4)切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者 make 一 个空间供切片来使用
    5)切片可以继续切片

    slice2 := slice[1:2] //  slice [ 20, 30, 40]    [30]
    	slice2[0] = 100  // 因为arr , slice 和slice2 指向的数据空间是同一个,因此slice2[0]=100,其它的都变化
    	fmt.Println("slice2=", slice2)
    	fmt.Println("slice=", slice)
    	fmt.Println("arr=", arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6)用 append 内置函数,可以对切片进行动态追加

    //用append内置函数,可以对切片进行动态追加
    	var slice3 []int = []int{100, 200, 300}
    	//通过append直接给slice3追加具体的元素
    	slice3 = append(slice3, 400, 500, 600)
    	fmt.Println("slice3", slice3) //100, 200, 300,400, 500, 600
    
    	//通过append将切片slice3追加给slice3
    	slice3 = append(slice3, slice3...) // 100, 200, 300,400, 500, 600 100, 200, 300,400, 500, 600
    	fmt.Println("slice3", slice3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    切片 append 操作的底层原理分析: 切片 append 操作的本质就是对数组扩容
    go 底层会创建一下新的数组 newArr(安装扩容后大小)
    将 slice 原来包含的元素拷贝到新的数组 newArr
    slice 重新引用到 newArr
    注意 newArr 是在底层来维护的,程序员不可见
    7)切片的拷贝操作 切片使用 copy 内置函数完成拷贝

    //切片使用copy内置函数完成拷贝,举例说明
    	fmt.Println()
    	var slice4 []int = []int{1, 2, 3, 4, 5}
    	var slice5 = make([]int, 10)
    	copy(slice5, slice4)
    	fmt.Println("slice4=", slice4)// 1, 2, 3, 4, 5
    	fmt.Println("slice5=", slice5) // 1, 2, 3, 4, 5, 0 , 0 ,0,0,0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (1)copy(para1, para2) 参数的数据类型是切片
    (2)按照上面的代码来看, slice4 和 slice5 的数据空间是独立,相互不影响,也就是说 slice4[0]= 999, slice5[0] 仍然是 1

    string 和 slice
    1)string 底层是一个 byte 数组,因此 string 也可以进行切片处理 案例演示:

    //string底层是一个byte数组,因此string也可以进行切片处理
    	str := "hello@atguigu"
    	//使用切片获取到 atguigu
    	slice := str[6:] 
    	fmt.Println("slice=", slice)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2)string 和切片在内存的形式,以 “abcd” 画出内存示意图
    3)string 是不可变的,也就说不能通过 str[0] = ‘z’ 方式来修改字符串
    4)如果需要修改字符串,可以先将 string -> []byte / 或者 []rune -> 修改 -> 重写转成 string

    	//如果需要修改字符串,可以先将string -> []byte / 或者 []rune -> 修改 -> 重写转成string
    	//"hello@atguigu" =>改成 "zello@atguigu"
    	arr1 := []byte(str) 
    	arr1[0] = 'z'
    	str = string(arr1)
    	fmt.Println("str=", str)
    	// 细节,我们转成[]byte后,可以处理英文和数字,但是不能处理中文
    	// 原因是 []byte 字节来处理 ,而一个汉字,是3个字节,因此就会出现乱码
    	// 解决方法是 将  string 转成 []rune 即可, 因为 []rune是按字符处理,兼容汉字
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    冒泡排序实现

    //冒泡排序
    func BubbleSort(arr *[5]int) {
    	fmt.Println("排序前arr=", (*arr))
    	temp := 0 //临时变量(用于做交换)
    	//冒泡排序..一步一步推导出来的
    	for i :=0; i < len(*arr) - 1; i++ {
    		for j := 0; j < len(*arr) - 1 - i; j++ {
    			if (*arr)[j] > (*arr)[j + 1] {
    				//交换
    				temp = (*arr)[j]
    				(*arr)[j] = (*arr)[j + 1]
    				(*arr)[j + 1] = temp
    			}
    		}
    	}
    	fmt.Println("排序后arr=", (*arr))
    
    }
    func main() {
    	//定义数组
    	arr := [5]int{24,69,80,57,13}
    	//将数组传递给一个函数,完成排序
    	BubbleSort(&arr)
    	fmt.Println("main arr=", arr) //有序? 是有序的
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    查找

    //有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王
    	//猜数游戏:从键盘中任意输入一个名称,判断数列中是否包含此名称【顺序查找】
    	//思路
    	//1 定义一个数组, 白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王 字符串数组
    	//2.从控制台接收一个名字,依次比较,如果发现有,提示
    
    	//代码
    	names := [4]string{"白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"}
    	var heroName = ""
    	fmt.Println("请输入要查找的人名...")
    	fmt.Scanln(&heroName)
    	//顺序查找:第2种方式
    	index := -1
    
    	for i := 0; i < len(names); i++ {
    		if heroName == names[i] {
    			index = i //将找到的值对应的下标赋给 index
    			break
    		} 
    	}
    	if index != -1 {
    		fmt.Printf("找到%v , 下标%v \n", heroName, index)
    	} else {
    		fmt.Println("没有找到", heroName)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    二分查找

    //二分查找的函数
    /*
    二分查找的思路: 比如我们要查找的数是 findVal
    1. arr是一个有序数组,并且是从小到大排序
    2.  先找到 中间的下标 middle = (leftIndex + rightIndex) / 2, 然后让 中间下标的值和findVal进行比较
    2.1 如果 arr[middle] > findVal ,  就应该向  leftIndex ---- (middle - 1)
    2.2 如果 arr[middle] < findVal ,  就应该向  middel+1---- rightIndex
    2.3 如果 arr[middle] == findVal , 就找到
    2.4 上面的2.1 2.2 2.3 的逻辑会递归执行
    3. 想一下,怎么样的情况下,就说明找不到[分析出退出递归的条件!!]
    if  leftIndex > rightIndex {
       // 找不到..
       return ..
    }
    */
    func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {
    
    	//判断leftIndex 是否大于 rightIndex
    	if leftIndex > rightIndex {
    		fmt.Println("找不到")
    		return
    	}
    
    	//先找到 中间的下标
    	middle := (leftIndex + rightIndex) / 2
    
    	if (*arr)[middle] > findVal {
    		//说明我们要查找的数,应该在  leftIndex --- middel-1
    		BinaryFind(arr, leftIndex, middle - 1, findVal)
    	} else if (*arr)[middle] < findVal {
    		//说明我们要查找的数,应该在  middel+1 --- rightIndex
    		BinaryFind(arr, middle + 1, rightIndex, findVal)
    	} else {
    		//找到了
    		fmt.Printf("找到了,下标为%v \n", middle)
    	}
    }
    
    func main() {
    	arr := [6]int{1,8, 10, 89, 1000, 1234}
    	//测试一把
    	BinaryFind(&arr, 0, len(arr) - 1, -6)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    二维数组
    使用方式 1: 先声明/定义,再赋值
    语法: var 数组名 [大小][大小]类型
    比如: var arr [2][3]int , 再赋值。

    var arr2 [2][3]int //以这个为例来分析arr2在内存的布局!!
    	arr2[1][1] = 10
    	fmt.Println(arr2)
    	fmt.Printf("arr2[0]的地址%p\n", &arr2[0])
    	fmt.Printf("arr2[1]的地址%p\n", &arr2[1])
    
    	fmt.Printf("arr2[0][0]的地址%p\n", &arr2[0][0])
    	fmt.Printf("arr2[1][0]的地址%p\n", &arr2[1][0])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用方式 2: 直接初始化
    声明:var 数组名 [大小][大小]类型 = [大小][大小]类型{{初值…},{初值…}}
    赋值(有默认值,比如 int 类型的就是 0)

    fmt.Println()
    	arr3  := [2][3]int{{1,2,3}, {4,5,6}}
    	fmt.Println("arr3=", arr3)
    
    • 1
    • 2
    • 3

    二维数组的遍历
    双层 for 循环完成遍历

    //演示二维数组的遍历
    	var arr3  = [2][3]int{{1,2,3}, {4,5,6}}
    
    	//for循环来遍历
    	for i := 0; i < len(arr3); i++ {
    		for j := 0; j < len(arr3[i]); j++ {
    			fmt.Printf("%v\t", arr3[i][j])
    		}
    		fmt.Println()
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    for-range 方式完成遍历

    //演示二维数组的遍历
    	var arr3  = [2][3]int{{1,2,3}, {4,5,6}}
    	//for-range来遍历二维数组
    	for i, v := range arr3 {
    		for j, v2 := range v {
    			fmt.Printf("arr3[%v][%v]=%v \t",i, j, v2)
    		}
    		fmt.Println()	
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    map的声明

    map 是 key-value 数据结构,又称为字段或者关联数组。类似其它编程语言的集合, 在编程中是经常使用到
    基本语法
    var map 变量名 map[keytype]valuetype
    key 可以是什么类型
    golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还可以是只 包含前面几个类型的 接口, 结构体, 数组
    通常 key 为 int 、string
    注意: slice, map 还有 function 不可以,因为这几个没法用 == 来判断
    valuetype 可以是什么类型
    valuetype 的类型和 key 基本一样,这里我就不再赘述了
    通常为: 数字(整数,浮点数),string,map,struct
    注意:声明是不会分配内存的,初始化需要 make ,分配内存后才能赋值和使用。 案例演示
    map 的使用

    //第一种使用方式
    	
    	var a map[string]string
    	//在使用map前,需要先make , make的作用就是给map分配数据空间
    	a = make(map[string]string, 10)
    	a["no1"] = "宋江" //ok?
    	a["no2"] = "吴用" //ok?
    	a["no1"] = "武松" //ok?
    	a["no3"] = "吴用" //ok?
    	fmt.Println(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    	//第二种方式
    	cities := make(map[string]string)
    	cities["no1"] = "北京"
    	cities["no2"] = "天津"
    	cities["no3"] = "上海"
    	fmt.Println(cities)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //第三种方式
    	heroes := map[string]string{
    		"hero1" : "宋江",
    		"hero2" : "卢俊义",
    		"hero3" : "吴用",
    	}
    	heroes["hero4"] = "林冲"
    	fmt.Println("heroes=", heroes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    map 的增删改查操作

    //第二种方式
    	cities := make(map[string]string)
    	cities["no1"] = "北京"
    	cities["no2"] = "天津"
    	cities["no3"] = "上海"
    	fmt.Println(cities)
    
    	//因为 no3这个key已经存在,因此下面的这句话就是修改
    	cities["no3"] = "上海~" 
    	fmt.Println(cities)
    
    	//演示删除
    	delete(cities, "no1")
    	fmt.Println(cities)
    	//当delete指定的key不存在时,删除不会操作,也不会报错
    	delete(cities, "no4")
    	fmt.Println(cities)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    map查找

    //演示map的查找
    	val, ok := cities["no2"]
    	if ok {
    		fmt.Printf("有no1 key 值为%v\n", val)
    	} else {
    		fmt.Printf("没有no1 key\n")
    	}
    
    	//如果希望一次性删除所有的key
    	//1. 遍历所有的key,如何逐一删除 [遍历]
    	//2. 直接make一个新的空间
    	cities = make(map[string]string)
    	fmt.Println(cities)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    map遍历

    	//使用for-range遍历map
    	//第二种方式
    	cities := make(map[string]string)
    	cities["no1"] = "北京"
    	cities["no2"] = "天津"
    	cities["no3"] = "上海"
    	
    	for k, v := range cities {
    		fmt.Printf("k=%v v=%v\n", k, v)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    	//使用for-range遍历一个结构比较复杂的map
    	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=", k1)
    		for k2, v2 := range v1 {
    				fmt.Printf("\t k2=%v v2=%v\n", k2, v2)
    		}
    		fmt.Println()
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

    	//演示map切片的使用
    	/*
    	要求:使用一个map来记录monster的信息 name 和 age, 也就是说一个
    	monster对应一个map,并且妖怪的个数可以动态的增加=>map切片
    	*/
    	//1. 声明一个map切片
    	var monsters []map[string]string
    	monsters = make([]map[string]string, 2) //准备放入两个妖怪
    	//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"
    	}
    
    	// 下面这个写法越界。
    	// if monsters[2] == nil {
    	// 	monsters[2] = make(map[string]string, 2)
    	// 	monsters[2]["name"] = "狐狸精"
    	// 	monsters[2]["age"] = "300"
    	// }
    
    	//这里我们需要使用到切片的append函数,可以动态的增加monster
    	//1. 先定义个monster信息
    	newMonster := map[string]string{
    		"name" : "新的妖怪~火云邪神",
    		"age" : "200",
    	}
    	monsters = append(monsters, newMonster)
    
    	fmt.Println(monsters)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    map排序
    1)golang 中没有一个专门的方法针对 map 的 key 进行排序
    2)golang 中的 map 默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出 可能不一样.
    3)golang 中 map 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可

    //map的排序
    	map1 := make(map[int]int, 10)
    	map1[10] = 100
    	map1[1] = 13
    	map1[4] = 56
    	map1[8] = 90
    
    	fmt.Println(map1)
    
    	//如果按照map的key的顺序进行排序输出
    	//1. 先将map的key 放入到 切片中
    	//2. 对切片排序 
    	//3. 遍历切片,然后按照key来输出map的值
    
    	var keys []int
    	for k, _ := range map1 {
    		keys = append(keys, k)
    	}
    	//排序
    	sort.Ints(keys)
    	fmt.Println(keys)
    
    	for _, k := range keys{
    		fmt.Printf("map1[%v]=%v \n", k, map1[k])
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    1)map 是引用类型,遵守引用类型传递的机制,在一个函数接收 map,修改后,会直接修改原来 的 map
    2)map 的容量达到后,再想 map 增加元素,会自动扩容,并不会发生 panic,也就是说 map 能动 态的增长 键值对(key-value)
    3)map 的 value 也经常使用 struct 类型,更适合管理复杂的数据(比前面 value 是一个 map 更好),比如 value 为 Student 结构体

    type Stu struct {
    	Name string
    	Age int
    	Address string
    }
    
    //map的value 也经常使用struct 类型,
    	//更适合管理复杂的数据(比前面value是一个map更好),
    	//比如value为 Student结构体 【案例演示,因为还没有学结构体,体验一下即可】
    	//1.map 的 key 为 学生的学号,是唯一的
    	//2.map 的 value为结构体,包含学生的 名字,年龄, 地址
    
    	students := make(map[string]Stu, 10)
    	//创建2个学生
    	stu1 := Stu{"tom", 18, "北京"}
    	stu2 := Stu{"mary", 28, "上海"}
    	students["no1"] = stu1
    	students["no2"] = stu2
    
    	fmt.Println(students)
    
    	//遍历各个学生信息
    	for k, v := range students {
    		fmt.Printf("学生的编号是%v \n", k)
    		fmt.Printf("学生的名字是%v \n", v.Name)
    		fmt.Printf("学生的年龄是%v \n", v.Age)
    		fmt.Printf("学生的地址是%v \n", v.Address)
    		fmt.Println()
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 相关阅读:
    MySQL学习笔记-8. 数据库高并发、高性能的基本保证--如何解决数据库超大容量
    Label 相关论文汇总
    oracle 迁移PG 博客
    LSTM-Attention单维时间序列预测研究(Matlab代码实现)
    带你认识JDK8中超nice的Native Memory Tracking
    Linux下的yum和vim
    C# 连接SQL Sever 数据库与数据查询实例 数据仓库
    Unity可视化Shader工具ASE介绍——9、整理节点让复杂的Shader条理更清晰
    无法打开文件“opengl32.lib”
    优雅地结合 Kotlin 特性深度解耦标题栏
  • 原文地址:https://blog.csdn.net/qq_34913864/article/details/124917050