# Golang 数据结构:掌握 Go 语言的核心数据结构
## 数组(Array)
### 数组的定义
数组是一种固定长度的、相同类型元素的集合。
“`go
// 声明数组
var arr [5]int
// 初始化数组
arr = [5]int{1, 2, 3, 4, 5}
// 自动推断长度
arr = […]int{1, 2, 3, 4, 5}
// 部分初始化
arr = [5]int{1, 2}
“`
### 数组的操作
“`go
// 访问元素
fmt.Println(arr[0]) // 输出:1
// 修改元素
arr[0] = 10
// 遍历数组
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
// 使用 range 遍历
for index, value := range arr {
fmt.Println(index, value)
}
```
### 数组的特性
- 数组的长度是固定的,一旦声明就不能改变
- 数组是值类型,赋值或传递给函数时会复制整个数组
- 数组的长度是其类型的一部分,`[5]int` 和 `[10]int` 是不同的类型
## 切片(Slice)
### 切片的定义
切片是对数组的引用,是一种可变长度的数据结构。
```go
// 声明切片
var slice []int
// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice = arr[1:4] // 包含索引 1,不包含索引 4
// 使用 make 创建切片
slice = make([]int, 5) // 长度为 5,容量为 5
slice = make([]int, 5, 10) // 长度为 5,容量为 10
// 直接初始化切片
slice = []int{1, 2, 3, 4, 5}
```
### 切片的操作
#### 添加元素
```go
// 添加单个元素
slice = append(slice, 6)
// 添加多个元素
slice = append(slice, 7, 8, 9)
// 添加另一个切片
slice = append(slice, []int{10, 11, 12}...)
```
#### 复制切片
```go
// 复制切片
src := []int{1, 2, 3}
dest := make([]int, len(src))
copy(dest, src)
```
#### 切片的长度和容量
```go
// 获取长度
length := len(slice)
// 获取容量
capacity := cap(slice)
```
### 切片的特性
- 切片是引用类型,指向底层数组
- 切片的长度可以动态改变
- 切片的容量是指从切片的第一个元素到底层数组末尾的元素个数
- 当切片容量不足时,`append` 操作会创建一个新的底层数组
## 映射(Map)
### 映射的定义
映射是一种键值对的集合,类似于其他语言中的字典或哈希表。
```go
// 声明映射
var m map[string]int
// 使用 make 创建映射
m = make(map[string]int)
// 直接初始化映射
m = map[string]int{
"apple": 1,
"banana": 2,
"cherry": 3,
}
```
### 映射的操作
#### 添加或修改元素
```go
m["apple"] = 5 // 如果键存在则修改,不存在则添加
```
#### 获取元素
```go
// 获取元素
value, ok := m["apple"]
if ok {
fmt.Println("Value:", value)
} else {
fmt.Println("Key not found")
}
// 直接获取(如果键不存在,返回零值)
value := m["orange"]
```
#### 删除元素
```go
delete(m, "apple")
```
#### 遍历映射
```go
for key, value := range m {
fmt.Println(key, value)
}
```
### 映射的特性
- 映射是引用类型
- 映射的键必须是可比较的类型(如字符串、数字、布尔值等)
- 映射的值可以是任意类型
- 映射是无序的,遍历顺序不固定
## 结构体(Struct)
### 结构体的定义
结构体是一种复合数据类型,用于组合多个不同类型的字段。
```go
type Person struct {
Name string
Age int
Address string
}
// 嵌套结构体
type Address struct {
Street string
City string
Country string
}
type Person struct {
Name string
Age int
Address Address
}
```
### 结构体的使用
```go
// 创建结构体实例
p := Person{
Name: "John",
Age: 30,
Address: "New York",
}
// 访问结构体字段
fmt.Println(p.Name)
fmt.Println(p.Age)
// 修改结构体字段
p.Age = 31
// 结构体作为参数
func printPerson(p Person) {
fmt.Println(p.Name, p.Age)
}
// 结构体指针作为参数
func updateAge(p *Person, age int) {
p.Age = age
}
```
### 结构体方法
```go
// 值接收者方法
func (p Person) GetName() string {
return p.Name
}
// 指针接收者方法
func (p *Person) SetAge(age int) {
p.Age = age
}
```
## 接口(Interface)
### 接口的定义
接口是一种抽象类型,定义了一组方法签名,任何类型只要实现了这些方法,就被认为实现了该接口。
```go
type Animal interface {
Speak() string
Move() string
}
```
### 接口的实现
```go
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return "Woof!"
}
func (d Dog) Move() string {
return "Run"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return "Meow!"
}
func (c Cat) Move() string {
return "Walk"
}
```
### 接口的使用
```go
func MakeAnimalSpeak(a Animal) {
fmt.Println(a.Speak())
}
func MakeAnimalMove(a Animal) {
fmt.Println(a.Move())
}
// 使用接口
dog := Dog{Name: "Buddy"}
cat := Cat{Name: "Whiskers"}
MakeAnimalSpeak(dog) // 输出: Woof!
MakeAnimalSpeak(cat) // 输出: Meow!
MakeAnimalMove(dog) // 输出: Run
MakeAnimalMove(cat) // 输出: Walk
```
### 接口的特性
- 接口是隐式实现的,不需要显式声明
- 一个类型可以实现多个接口
- 接口可以嵌套
- 空接口 `interface{}` 可以接受任何类型的值
## 通道(Channel)
### 通道的定义
通道是用于在 goroutine 之间传递数据的管道。
```go
// 声明通道
var ch chan int
// 使用 make 创建通道
ch = make(chan int)
// 创建带缓冲区的通道
ch = make(chan int, 10)
```
### 通道的操作
#### 发送数据
```go
ch <- 10 // 发送 10 到通道
```
#### 接收数据
```go
// 接收数据
value := <-ch
// 非阻塞接收
value, ok := <-ch
if ok {
fmt.Println("Received:", value)
} else {
fmt.Println("Channel closed")
}
```
#### 关闭通道
```go
close(ch)
```
#### 遍历通道
```go
// 遍历通道中的所有数据
for value := range ch {
fmt.Println(value)
}
```
### 通道的特性
- 通道是引用类型
- 通道默认是无缓冲的,发送和接收操作会阻塞
- 带缓冲的通道在缓冲区满时发送会阻塞,在缓冲区空时接收会阻塞
- 关闭通道后,不能再发送数据,但可以继续接收数据
## 总结
本文介绍了 Go 语言中的核心数据结构,包括数组、切片、映射、结构体、接口和通道。这些数据结构是 Go 语言的基础,掌握它们对于编写高效、清晰的 Go 代码至关重要。在实际开发中,我们需要根据具体场景选择合适的数据结构,以达到最佳的性能和代码可读性。