复合数据类型
2026/3/20大约 14 分钟
复合数据类型
一、数组
1.1 数组基础
数组是固定长度、同类型元素的序列。在 Go 中,数组长度是类型的一部分。
package main
import "fmt"
func main() {
// ========== 数组声明 ==========
// 方式一:指定长度和类型
var arr1 [5]int // [0, 0, 0, 0, 0]
// 方式二:声明并初始化
var arr2 = [5]int{1, 2, 3, 4, 5}
// 方式三:短声明
arr3 := [5]int{1, 2, 3} // [1, 2, 3, 0, 0]
// 方式四:自动推断长度
arr4 := [...]int{1, 2, 3, 4, 5} // 长度为 5
// 方式五:指定索引初始化
arr5 := [5]string{1: "one", 3: "three"} // ["", "one", "", "three", ""]
fmt.Println(arr1, arr2, arr3, arr4, arr5)
// ========== 数组操作 ==========
// 访问元素
fmt.Println("arr2[0] =", arr2[0])
// 修改元素
arr2[0] = 100
fmt.Println("修改后:", arr2)
// 获取长度
fmt.Println("长度:", len(arr2))
// ========== 数组遍历 ==========
servers := [3]string{"web-01", "web-02", "web-03"}
// for 循环
for i := 0; i < len(servers); i++ {
fmt.Printf("servers[%d] = %s\n", i, servers[i])
}
// range 遍历
for index, value := range servers {
fmt.Printf("[%d] %s\n", index, value)
}
// ========== 多维数组 ==========
matrix := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
fmt.Println("矩阵:")
for _, row := range matrix {
fmt.Println(row)
}
// ========== 数组是值类型 ==========
original := [3]int{1, 2, 3}
copied := original // 完整复制
copied[0] = 100
fmt.Println("original:", original) // [1, 2, 3](未变)
fmt.Println("copied:", copied) // [100, 2, 3]
}
1.2 数组与内存
package main
import (
"fmt"
"unsafe"
)
func main() {
// 数组内存连续分布
arr := [5]int{10, 20, 30, 40, 50}
fmt.Printf("数组地址: %p\n", &arr)
fmt.Printf("元素大小: %d bytes\n", unsafe.Sizeof(arr[0]))
fmt.Printf("数组大小: %d bytes\n", unsafe.Sizeof(arr))
// 验证连续内存
for i := 0; i < len(arr); i++ {
fmt.Printf("arr[%d] 地址: %p, 值: %d\n", i, &arr[i], arr[i])
}
}
1.3 数组比较
package main
import "fmt"
func main() {
// 相同类型的数组可以比较
arr1 := [3]int{1, 2, 3}
arr2 := [3]int{1, 2, 3}
arr3 := [3]int{1, 2, 4}
fmt.Println("arr1 == arr2:", arr1 == arr2) // true
fmt.Println("arr1 == arr3:", arr1 == arr3) // false
// 不同长度的数组是不同类型,不能比较
// var a [3]int
// var b [4]int
// fmt.Println(a == b) // 编译错误
// 包含不可比较类型的数组也不能比较
// var sliceArr1 [2][]int
// var sliceArr2 [2][]int
// fmt.Println(sliceArr1 == sliceArr2) // 编译错误
}
二、切片(Slice)
2.1 切片基础
切片是动态数组,是 Go 中最常用的数据结构之一:
package main
import "fmt"
func main() {
// ========== 切片创建 ==========
// 方式一:字面量
s1 := []int{1, 2, 3, 4, 5}
// 方式二:make 创建(指定长度和容量)
s2 := make([]int, 5) // len=5, cap=5
s3 := make([]int, 3, 10) // len=3, cap=10
// 方式三:从数组切片
arr := [5]int{10, 20, 30, 40, 50}
s4 := arr[1:4] // [20, 30, 40]
// 方式四:nil 切片
var s5 []int // nil,但可以直接 append
fmt.Println(s1, s2, s3, s4, s5)
fmt.Printf("s3: len=%d, cap=%d\n", len(s3), cap(s3))
fmt.Printf("s5 == nil: %v\n", s5 == nil)
// ========== 切片操作 ==========
// 访问和修改
s1[0] = 100
fmt.Println("修改后 s1:", s1)
// 追加元素
s5 = append(s5, 1, 2, 3)
fmt.Println("append 后 s5:", s5)
// 追加另一个切片
s5 = append(s5, s1...)
fmt.Println("追加 s1 后:", s5)
// ========== 切片表达式 ==========
data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("data[2:5]:", data[2:5]) // [2, 3, 4]
fmt.Println("data[:3]:", data[:3]) // [0, 1, 2]
fmt.Println("data[7:]:", data[7:]) // [7, 8, 9]
fmt.Println("data[:]:", data[:]) // 完整切片
// 完整切片表达式(指定容量)
s := data[2:5:7] // [2:5] 但容量限制为 7-2=5
fmt.Printf("s: %v, len=%d, cap=%d\n", s, len(s), cap(s))
}
2.2 切片内部结构
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
// 切片内部结构:
// type slice struct {
// array unsafe.Pointer // 指向底层数组的指针
// len int // 长度
// cap int // 容量
// }
s := make([]int, 3, 5)
s[0], s[1], s[2] = 10, 20, 30
// 使用 reflect 查看切片头部
header := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("Data: %x\n", header.Data)
fmt.Printf("Len: %d\n", header.Len)
fmt.Printf("Cap: %d\n", header.Cap)
// 切片共享底层数组
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[1:4]
s2 := arr[2:5]
fmt.Println("修改前:")
fmt.Println("arr:", arr)
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
s1[1] = 100 // 修改 s1[1] 也就是 arr[2]
fmt.Println("修改 s1[1]=100 后:")
fmt.Println("arr:", arr) // [1, 2, 100, 4, 5]
fmt.Println("s1:", s1) // [2, 100, 4]
fmt.Println("s2:", s2) // [100, 4, 5]
}
2.3 切片扩容机制
package main
import "fmt"
func main() {
s := make([]int, 0)
fmt.Println("扩容过程:")
prevCap := cap(s)
for i := 0; i < 20; i++ {
s = append(s, i)
if cap(s) != prevCap {
fmt.Printf("len=%2d, cap=%2d -> %2d (扩容)\n",
len(s), prevCap, cap(s))
prevCap = cap(s)
}
}
// Go 1.18+ 扩容策略:
// - 如果新容量 > 2*旧容量,使用新容量
// - 如果旧容量 < 256,新容量 = 2*旧容量
// - 否则,新容量 = 旧容量 + (旧容量 + 3*256) / 4
// 最后会进行内存对齐调整
// 预分配容量避免频繁扩容
servers := make([]string, 0, 100) // 预分配 100 容量
for i := 0; i < 100; i++ {
servers = append(servers, fmt.Sprintf("server-%d", i))
}
fmt.Printf("servers: len=%d, cap=%d\n", len(servers), cap(servers))
}
2.4 切片操作实战
package main
import (
"fmt"
"slices" // Go 1.21+
"sort"
)
func main() {
// ========== 复制切片 ==========
src := []int{1, 2, 3, 4, 5}
// 方式一:copy 函数
dst1 := make([]int, len(src))
copy(dst1, src)
// 方式二:append(推荐)
dst2 := append([]int(nil), src...)
// 方式三:slices.Clone(Go 1.21+)
dst3 := slices.Clone(src)
src[0] = 100
fmt.Println("src:", src) // [100, 2, 3, 4, 5]
fmt.Println("dst1:", dst1) // [1, 2, 3, 4, 5]
fmt.Println("dst2:", dst2) // [1, 2, 3, 4, 5]
fmt.Println("dst3:", dst3) // [1, 2, 3, 4, 5]
// ========== 删除元素 ==========
s := []int{0, 1, 2, 3, 4, 5}
// 删除索引 2 的元素
i := 2
s = append(s[:i], s[i+1:]...)
fmt.Println("删除索引2后:", s) // [0, 1, 3, 4, 5]
// Go 1.21+ 使用 slices.Delete
s2 := []int{0, 1, 2, 3, 4, 5}
s2 = slices.Delete(s2, 2, 3) // 删除 [2, 3) 范围
fmt.Println("slices.Delete:", s2)
// ========== 插入元素 ==========
s3 := []int{1, 2, 5, 6}
// 在索引 2 处插入 3, 4
s3 = slices.Insert(s3, 2, 3, 4)
fmt.Println("插入后:", s3) // [1, 2, 3, 4, 5, 6]
// ========== 排序 ==========
nums := []int{5, 2, 8, 1, 9, 3}
sort.Ints(nums)
fmt.Println("排序后:", nums)
// 自定义排序
servers := []struct {
Name string
Load int
}{
{"web-01", 50},
{"web-03", 30},
{"web-02", 80},
}
sort.Slice(servers, func(i, j int) bool {
return servers[i].Load < servers[j].Load
})
fmt.Println("按负载排序:", servers)
// Go 1.21+ slices.SortFunc
slices.SortFunc(servers, func(a, b struct{ Name string; Load int }) int {
return a.Load - b.Load
})
// ========== 查找 ==========
data := []int{1, 3, 5, 7, 9, 11}
// 二分查找(切片必须有序)
idx, found := slices.BinarySearch(data, 7)
fmt.Printf("查找 7: index=%d, found=%v\n", idx, found)
// 线性查找
idx = slices.Index(data, 5)
fmt.Printf("查找 5: index=%d\n", idx)
// ========== 去重 ==========
withDups := []int{1, 2, 2, 3, 3, 3, 4}
slices.Sort(withDups)
unique := slices.Compact(withDups)
fmt.Println("去重后:", unique)
// ========== 反转 ==========
toReverse := []int{1, 2, 3, 4, 5}
slices.Reverse(toReverse)
fmt.Println("反转后:", toReverse)
}
2.5 切片陷阱
package main
import "fmt"
func main() {
// ========== 陷阱1:切片共享底层数组 ==========
original := []int{1, 2, 3, 4, 5}
sub := original[1:3]
sub[0] = 100
fmt.Println("original:", original) // [1, 100, 3, 4, 5]
// 解决:复制切片
// ========== 陷阱2:append 可能返回新切片 ==========
s1 := make([]int, 3, 3) // len=cap=3
s1[0], s1[1], s1[2] = 1, 2, 3
s2 := s1
s2 = append(s2, 4) // 触发扩容,s2 指向新数组
s1[0] = 100
fmt.Println("s1:", s1) // [100, 2, 3]
fmt.Println("s2:", s2) // [1, 2, 3, 4](不受影响)
// ========== 陷阱3:截取切片可能导致内存泄漏 ==========
// 大切片截取小部分,底层数组无法被 GC
bigData := make([]byte, 1<<20) // 1MB
// 错误:小切片引用大数组
// smallPart := bigData[:100]
// 正确:复制需要的部分
smallPart := make([]byte, 100)
copy(smallPart, bigData[:100])
_ = smallPart
// ========== 陷阱4:nil 切片 vs 空切片 ==========
var nilSlice []int // nil
emptySlice := []int{} // 非 nil,但长度为 0
makeSlice := make([]int, 0) // 非 nil,但长度为 0
fmt.Printf("nil: %v, %v\n", nilSlice == nil, len(nilSlice))
fmt.Printf("empty: %v, %v\n", emptySlice == nil, len(emptySlice))
fmt.Printf("make: %v, %v\n", makeSlice == nil, len(makeSlice))
// 都可以正常 append
nilSlice = append(nilSlice, 1)
emptySlice = append(emptySlice, 1)
fmt.Println(nilSlice, emptySlice)
}
三、映射(Map)
3.1 Map 基础
package main
import "fmt"
func main() {
// ========== Map 创建 ==========
// 方式一:make
m1 := make(map[string]int)
// 方式二:字面量
m2 := map[string]int{
"cpu": 80,
"memory": 60,
"disk": 45,
}
// 方式三:空 map
m3 := map[string]int{}
// nil map(只读,写入会 panic)
var m4 map[string]int
fmt.Println("m4 == nil:", m4 == nil)
_ = m1; _ = m3
// ========== Map 操作 ==========
// 添加/修改
m2["network"] = 30
// 读取
cpu := m2["cpu"]
fmt.Println("cpu:", cpu)
// 读取不存在的键返回零值
unknown := m2["unknown"]
fmt.Println("unknown:", unknown) // 0
// 判断键是否存在
value, exists := m2["memory"]
if exists {
fmt.Println("memory:", value)
}
// 删除
delete(m2, "disk")
fmt.Println("删除 disk 后:", m2)
// 获取长度
fmt.Println("长度:", len(m2))
// ========== Map 遍历 ==========
config := map[string]string{
"host": "localhost",
"port": "8080",
"protocol": "https",
}
// 遍历键值对
for key, value := range config {
fmt.Printf("%s = %s\n", key, value)
}
// 只遍历键
for key := range config {
fmt.Println("key:", key)
}
// 注意:map 遍历顺序是随机的!
}
3.2 Map 高级用法
package main
import (
"fmt"
"maps" // Go 1.21+
"sort"
"sync"
)
func main() {
// ========== 有序遍历 ==========
scores := map[string]int{
"alice": 95,
"bob": 87,
"carol": 92,
}
// 提取键并排序
keys := make([]string, 0, len(scores))
for k := range scores {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Println("按名字排序:")
for _, k := range keys {
fmt.Printf("%s: %d\n", k, scores[k])
}
// ========== Map 作为集合(Set)==========
visited := make(map[string]bool)
// 或使用空结构体节省内存
visitedEfficient := make(map[string]struct{})
visited["web-01"] = true
visited["web-02"] = true
visitedEfficient["web-01"] = struct{}{}
visitedEfficient["web-02"] = struct{}{}
// 检查是否存在
if visited["web-01"] {
fmt.Println("web-01 已访问")
}
if _, ok := visitedEfficient["web-03"]; !ok {
fmt.Println("web-03 未访问")
}
// ========== 嵌套 Map ==========
servers := map[string]map[string]int{
"web-01": {
"cpu": 80,
"memory": 60,
},
"web-02": {
"cpu": 45,
"memory": 70,
},
}
// 访问嵌套值
if web01, ok := servers["web-01"]; ok {
fmt.Println("web-01 cpu:", web01["cpu"])
}
// 添加新服务器(需要初始化内层 map)
servers["web-03"] = make(map[string]int)
servers["web-03"]["cpu"] = 30
// ========== Map 复制(Go 1.21+)==========
original := map[string]int{"a": 1, "b": 2}
copied := maps.Clone(original)
copied["a"] = 100
fmt.Println("original:", original) // {a:1, b:2}
fmt.Println("copied:", copied) // {a:100, b:2}
// ========== Map 比较(Go 1.21+)==========
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1, "b": 2}
m3 := map[string]int{"a": 1, "b": 3}
fmt.Println("m1 equals m2:", maps.Equal(m1, m2)) // true
fmt.Println("m1 equals m3:", maps.Equal(m1, m3)) // false
}
// ========== 并发安全的 Map ==========
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
func (sm *SafeMap) Get(key string) (int, bool) {
sm.mu.RLock()
defer sm.mu.RUnlock()
val, ok := sm.m[key]
return val, ok
}
func (sm *SafeMap) Set(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}
3.3 sync.Map
package main
import (
"fmt"
"sync"
)
func main() {
// sync.Map 适用于:
// 1. 键值对只写入一次但读取多次
// 2. 多个 goroutine 读写不相交的键集合
var sm sync.Map
// 存储
sm.Store("cpu", 80)
sm.Store("memory", 60)
sm.Store("disk", 45)
// 读取
if val, ok := sm.Load("cpu"); ok {
fmt.Println("cpu:", val)
}
// 读取或存储(不存在则存储)
actual, loaded := sm.LoadOrStore("network", 30)
fmt.Printf("network: %v, 已存在: %v\n", actual, loaded)
// 删除
sm.Delete("disk")
// 遍历
sm.Range(func(key, value interface{}) bool {
fmt.Printf("%v: %v\n", key, value)
return true // 返回 false 停止遍历
})
// LoadAndDelete(Go 1.15+)
if val, loaded := sm.LoadAndDelete("cpu"); loaded {
fmt.Println("删除的 cpu:", val)
}
// Swap(Go 1.20+)
// old, loaded := sm.Swap("memory", 70)
// CompareAndSwap / CompareAndDelete(Go 1.20+)
// sm.CompareAndSwap("memory", 60, 70)
}
3.4 运维场景实战
package main
import (
"fmt"
"strings"
"time"
)
// 服务器监控数据
type MetricData struct {
Value float64
Timestamp time.Time
}
// 监控数据存储
type MetricsStore struct {
// server -> metric -> data
data map[string]map[string]MetricData
}
func NewMetricsStore() *MetricsStore {
return &MetricsStore{
data: make(map[string]map[string]MetricData),
}
}
func (ms *MetricsStore) Record(server, metric string, value float64) {
if ms.data[server] == nil {
ms.data[server] = make(map[string]MetricData)
}
ms.data[server][metric] = MetricData{
Value: value,
Timestamp: time.Now(),
}
}
func (ms *MetricsStore) Get(server, metric string) (MetricData, bool) {
if metrics, ok := ms.data[server]; ok {
if data, ok := metrics[metric]; ok {
return data, true
}
}
return MetricData{}, false
}
func (ms *MetricsStore) GetServerMetrics(server string) map[string]MetricData {
return ms.data[server]
}
func (ms *MetricsStore) Report() string {
var sb strings.Builder
sb.WriteString("=== 监控报告 ===\n")
for server, metrics := range ms.data {
sb.WriteString(fmt.Sprintf("\n[%s]\n", server))
for metric, data := range metrics {
sb.WriteString(fmt.Sprintf(" %s: %.2f (at %s)\n",
metric, data.Value, data.Timestamp.Format("15:04:05")))
}
}
return sb.String()
}
// 配置解析器
func parseConfig(configStr string) map[string]string {
config := make(map[string]string)
lines := strings.Split(configStr, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
config[key] = value
}
}
return config
}
func main() {
// 监控数据存储
store := NewMetricsStore()
store.Record("web-01", "cpu", 75.5)
store.Record("web-01", "memory", 60.2)
store.Record("web-02", "cpu", 45.8)
store.Record("web-02", "memory", 80.1)
fmt.Println(store.Report())
// 配置解析
configStr := `
# Server Configuration
host = 192.168.1.100
port = 8080
debug = true
max_connections = 1000
`
config := parseConfig(configStr)
fmt.Println("解析的配置:")
for k, v := range config {
fmt.Printf(" %s = %s\n", k, v)
}
}
四、结构体
4.1 结构体基础
package main
import (
"encoding/json"
"fmt"
"time"
)
// 结构体定义
type Server struct {
Name string
IP string
Port int
IsRunning bool
StartTime time.Time
}
// 嵌套结构体
type Cluster struct {
Name string
Region string
Servers []Server
}
// 匿名字段(嵌入)
type MonitoredServer struct {
Server // 嵌入 Server
CPUUsage float64
MemoryUsage float64
}
func main() {
// ========== 创建结构体 ==========
// 方式一:字段名初始化(推荐)
s1 := Server{
Name: "web-01",
IP: "192.168.1.10",
Port: 8080,
IsRunning: true,
StartTime: time.Now(),
}
// 方式二:按顺序初始化(不推荐,易错)
s2 := Server{"web-02", "192.168.1.11", 8080, false, time.Time{}}
// 方式三:指针
s3 := &Server{
Name: "web-03",
IP: "192.168.1.12",
}
// 方式四:new(返回零值指针)
s4 := new(Server)
s4.Name = "web-04"
fmt.Printf("s1: %+v\n", s1)
fmt.Printf("s2: %+v\n", s2)
fmt.Printf("s3: %+v\n", s3)
fmt.Printf("s4: %+v\n", s4)
// ========== 访问和修改 ==========
fmt.Println("名称:", s1.Name)
s1.Port = 9090
fmt.Println("新端口:", s1.Port)
// 指针也使用点操作符(Go 自动解引用)
s3.Port = 8081
fmt.Println("s3 端口:", s3.Port)
// ========== 嵌套结构体 ==========
cluster := Cluster{
Name: "production",
Region: "us-east-1",
Servers: []Server{
{Name: "node-1", IP: "10.0.0.1", Port: 8080},
{Name: "node-2", IP: "10.0.0.2", Port: 8080},
},
}
fmt.Printf("集群: %+v\n", cluster)
// ========== 匿名字段(嵌入)==========
ms := MonitoredServer{
Server: Server{
Name: "monitored-01",
IP: "10.0.0.10",
Port: 8080,
},
CPUUsage: 75.5,
MemoryUsage: 60.2,
}
// 可以直接访问嵌入结构体的字段
fmt.Println("服务器名:", ms.Name) // 等同于 ms.Server.Name
fmt.Println("CPU 使用率:", ms.CPUUsage)
}
4.2 结构体标签
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// 带标签的结构体
type Config struct {
Host string `json:"host" yaml:"host" env:"APP_HOST" validate:"required"`
Port int `json:"port" yaml:"port" env:"APP_PORT" validate:"min=1,max=65535"`
Debug bool `json:"debug,omitempty" yaml:"debug"`
APIKey string `json:"api_key" yaml:"api_key" env:"API_KEY" validate:"required"`
MaxRetries int `json:"max_retries,omitempty" yaml:"max_retries" default:"3"`
internalKey string // 未导出字段,JSON 忽略
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Password string `json:"-"` // 忽略该字段
Email string `json:"email,omitempty"` // 空值时省略
}
func main() {
// JSON 序列化
config := Config{
Host: "localhost",
Port: 8080,
Debug: false,
APIKey: "secret123",
MaxRetries: 3,
}
jsonData, _ := json.MarshalIndent(config, "", " ")
fmt.Println("JSON 输出:")
fmt.Println(string(jsonData))
// JSON 反序列化
jsonStr := `{"host":"10.0.0.1","port":9090,"debug":true,"api_key":"newkey"}`
var newConfig Config
json.Unmarshal([]byte(jsonStr), &newConfig)
fmt.Printf("解析后: %+v\n", newConfig)
// 用户序列化(密码被忽略)
user := User{
ID: 1,
Name: "admin",
Password: "secret",
Email: "",
}
userData, _ := json.Marshal(user)
fmt.Printf("用户 JSON: %s\n", userData)
// 读取结构体标签
t := reflect.TypeOf(Config{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("字段: %-12s JSON: %-20s Env: %s\n",
field.Name,
field.Tag.Get("json"),
field.Tag.Get("env"))
}
}
4.3 结构体方法
package main
import (
"fmt"
"strings"
"time"
)
type Server struct {
Name string
IP string
Port int
Status string
StartTime time.Time
}
// 值接收者方法(不修改原结构体)
func (s Server) Address() string {
return fmt.Sprintf("%s:%d", s.IP, s.Port)
}
func (s Server) String() string {
return fmt.Sprintf("Server{Name: %s, Address: %s, Status: %s}",
s.Name, s.Address(), s.Status)
}
func (s Server) IsHealthy() bool {
return s.Status == "running"
}
// 指针接收者方法(可以修改结构体)
func (s *Server) Start() {
s.Status = "running"
s.StartTime = time.Now()
}
func (s *Server) Stop() {
s.Status = "stopped"
}
func (s *Server) Restart() {
s.Stop()
s.Start()
}
func (s *Server) SetPort(port int) error {
if port < 1 || port > 65535 {
return fmt.Errorf("invalid port: %d", port)
}
s.Port = port
return nil
}
// 运维场景:服务器组
type ServerGroup struct {
Name string
Servers []*Server
}
func NewServerGroup(name string) *ServerGroup {
return &ServerGroup{
Name: name,
Servers: make([]*Server, 0),
}
}
func (g *ServerGroup) Add(servers ...*Server) {
g.Servers = append(g.Servers, servers...)
}
func (g *ServerGroup) StartAll() {
for _, s := range g.Servers {
s.Start()
}
}
func (g *ServerGroup) StopAll() {
for _, s := range g.Servers {
s.Stop()
}
}
func (g *ServerGroup) HealthyCount() int {
count := 0
for _, s := range g.Servers {
if s.IsHealthy() {
count++
}
}
return count
}
func (g *ServerGroup) Report() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("=== %s ===\n", g.Name))
sb.WriteString(fmt.Sprintf("总数: %d, 健康: %d\n\n",
len(g.Servers), g.HealthyCount()))
for _, s := range g.Servers {
status := "[ ]"
if s.IsHealthy() {
status = "[✓]"
}
sb.WriteString(fmt.Sprintf("%s %s (%s)\n", status, s.Name, s.Address()))
}
return sb.String()
}
func main() {
// 创建服务器
web1 := &Server{Name: "web-01", IP: "10.0.0.1", Port: 8080}
web2 := &Server{Name: "web-02", IP: "10.0.0.2", Port: 8080}
web3 := &Server{Name: "web-03", IP: "10.0.0.3", Port: 8080}
// 创建服务器组
group := NewServerGroup("Web Servers")
group.Add(web1, web2, web3)
// 启动部分服务器
web1.Start()
web2.Start()
// 打印报告
fmt.Println(group.Report())
// 测试方法
fmt.Println("web1 地址:", web1.Address())
fmt.Println("web1 健康:", web1.IsHealthy())
fmt.Println("web3 健康:", web3.IsHealthy())
}
4.4 结构体比较与复制
package main
import "fmt"
// 可比较的结构体(所有字段都可比较)
type Point struct {
X, Y int
}
// 不可比较的结构体(包含切片)
type Path struct {
Points []Point
}
func main() {
// ========== 结构体比较 ==========
p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{1, 3}
fmt.Println("p1 == p2:", p1 == p2) // true
fmt.Println("p1 == p3:", p1 == p3) // false
// 不可比较类型会编译错误
// path1 := Path{[]Point{{1, 2}}}
// path2 := Path{[]Point{{1, 2}}}
// fmt.Println(path1 == path2) // 编译错误
// ========== 结构体复制 ==========
original := Point{10, 20}
copied := original // 值复制
copied.X = 100
fmt.Println("original:", original) // {10, 20}
fmt.Println("copied:", copied) // {100, 20}
// 包含引用类型的结构体是浅复制
type Config struct {
Name string
Hosts []string
}
c1 := Config{
Name: "app",
Hosts: []string{"host1", "host2"},
}
c2 := c1 // 浅复制
c2.Name = "app2"
c2.Hosts[0] = "modified"
fmt.Printf("c1: %+v\n", c1) // Hosts[0] 也被修改了
fmt.Printf("c2: %+v\n", c2)
// 深复制需要手动处理
c3 := Config{
Name: c1.Name,
Hosts: append([]string(nil), c1.Hosts...),
}
c3.Hosts[0] = "new"
fmt.Printf("c1 after deep copy: %+v\n", c1)
fmt.Printf("c3: %+v\n", c3)
}
五、本章小结
| 类型 | 特点 | 使用场景 |
|---|---|---|
| 数组 | 固定长度,值类型 | 少用,一般用切片代替 |
| 切片 | 动态长度,引用底层数组 | 最常用的集合类型 |
| Map | 键值对,无序,引用类型 | 配置、缓存、索引 |
| 结构体 | 自定义复合类型,值类型 | 数据建模、配置定义 |
运维开发建议
- 切片:预分配容量避免扩容,注意共享底层数组
- Map:使用前必须初始化,并发场景用
sync.Map - 结构体:善用标签进行 JSON/YAML 序列化
- 复制:注意浅复制和深复制的区别