diff --git a/day02/map/README.md b/day02/map/README.md index 1568777..16d66fc 100644 --- a/day02/map/README.md +++ b/day02/map/README.md @@ -2,6 +2,56 @@ Go中的map是一种无序的键值对集合,底层基于哈希表实现。每个键必须是可比较的类型,值可以是任意类型。map提供快速的查找、插入和删除操作。 +为啥有了切片,我还有有map + +1. 切片如何寻找到指定的对象 + +```go +func main() { + personList := []Person{} + personList = append(personList, Person{ + Name: "张三", + Age: 18, + }) + personList = append(personList, Person{ + Name: "李四", + Age: 20, + }) + + // 如何找到李四这个对象 + for _, person := range personList { + if person.Name == "李四" { + // 找到了 + fmt.Println("找到李四:", person) + break + } + } + + // 如果我这个切切片非常大, 上百万, 上千万 + // 通过切片查找一个元素, 效率非常低, O(n) + // 这时可以使用 map 来优化查找效率 + + // 使用map 来存储人员信息, key 是姓名, value 是 Person 对象 + personMap := map[string]Person{} + personMap["张三"] = Person{ + Name: "张三", + Age: 22, + } + personMap["李四"] = Person{ + Name: "李四", + Age: 24, + } + fmt.Println("通过Map直接找到李四: ", personMap["李四"]) +} + +type Person struct { + Name string + Age int +} +``` + +![alt text](image.png) + ## 核心概念 map的底层实现是哈希表,通过键的哈希值快速定位值。键必须是可哈希的类型(如int、string、指针等),值可以是任意类型。map是引用类型,赋值时共享底层数据。 @@ -11,6 +61,9 @@ map的底层实现是哈希表,通过键的哈希值快速定位值。键必 var m map[string]int // m == nil ``` ++ 无序的: (hash) ++ key不允许重复 (hash) + ## 创建和初始化 **原理**:使用make创建map,指定初始容量可以提高性能。也可以使用字面量初始化。 @@ -101,6 +154,8 @@ fmt.Println(m1) // map[a:1 b:2] m1也被修改 fmt.Println(m2) // map[a:1 b:2] ``` +![alt text](image-1.png) + ## map作为函数参数 **原理**:map作为参数传递时,是引用传递,函数内修改会影响调用者。 @@ -117,6 +172,9 @@ func main() { } ``` +## 使用map去重 + + ## 扩展 Map的底层原理 从Go 1.23开始,map的底层实现从传统的哈希表改为基于Swiss table的高性能实现,大幅提升了性能和内存效率。以下详细讲解其工作原理。 @@ -233,4 +291,7 @@ map是Go中常用的数据结构,适合需要快速查找的场景。记住map 6. **函数参数** 编写一个函数,接受map作为参数,添加一个新键值对。 +7. **去重** + 给定一个只能的[]string切片,使用map来进行去重 + 请将代码写在 `main.go` 文件中,并运行测试。 \ No newline at end of file diff --git a/day02/map/image-1.png b/day02/map/image-1.png new file mode 100644 index 0000000..e88d43f Binary files /dev/null and b/day02/map/image-1.png differ diff --git a/day02/map/image.png b/day02/map/image.png new file mode 100644 index 0000000..a1c4330 Binary files /dev/null and b/day02/map/image.png differ diff --git a/day02/map/main.go b/day02/map/main.go new file mode 100644 index 0000000..ed3f68a --- /dev/null +++ b/day02/map/main.go @@ -0,0 +1,135 @@ +package main + +import "fmt" + +var ( + globalMap = map[string]string{} +) + +func main() { + personList := []Person{} + personList = append(personList, Person{ + Name: "张三", + Age: 18, + }) + personList = append(personList, Person{ + Name: "李四", + Age: 20, + }) + + // 如何找到李四这个对象 + for _, person := range personList { + if person.Name == "李四" { + // 找到了 + fmt.Println("找到李四:", person) + break + } + } + + // 如果我这个切切片非常大, 上百万, 上千万 + // 通过切片查找一个元素, 效率非常低, O(n) + // 这时可以使用 map 来优化查找效率 + + // 使用map 来存储人员信息, key 是姓名, value 是 Person 对象 + personMap := map[string]Person{} + personMap["张三"] = Person{ + Name: "张三", + Age: 22, + } + personMap["李四"] = Person{ + Name: "李四", + Age: 24, + } + fmt.Println("通过Map直接找到李四: ", personMap["李四"]) + + /* + 1. 声明和初始化 + map[keyType]valueType + keyType 必须是可以hash的类型, 常见的可以是 string, int, float64, bool + */ + // 先声明,在初始化 + var animalMap map[string]string + // animalMap = make(map[string]string) + // 初始化一个空的 map + animalMap = map[string]string{} + fmt.Println("animalMap:", animalMap) + + // 声明的同时初始化(常用, 简短声明用不了的情况, 只能用 var 声明) + var cityMap = map[string]string{ + "北京": "北京是中国的首都", + "上海": "上海是中国的经济中心", + "广州": "广州是中国的南大门", + } + fmt.Println("cityMap:", cityMap) + + // 简短声明(最常用, 推荐) + cityMap1 := map[string]string{ + "北京": "北京是中国的首都", + "上海": "上海是中国的经济中心", + "广州": "广州是中国的南大门", + } + fmt.Println("cityMap:", cityMap1) + + /* * 2. 访问元素 + value := map[key] + */ + fmt.Println("== 访问元素 ==") + beijingDesc := cityMap1["北京"] + fmt.Println("北京的描述是:", beijingDesc) + + /* * 3. 删除元素 + delete(map, key) + */ + delete(cityMap1, "广州") + fmt.Println("删除广州后 cityMap1:", cityMap1) + + /* * 2. 增加/修改元素 + map[key] = value + key 不存在就是新增,key 存在就是修改 + */ + cityMap1["深圳"] = "深圳是中国的科技创新中心" + fmt.Println("添加深圳后 cityMap1:", cityMap1) + + /* + 如何判断某个key是否存在 + comok断言: value, ok := map[key] + 常用场景: 有了后就不修改, 没有才添加 + */ + value, ok := cityMap1["上海"] + if ok { + fmt.Println("上海存在,描述是:", value) + } else { + fmt.Println("上海不存在") + } + + /* + map的遍历 + range iterage -> (k/indev/..., v) + for key, value := range map { + } + */ + fmt.Println("== map的遍历 ==") + for key, value := range cityMap1 { + fmt.Printf("%s 的描述是: %s\n", key, value) + } + // 通常只需要 key 的场景, 对象修改 + for key := range cityMap1 { + fmt.Printf("%s 的描述是: %s\n", key, cityMap1[key]) + } + + /* + map 是一种引用类型, 默认值是 nil, 通过指针 指向数据(key value)c存储区(bucket) + */ + fmt.Println("== map 是引用类型 ==") + m1 := map[string]int{"a": 1} + m2 := m1 // 共享底层数据 + + m2["b"] = 2 + fmt.Println(m1) // map[a:1 b:2] m1也被修改 + fmt.Println(m2) // map[a:1 b:2] +} + +type Person struct { + Name string + Age int +} diff --git a/day02/map/slices_vs_map.drawio b/day02/map/slices_vs_map.drawio new file mode 100644 index 0000000..7472485 --- /dev/null +++ b/day02/map/slices_vs_map.drawio @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/day02/slice/README.md b/day02/slice/README.md index ecc33f9..a7a5c68 100644 --- a/day02/slice/README.md +++ b/day02/slice/README.md @@ -251,14 +251,21 @@ func MySortV2(arr *[]int) { 6. **函数参数** 编写一个函数,接受字符串切片作为参数,将所有字符串转换为大写并返回新切片, 需要实现2个版本, 1.一个值版本, 2. 指针版本。 -7. **问题代码** +7. **问题代码(思考题)** 分析下面2个版本的差异, 给出最优版本 ```go requests := []string{"a", "b", "c"} -func HandleRequest(*[]any{}) +func HandleRequest(*[]any{}) { + // boxed 一下,再传递 + // 值传递,容器修改到副本,而不是对象本身 + handle_one(&obj) +} -func HandleRequest([]*any{}) +func HandleRequest([]*any{}) { + // box 给下去就行了 + handle_one(obj) +} ``` 请将代码写在 `main.go` 文件中,并运行测试。