python_到_go:第_3_篇_-_数据结构与集合操作.md2025-10-15
./meta --show-details
Published
2025年10月15日
Reading
17 min
Words
16,352
Status
PUBLISHED
Python 到 Go:数据结构与集合操作
📚 系列文章:第 3 篇 / 共 8 篇 🔗 上一篇:基础语法对比
目录
1. 数组 vs 切片(Slice)
1.1 Python 列表(动态数组)
# Python 列表是动态数组(自动扩容)
numbers = [1, 2, 3, 4, 5]
# 添加元素
numbers.append(6) # [1, 2, 3, 4, 5, 6]
numbers.extend([7, 8]) # [1, 2, 3, 4, 5, 6, 7, 8]
numbers.insert(0, 0) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
# 删除元素
numbers.pop() # 删除最后一个
numbers.remove(5) # 删除值为 5 的元素
del numbers[0] # 删除索引 0
# 切片
sub = numbers[1:4] # [2, 3, 4]
reversed_list = numbers[::-1] # 反转
# 长度
print(len(numbers)) # 长度
1.2 Go 数组(固定长度)
// Go 数组是固定长度的(很少直接使用)
var arr [5]int // [0, 0, 0, 0, 0]
arr[0] = 1
arr[1] = 2
// 数组字面量
arr := [5]int{1, 2, 3, 4, 5}
// 自动推断长度
arr := [...]int{1, 2, 3, 4, 5} // 长度为 5
// 数组的问题:
// 1. 长度固定,无法动态扩容
// 2. 长度是类型的一部分
var arr1 [5]int
var arr2 [10]int
// arr1 和 arr2 是不同类型!
1.3 Go 切片(动态数组)⭐
// 切片(Slice)是 Go 中最常用的数据结构
// 类似 Python 列表,动态扩容
// 创建切片
var numbers []int // nil 切片
numbers := []int{} // 空切片
numbers := []int{1, 2, 3, 4, 5} // 初始化
// 使用 make 创建(指定长度和容量)
numbers := make([]int, 5) // 长度 5,容量 5,初始值 [0, 0, 0, 0, 0]
numbers := make([]int, 5, 10) // 长度 5,容量 10
fmt.Println(len(numbers)) // 长度
fmt.Println(cap(numbers)) // 容量
1.4 切片的底层原理
// 切片底层结构
type slice struct {
ptr *[]int // 指向底层数组的指针
len int // 长度
cap int // 容量
}
// 示例
arr := [6]int{1, 2, 3, 4, 5, 6} // 底层数组
s1 := arr[1:4] // [2, 3, 4],len=3, cap=5
s2 := arr[2:5] // [3, 4, 5],len=3, cap=4
// s1 和 s2 共享底层数组!
s1[0] = 999
fmt.Println(arr) // [1, 999, 3, 4, 5, 6] ⚠️ 底层数组被修改
// 这是 Python 列表所没有的行为
可视化:
底层数组:[1, 2, 3, 4, 5, 6]
↑ ↑ ↑
s1 = arr[1:4] [2, 3, 4] (len=3, cap=5)
s2 = arr[2:5] [3, 4, 5] (len=3, cap=4)
2. 列表操作对比
2.1 添加元素
Python:
numbers = [1, 2, 3]
# 末尾添加
numbers.append(4) # [1, 2, 3, 4]
# 批量添加
numbers.extend([5, 6]) # [1, 2, 3, 4, 5, 6]
# 指定位置插入
numbers.insert(0, 0) # [0, 1, 2, 3, 4, 5, 6]
# 合并列表
combined = [1, 2] + [3, 4] # [1, 2, 3, 4]
Go:
numbers := []int{1, 2, 3}
// 末尾添加(append)⭐
numbers = append(numbers, 4) // [1, 2, 3, 4]
// 批量添加(展开切片)
numbers = append(numbers, 5, 6) // [1, 2, 3, 4, 5, 6]
numbers = append(numbers, []int{7, 8}...) // 展开切片
// 头部插入(需要手动实现)
numbers = append([]int{0}, numbers...) // [0, 1, 2, 3, 4, 5, 6, 7, 8]
// 中间插入
func insert(slice []int, index, value int) []int {
slice = append(slice[:index], append([]int{value}, slice[index:]...)...)
return slice
}
numbers = insert(numbers, 2, 99) // 在索引 2 插入 99
// 合并切片
combined := append([]int{1, 2}, []int{3, 4}...)
append 的陷阱⚠️:
// append 可能导致底层数组重新分配
s1 := []int{1, 2, 3}
s2 := s1 // s2 和 s1 共享底层数组
s1 = append(s1, 4) // 如果容量不足,会创建新数组
s1[0] = 999
fmt.Println(s1) // [999, 2, 3, 4]
fmt.Println(s2) // [1, 2, 3] 或 [999, 2, 3](取决于是否重新分配)
// 安全做法:使用 copy
s2 := make([]int, len(s1))
copy(s2, s1) // 深拷贝
2.2 删除元素
Python:
numbers = [1, 2, 3, 4, 5]
# 删除末尾
numbers.pop() # 返回 5,numbers = [1, 2, 3, 4]
# 删除指定索引
numbers.pop(1) # 返回 2,numbers = [1, 3, 4]
# 删除指定值
numbers.remove(3) # numbers = [1, 4]
# 删除切片
del numbers[0:2] # numbers = []
# 清空
numbers.clear() # []
Go:
numbers := []int{1, 2, 3, 4, 5}
// 删除末尾
numbers = numbers[:len(numbers)-1] // [1, 2, 3, 4]
// 删除指定索引
func remove(slice []int, index int) []int {
return append(slice[:index], slice[index+1:]...)
}
numbers = remove(numbers, 1) // [1, 3, 4]
// 删除指定值(需要先查找)
func removeValue(slice []int, value int) []int {
for i, v := range slice {
if v == value {
return append(slice[:i], slice[i+1:]...)
}
}
return slice
}
numbers = removeValue(numbers, 3) // [1, 4]
// 清空(保留容量)
numbers = numbers[:0]
// 清空(释放内存)
numbers = nil
2.3 切片操作
Python:
numbers = [0, 1, 2, 3, 4, 5]
# 基础切片
sub = numbers[1:4] # [1, 2, 3]
sub = numbers[:3] # [0, 1, 2]
sub = numbers[3:] # [3, 4, 5]
sub = numbers[:] # [0, 1, 2, 3, 4, 5] 浅拷贝
# 步长切片
even = numbers[::2] # [0, 2, 4]
odd = numbers[1::2] # [1, 3, 5]
reversed_list = numbers[::-1] # [5, 4, 3, 2, 1, 0]
Go:
numbers := []int{0, 1, 2, 3, 4, 5}
// 基础切片
sub := numbers[1:4] // [1, 2, 3]
sub := numbers[:3] // [0, 1, 2]
sub := numbers[3:] // [3, 4, 5]
sub := numbers[:] // [0, 1, 2, 3, 4, 5] 共享底层数组!
// 完整切片表达式(限制容量)
sub := numbers[1:4:4] // [1, 2, 3],len=3, cap=3
// 格式:slice[low:high:max]
// 步长切片(需要手动实现)
func stepSlice(slice []int, step int) []int {
result := []int{}
for i := 0; i < len(slice); i += step {
result = append(result, slice[i])
}
return result
}
even := stepSlice(numbers, 2) // [0, 2, 4]
// 反转(需要手动实现)
func reverse(slice []int) []int {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
return slice
}
reversed := reverse(numbers) // [5, 4, 3, 2, 1, 0]
2.4 常用操作对比
操作 | Python | Go |
---|---|---|
长度 | len(list) | len(slice) |
追加 | list.append(x) | slice = append(slice, x) |
扩展 | list.extend(other) | slice = append(slice, other...) |
插入 | list.insert(i, x) | 手动实现 |
删除 | list.pop() / list.remove() | 手动实现 |
切片 | list[1:4] | slice[1:4] |
反转 | list[::-1] | 手动实现 |
排序 | list.sort() | sort.Ints(slice) |
复制 | list.copy() / list[:] | copy(dst, src) |
3. 字典 vs 映射(Map)
3.1 Python 字典
# 创建字典
user = {
"name": "Alice",
"age": 30,
"email": "[email protected]"
}
# 访问
print(user["name"]) # "Alice"
print(user.get("phone")) # None(不存在返回 None)
# 添加/修改
user["phone"] = "123456"
user.update({"age": 31, "city": "Beijing"})
# 删除
del user["email"]
user.pop("phone", None) # 返回值,不存在返回默认值
# 检查键是否存在
if "name" in user:
print(user["name"])
# 遍历
for key in user:
print(f"{key}: {user[key]}")
for key, value in user.items():
print(f"{key}: {value}")
# 字典方法
keys = user.keys() # dict_keys(['name', 'age', 'city'])
values = user.values() # dict_values(['Alice', 31, 'Beijing'])
items = user.items() # dict_items([('name', 'Alice'), ...])
3.2 Go Map
// 创建 Map
// 方式 1:字面量
user := map[string]interface{}{
"name": "Alice",
"age": 30,
"email": "[email protected]",
}
// 方式 2:make(推荐)
user := make(map[string]string)
user["name"] = "Alice"
user["age"] = "30"
user["email"] = "[email protected]"
// 类型安全版本(推荐)⭐
type User struct {
Name string
Age int
Email string
}
users := make(map[string]User)
users["alice"] = User{Name: "Alice", Age: 30, Email: "[email protected]"}
// 访问
name := user["name"] // 如果键不存在,返回零值
// 检查键是否存在(惯用法)⭐
value, ok := user["name"]
if ok {
fmt.Println(value)
} else {
fmt.Println("键不存在")
}
// 添加/修改
user["phone"] = "123456"
user["age"] = "31"
// 删除
delete(user, "email")
// 遍历(顺序不保证!)
for key := range user {
fmt.Printf("%s: %s\n", key, user[key])
}
for key, value := range user {
fmt.Printf("%s: %s\n", key, value)
}
// 长度
fmt.Println(len(user))
3.3 Map 的陷阱
1. 并发不安全⚠️
// 多个 Goroutine 同时读写 Map 会导致 panic
m := make(map[string]int)
// ❌ 并发写入会 panic
go func() { m["a"] = 1 }()
go func() { m["b"] = 2 }()
// ✅ 使用 sync.Map(并发安全)
import "sync"
var m sync.Map
m.Store("a", 1)
m.Store("b", 2)
value, ok := m.Load("a")
// ✅ 或使用互斥锁
var mu sync.Mutex
var m = make(map[string]int)
mu.Lock()
m["a"] = 1
mu.Unlock()
2. 遍历顺序不确定⚠️
m := map[string]int{"c": 3, "a": 1, "b": 2}
for k, v := range m {
fmt.Printf("%s: %d\n", k, v)
}
// 输出顺序每次可能不同!
// 如果需要有序遍历,需要先排序键
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf("%s: %d\n", k, m[k])
}
3.4 对比总结
特性 | Python dict | Go Map |
---|---|---|
创建 | {key: value} | make(map[K]V) |
访问 | dict[key] | value, ok := map[key] |
存在检查 | key in dict | _, ok := map[key] |
删除 | del dict[key] | delete(map, key) |
长度 | len(dict) | len(map) |
遍历顺序 | 3.7+ 有序 | ❌ 无序 |
并发安全 | ❌ 不安全 | ❌ 不安全(需 sync.Map) |
默认值 | KeyError | 零值 |
4. 集合(Set)实现
4.1 Python Set
# 创建集合
fruits = {"apple", "banana", "orange"}
numbers = set([1, 2, 3, 3, 3]) # {1, 2, 3} 自动去重
# 添加
fruits.add("grape")
# 删除
fruits.remove("banana") # 不存在会报错
fruits.discard("kiwi") # 不存在不报错
# 检查成员
if "apple" in fruits:
print("Apple exists")
# 集合操作
a = {1, 2, 3}
b = {3, 4, 5}
union = a | b # {1, 2, 3, 4, 5} 并集
intersection = a & b # {3} 交集
difference = a - b # {1, 2} 差集
symmetric = a ^ b # {1, 2, 4, 5} 对称差
# 长度
len(fruits)
4.2 Go 实现 Set(使用 Map)
// Go 没有内置 Set,使用 map[T]bool 模拟
// 创建 Set
fruits := make(map[string]bool)
fruits["apple"] = true
fruits["banana"] = true
fruits["orange"] = true
// 或使用 struct{} 节省内存
fruitsSet := make(map[string]struct{})
fruitsSet["apple"] = struct{}{}
// 添加
fruits["grape"] = true
// 删除
delete(fruits, "banana")
// 检查成员
if fruits["apple"] {
fmt.Println("Apple exists")
}
// 更好的检查方式
if _, exists := fruits["apple"]; exists {
fmt.Println("Apple exists")
}
// 遍历
for fruit := range fruits {
fmt.Println(fruit)
}
// 长度
len(fruits)
4.3 Set 工具函数
// 封装 Set 操作
type StringSet map[string]struct{}
func NewStringSet() StringSet {
return make(StringSet)
}
func (s StringSet) Add(value string) {
s[value] = struct{}{}
}
func (s StringSet) Remove(value string) {
delete(s, value)
}
func (s StringSet) Contains(value string) bool {
_, exists := s[value]
return exists
}
func (s StringSet) Size() int {
return len(s)
}
// 并集
func (s StringSet) Union(other StringSet) StringSet {
result := NewStringSet()
for k := range s {
result.Add(k)
}
for k := range other {
result.Add(k)
}
return result
}
// 交集
func (s StringSet) Intersection(other StringSet) StringSet {
result := NewStringSet()
for k := range s {
if other.Contains(k) {
result.Add(k)
}
}
return result
}
// 差集
func (s StringSet) Difference(other StringSet) StringSet {
result := NewStringSet()
for k := range s {
if !other.Contains(k) {
result.Add(k)
}
}
return result
}
// 使用
fruits := NewStringSet()
fruits.Add("apple")
fruits.Add("banana")
if fruits.Contains("apple") {
fmt.Println("Found apple")
}
5. 实战案例
5.1 案例:单词统计
Python:
from collections import Counter
text = "hello world hello python world"
words = text.split()
# 方法 1:手动统计
word_count = {}
for word in words:
word_count[word] = word_count.get(word, 0) + 1
# 方法 2:使用 Counter
word_count = Counter(words)
# 获取最常见的词
most_common = word_count.most_common(2) # [('hello', 2), ('world', 2)]
print(word_count) # Counter({'hello': 2, 'world': 2, 'python': 1})
Go:
import (
"sort"
"strings"
)
text := "hello world hello go world"
words := strings.Fields(text)
// 统计词频
wordCount := make(map[string]int)
for _, word := range words {
wordCount[word]++
}
// 获取最常见的词(需要排序)
type WordFreq struct {
Word string
Count int
}
freqs := make([]WordFreq, 0, len(wordCount))
for word, count := range wordCount {
freqs = append(freqs, WordFreq{word, count})
}
// 按频率排序
sort.Slice(freqs, func(i, j int) bool {
return freqs[i].Count > freqs[j].Count
})
// 获取前 2 个
mostCommon := freqs[:2]
fmt.Println(mostCommon) // [{hello 2} {world 2}]
5.2 案例:去重并排序
Python:
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
# 去重
unique = list(set(numbers))
# 排序
unique.sort()
print(unique) # [1, 2, 3, 4, 5, 6, 9]
# 一行搞定
unique_sorted = sorted(set(numbers))
Go:
import "sort"
numbers := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}
// 去重(使用 map)
uniqueMap := make(map[int]bool)
for _, num := range numbers {
uniqueMap[num] = true
}
// 转回切片
unique := make([]int, 0, len(uniqueMap))
for num := range uniqueMap {
unique = append(unique, num)
}
// 排序
sort.Ints(unique)
fmt.Println(unique) // [1 2 3 4 5 6 9]
// 封装成函数
func uniqueAndSort(nums []int) []int {
seen := make(map[int]bool)
result := []int{}
for _, num := range nums {
if !seen[num] {
seen[num] = true
result = append(result, num)
}
}
sort.Ints(result)
return result
}
5.3 案例:分组统计
Python:
from collections import defaultdict
students = [
{"name": "Alice", "grade": "A"},
{"name": "Bob", "grade": "B"},
{"name": "Charlie", "grade": "A"},
{"name": "David", "grade": "C"},
{"name": "Eve", "grade": "B"},
]
# 按成绩分组
groups = defaultdict(list)
for student in students:
groups[student["grade"]].append(student["name"])
print(dict(groups))
# {'A': ['Alice', 'Charlie'], 'B': ['Bob', 'Eve'], 'C': ['David']}
Go:
type Student struct {
Name string
Grade string
}
students := []Student{
{"Alice", "A"},
{"Bob", "B"},
{"Charlie", "A"},
{"David", "C"},
{"Eve", "B"},
}
// 按成绩分组
groups := make(map[string][]string)
for _, student := range students {
groups[student.Grade] = append(groups[student.Grade], student.Name)
}
fmt.Println(groups)
// map[A:[Alice Charlie] B:[Bob Eve] C:[David]]
总结
Python → Go 数据结构迁移清单
Python | Go | 备注 |
---|---|---|
list | []T (slice) | 动态数组 |
tuple | [N]T (array) | 固定长度 |
dict | map[K]V | 键值对 |
set | map[T]bool | 需模拟 |
list.append() | append(slice, item) | 注意赋值 |
len(list) | len(slice) | 相同 |
key in dict | _, ok := map[key] | 检查存在 |
核心要点
- 切片(Slice):Go 最常用的数据结构,类似 Python 列表
- append 必须赋值:
slice = append(slice, item)
- Map 检查存在:使用
value, ok := map[key]
- Set 用 Map 模拟:
map[T]struct{}
或map[T]bool
- 遍历顺序不保证:Map 遍历顺序随机
下一篇预告
第 4 篇:面向对象 vs 组合模式
我们将深入对比:
- 类(Class) vs 结构体(Struct)
- 继承 vs 组合
- 方法与接收者
- 接口(Interface)的强大之处
🎯 动手实践:
- 实现一个通用的 Set 工具库
- 将 Python 的列表推导式改写为 Go
- 练习 Slice 的切片操作
下一篇见!🚀