./read "Python 到 Go:第 3 篇 - ..."

Python 到 Go:第 3 篇 - 数据结构与集合操作

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)
  2. 列表操作对比
  3. 字典 vs 映射(Map)
  4. 集合(Set)实现
  5. 实战案例

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 常用操作对比

操作PythonGo
长度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 dictGo 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 数据结构迁移清单

PythonGo备注
list[]T (slice)动态数组
tuple[N]T (array)固定长度
dictmap[K]V键值对
setmap[T]bool需模拟
list.append()append(slice, item)注意赋值
len(list)len(slice)相同
key in dict_, ok := map[key]检查存在

核心要点

  1. 切片(Slice):Go 最常用的数据结构,类似 Python 列表
  2. append 必须赋值slice = append(slice, item)
  3. Map 检查存在:使用 value, ok := map[key]
  4. Set 用 Map 模拟map[T]struct{}map[T]bool
  5. 遍历顺序不保证:Map 遍历顺序随机

下一篇预告

第 4 篇:面向对象 vs 组合模式

我们将深入对比:

  • 类(Class) vs 结构体(Struct)
  • 继承 vs 组合
  • 方法与接收者
  • 接口(Interface)的强大之处

🎯 动手实践

  1. 实现一个通用的 Set 工具库
  2. 将 Python 的列表推导式改写为 Go
  3. 练习 Slice 的切片操作

下一篇见!🚀

comments.logDiscussion Thread
./comments --show-all

讨论区

./loading comments...