• 【Go ~ 0到1 】 第三天 6月27 slice,map 与 函数


    1. 切片

    1.1 切片的定义

    切片指向了底层的一个数组

    1.2 切片的长度

    切片的长度等于切片元素的个数

    1.3 切片的容量

    切片的容量是底层数组从切片的第一个元素到最后一个元素的数量

    	//默认情况下  切片的长度 == 容量
    
    	s1 := []int{1, 2, 3}
    
    	fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.4 切片存在的意义

    在Go中,数组一旦创建,大小就固定了无dui法改变数组的长度 因此有了切片

    切片的底层是动态数组 可以动态的往数组中添加,删除元素

    类似与 Java 中的集合

    1.5 创建一个切片

    1.5.1 方式一 : 元素个数作为默认容量和长度大小

    //默认情况下  切片的长度 == 容量
    
    	s1 := []int{1, 2, 3}
    
    • 1
    • 2
    • 3

    1.5.2 方式二 : 指定切片容量大小和长度

    //指定切片的容量和长度大小
    ss1 := make([]int, 3, 5)
    
    	fmt.Printf("当前切片的长度 %d 切片容量 %d", len(ss1), cap(ss1))
    
    • 1
    • 2
    • 3
    • 4

    1.5.3 方式三 : 通过数组创建

    	// 创建切片的方式三 :
    
    	//先创建数组
    	a1 := [...]int{1, 2, 3}
    	//将数组转换为切片
    	sa1 := a1[0:len(a1)]
    
    	fmt.Printf("当前切片的长度 %d 切片容量 %d", len(sa1), cap(sa1))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2. 切片扩容

    切片一次扩容为上一次 容量*2 当元素长度 >

    2.1 append 增加 元素

    	//默认情况下  切片的长度 == 容量
    
    	s1 := []int{1, 2, 3}
    
    	fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
    
    	// 添加一个元素  切片容量将增加2倍  长度将增加一
    	s1 = append(s1, 4)
    
    	fmt.Printf("当前切片的长度 %d 切片容量 %d", len(s1), cap(s1))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2. 切片的扩容机制

    • 1、当需要的容量超过原切片容量的两倍时,会使用需要的容量作为新容量。

    • 2、当原切片长度小于1024时,新切片的容量会直接翻倍。而当原切片的容量大于等于1024时,会反复地增加25%,直到新容量超过所需要的容量。

    上述都是不精确的 容量在计算完毕后 还要考虑到内存的高效率利用,进行内存对齐

    2.2. 1需要容量 > old * 2 则 新容量 = 需要

    	// 情景一 : 如果需要的容量大于当前容量*2
    	s1 := []int{0}
    
    	fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
    
    	s1 = append(s1, 1, 2, 3)
    
    	fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
    
    	//输出结果  原始切片的长度 1 切片容量 1  
    	//		   新切片的长度 4 切片容量 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.2.2 需要容量 < old * 2 且 old < 1024 则 新容量 = 原容量 * 2

    	//情景二 :  如果需要的容量 小于 当前容量 * 2 且 当前容量 少于 1024
    
    	s1 := make([]int, 99, 100) // 原有容量大于 1024 则 扩容规则为  oldcap  = oldcap *2  则结果应为 200
    
    	fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
    
    	s1 = append(s1, 1, 2, 3)
    
    	fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1)) //实际结果为 200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.2.3 需要容量 < old * 2 且 old > 1024 则 新容量 = 原容量 * 1.25

    	//情景二 :  如果需要的容量 小于 当前容量 * 2 且 当前容量 少于 1024
    
    	s1 := make([]int, 1022, 1024) // 原有容量大于 1024 则 扩容规则为  oldcap  = oldcap *1.5  则结果应为 1536
    
    	fmt.Printf("原始切片的长度 %d 切片容量 %d \n", len(s1), cap(s1))
    
    	s1 = append(s1, 1, 2, 3)
    
    	fmt.Printf("新切片的长度 %d 切片容量 %d \n", len(s1), cap(s1)) //实际结果为 1536
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. 切片复制

    s1 := []int{1, 2, 3}
    
    s2 := make([]int, 2, 2)
    
    copy(s2, s1) // 参数一: 目的地  参数二 : 来源
    
    	fmt.Println(s2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    切片复制时 将根据需要复制元素的切片长度进行复制 如上述代码所示

    则 s2 的打印结果为 1,2

    4. 获取内存地址值与对应的取值

    在 Go 语言中 不存在 对指针的操作 与内存相关的有两个符号 需要记住

    4.1 ’ & ’ 获取对象内存地址值

    	s1 := 10
    
    	fmt.Printf("s1 的内存地址为 %v", &s1)
    
    • 1
    • 2
    • 3

    4.2 ’ * ’ 根据内存地址值获取值

    mt.Printf("根据内存地址取值 %v", *(&s1))
    
    • 1

    5. new 函数和 make 函数的用法

    两者都是用来申请内存地址的 但 还是有区别的

    new 主要用来给基本数据类型申请内存地址 而 make 是用来给 slice, map , chan 申请内存地址的

    4. map 的使用

    4.1 map 的 创建

    	//创建一个map
    	m := make(map[string]int)
    	//创建一个map
    	m := make(map[string]int,[容量大小])
    
    • 1
    • 2
    • 3
    • 4

    4.2 map 添加值

    	m["张三"] = 19
    	m["李四"] = 20
    	m["王五"] = 21
    
    • 1
    • 2
    • 3

    4.3 map 遍历

    4.3.1 遍历key,value

    //遍历:
    
    for key, value := range m {
        fmt.Printf("%v ---- %v \n", key, value)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.3.2 遍历key

    //遍历key
    	for key, _ := range m {
    		fmt.Println(key)
    	}
    
    • 1
    • 2
    • 3
    • 4

    4.3.3 遍历 value

    //遍历value
    
    	for _, value := range m {
    		fmt.Println(value)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.4 根据 key 删除 value

    	//根据key删除 value
    	delete(m,"张三")
    
    • 1
    • 2

    4.5 判断key是否存在

    约定俗成使用 ok 标识一个 key是否存在

    //判断key是否存在
    	i, ok := m["张三1"]
    
    	if !ok {
    		fmt.Printf("%v 不存在当前key \n", m)
    	} else {
    		fmt.Printf("当前key对应的值为 %v \n", i)
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5. 函数

    5.1 什么是函数

    函数时对代码的封装 把一段代码抽取出来, 给它起个名字 每次使用的时候只需要调用函数名即可 提高了代码的利用率

    5.2 函数的定义

    func mymethod(x int, y int) (ret int) {
    	return x + y
    }
    
    • 1
    • 2
    • 3

    注意事项 :

    1. 形参列表如果连续多个参类型数一致时,只需要写最后一个参数的类型 ,其他参数的类型可以省略

    2. 如果 我们对返回值进行了命名(等同于声明一个局部变量), 可以直接写一个return

    func mymethod1(x,y int)(sum int)  {
    	sum = x + y
    	return 
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 多个返回值时,同样也可以对返回值进行命名
    func mymethod1(x, y int) (sum int, del int) {
    	sum = x + y
    	del = x - y
    	return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.3 匿名函数

    如果一个函数我们只想使用一次,那么可以使用匿名函数

    	//匿名函数赋值给一个变量
    	mn1 := func(x, y int) (int) {
    		return x + y
    	}
    
    	fmt.Println(mn1(1, 9))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.4 全局匿名函数

    //全局的匿名函数 赋值给一个变量
    var (
    	m = func(x, y int) int {
    		return x + y
    	}
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    end 练习

    1.统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1

    str := "what do you want do what"
    	split := strings.Split(str, " ")
    
    	//遍历 字符切片  往  map 集合中 存
    	m1 := make(map[string]int)
    
    	for _, word := range split {
    		//判断字符串是否已经存在
    		_, ok := m1[word]
    		if !ok {
    			//不存在
    			m1[word] = 1
    		} else {
    			//已经存在
    			m1[word] = m1[word] + 1
    		}
    	}
    
    	//打印结果
    	fmt.Println(m1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    手撸时间轮
    深入理解 JVM 之——Java 内存区域与溢出异常
    【2023最新版】Spring Cloud面试题总结(35道题含答案解析)
    打破焦虑!AI 时代的程序员为什么需要云端 IDE?
    树的统计问题
    【猫鼠游戏】一个半径为 1 的圆形水池圆心有一只老鼠,池边有一只猫。
    分享几个方便好用的网盘搜索引擎
    vue3自定义指令
    模板学堂丨DataEase用户操作日志分析大屏
    低代码助力疫情防控:综合管理系统模板
  • 原文地址:https://blog.csdn.net/JAVAlife2021/article/details/125484687