# Map

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是引用类型，赋值时共享底层数据。

```go
// map的零值是nil，不能直接使用
var m map[string]int  // m == nil
```

+ 无序的: (hash)
+ key不允许重复 (hash)

## 创建和初始化

**原理**：使用make创建map，指定初始容量可以提高性能。也可以使用字面量初始化。

```go
// 使用make创建
m1 := make(map[string]int)  // 空map
m2 := make(map[string]int, 10)  // 指定初始容量

// 字面量初始化
m3 := map[string]int{
    "apple":  5,
    "banana": 3,
}
fmt.Println(m3)  // map[apple:5 banana:3]
```

## 访问元素

**原理**：通过键访问值，如果键不存在，返回零值。可以使用第二个返回值检查键是否存在。

```go
m := map[string]int{"a": 1, "b": 2}

value := m["a"]  // 1
fmt.Println(value)

// 检查键是否存在
if val, exists := m["c"]; exists {
    fmt.Println("存在:", val)
} else {
    fmt.Println("不存在")
}
```

## 修改和添加元素

**原理**：直接赋值键即可添加或修改。如果键存在则修改，不存在则添加。

```go
m := make(map[string]int)

m["key1"] = 10  // 添加
m["key1"] = 20  // 修改
fmt.Println(m)   // map[key1:20]
```

## 删除元素

**原理**：使用delete函数删除键值对。如果键不存在，delete不报错。

```go
m := map[string]int{"a": 1, "b": 2, "c": 3}

delete(m, "b")  // 删除键"b"
fmt.Println(m)   // map[a:1 c:3]

delete(m, "d")  // 删除不存在的键，无错误
```

## 遍历map

**原理**：使用range遍历键值对。遍历顺序是随机的，不能依赖顺序。

```go
m := map[string]int{"a": 1, "b": 2, "c": 3}

for key, value := range m {
    fmt.Printf("key: %s, value: %d\n", key, value)
}

// 只遍历键
for key := range m {
    fmt.Println(key)
}
```

## map是引用类型

**原理**：map是引用类型，赋值时共享底层数据。修改一个会影响另一个。

```go
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]
```

![alt text](image-1.png)

## map作为函数参数

**原理**：map作为参数传递时，是引用传递，函数内修改会影响调用者。

```go
func addEntry(m map[string]int, key string, value int) {
    m[key] = value
}

func main() {
    m := make(map[string]int)
    addEntry(m, "new", 42)
    fmt.Println(m)  // map[new:42]
}
```

## 使用map去重


## 扩展 Map的底层原理

从Go 1.23开始，map的底层实现从传统的哈希表改为基于Swiss table的高性能实现，大幅提升了性能和内存效率。以下详细讲解其工作原理。

### 1. Swiss Table结构

Swiss table是一种现代的开放寻址哈希表设计，具有以下特点：
- 每个桶包含一个控制字节数组和数据数组
- 控制字节存储元数据（哈希值的高位、删除标记等）
- 数据与控制字节分离，减少缓存未命中

```
Swiss Table结构示意图：
+-------------------+     +-------------------+
| 控制字节 (8字节)  |     | 数据槽位 (8个)     |
| [H1|H2|H3|H4|...]|     | [k1,v1] [k2,v2]...|
+-------------------+     +-------------------+
| 桶0: 控制元数据   | --> | 桶0: 键值对数据   |
+-------------------+     +-------------------+
| 桶1: 控制元数据   | --> | 桶1: 键值对数据   |
+-------------------+     +-------------------+
```

### 2. 哈希计算和定位

**改进的哈希计算**：
- 使用更快的哈希函数（AesHash或类似）
- 哈希值分为高位和低位
- 高位用于SIMD比较，低位用于定位

```
哈希计算过程：
Key -> hash(key) -> [高7位: SIMD比较] [低位: 位置索引]
```

### 3. 插入和查找过程

**查找优化**：
1. 计算哈希值
2. 使用SIMD指令并行比较控制字节
3. 快速定位匹配的槽位

**插入优化**：
1. 查找空闲槽位
2. 使用线性探测或二次探测
3. 写入控制字节和数据

```
查找示意图（使用SIMD）：
Hash(Key) = 0xABCD1234
控制字节 = 0xAB (高位)
位置索引 = 0x1234 & 掩码

控制字节数组: [AB|CD|EF|00|...]
                    ↑ SIMD比较找到匹配
数据数组: [k1,v1] [k2,v2] [k3,v3] ...
```

### 4. 扩容机制

**渐进式扩容**：
- 当负载因子超过阈值时触发扩容
- 新表大小为2的幂次
- 每次操作迁移少量数据
- 支持就地扩容（in-place rehashing）

```
扩容过程：
旧表: 控制字节 [A|B|C|D] --> 数据 [k1,v1] [k2,v2] [k3,v3] [k4,v4]

新表: 控制字节 [A|0|B|0|C|0|D|0] --> 数据 [k1,v1] [k2,v2] [k3,v3] [k4,v4]
      (插入空字节分隔)
```

### 5. 性能优势

**相比传统哈希表**：
- **更快的查找**：SIMD并行比较，减少分支预测失败
- **更好的缓存利用**：控制字节和数据分离
- **更低的内存开销**：更紧凑的布局
- **抗哈希冲突**：更好的分布减少最坏情况

```
性能对比：
操作      | 传统哈希表 | Swiss Table
查找      | O(1) avg    | O(1) faster
插入      | O(1) avg    | O(1) faster  
内存使用  | ~32字节/对  | ~24字节/对
```

## 总结

map是Go中常用的数据结构，适合需要快速查找的场景。记住map是无序的，键必须可哈希，操作是引用传递的。

## 作业

请同学们完成以下map练习题，以巩固对Go语言map的理解：

1. **创建和初始化map**  
   创建一个map，键为string，值为int，初始化包含3个键值对，并打印map。

2. **访问元素**  
   给定map，检查某个键是否存在，如果存在打印值，否则打印"不存在"。

3. **修改和添加**  
   从空map开始，添加5个键值对，然后修改其中一个值。

4. **删除元素**  
   删除map中的一个元素，并打印删除前后的map。

5. **遍历map**  
   遍历map，计算所有值的和。

6. **函数参数**  
   编写一个函数，接受map作为参数，添加一个新键值对。

7. **去重**
   给定一个只能的[]string切片，使用map来进行去重

请将代码写在 `main.go` 文件中，并运行测试。