Go 数据类型
Go 数据类型
Go 语言提供了丰富的数据类型,主要分为基本数据类型和复合数据类型两大类。
基本数据类型包括整数、浮点数、字符、字符串、布尔值等
复合数据类型包括数组、切片、映射(Map)等。
理解这些数据类型的特性和用法是学习 Go 语言的基础。
一、基本数据类型
Go 语言提供了多种基本数据类型,主要包括
- 整型
- 浮点型
- 字符型
- 字符串型
- 布尔型
1.1 整型
Go 语言提供了多种整数类型,分为有符号和无符号两大类:
int8、int16、int32、int64(有符号整数)uint8、uint16、uint32、uint64(无符号整数)int和uint的大小取决于平台(32 位系统通常为 32 位,64 位系统通常为 64 位)
| 类型 | 大小(位) | 范围(有符号) | 范围(无符号) |
|---|---|---|---|
int8 |
8 | -128 到 127 | 0 到 255 |
int16 |
16 | -32768 到 32767 | 0 到 65535 |
int32 |
32 | -2147483648 到 2147483647 | 0 到 4294967295 |
int64 |
64 | -9223372036854775808 到 9223372036854775807 | 0 到 18446744073709551615 |
int |
32 或 64 | 与平台相关,通常为 -2^31 到 2^31-1 或 -2^63 到 2^63-1 | 与平台相关,通常为 0 到 2^32-1 或 0 到 2^64-1 |
uint8 |
8 | N/A | 0 到 255 |
uint16 |
16 | N/A | 0 到 65535 |
uint32 |
32 | N/A | 0 到 4294967295 |
uint64 |
64 | N/A | 0 到 18446744073709551615 |
uint |
32 或 64 | N/A | 与平台相关,通常为 0 到 2^32-1 或 0 到 2^64-1 |
int和uint的大小取决于平台(32 位系统通常为 32 位,64 位系统通常为 64 位),因此在需要明确大小的场景下,建议使用int32、int64等具体类型。
1.2 浮点型
Go 语言提供了两种浮点类型:
float32float64(默认浮点类型)
| 类型 | 大小(位) | 精度(有效数字位数) | 范围(近似值) |
|---|---|---|---|
float32 |
32 | 6-9 | ±1.18e-38 到 ±3.4e+38 |
float64 |
64 | 15-17 | ±2.22e-308 到 ±1.8e+308 |
- 最大值和最小值可以通过
math包中的常量获取,如math.MaxFloat32、math.SmallestNonzeroFloat64等。
1.3 字符型
Go 语言没有专门的字符类型,但可以使用 rune(int32 的别名)来表示 Unicode 码点,或使用 byte(uint8 的别名)来表示 ASCII 字符。
| 类型 | 大小(位) | 描述 | 示例 |
|---|---|---|---|
byte |
8 | uint8 的别名,表示单字节字符 | 'a' |
rune |
32 | int32 的别名,表示 Unicode 码点 | '中' |
示例:
1 | package main |
1.4 字符串型
Go 语言中的字符串是不可变的 UTF-8 编码的字节序列,可以使用双引号 "" 来定义字符串。
1 | package main |
1.4.1 字符串长度
长度
len()返回的是字符串包含的字节数,而不是字符数1
2str := "Hello,世界"
fmt.Println(len(str)) // 输出 12(Hello, 占 6 字节,世界占 6 字节)
字符数
utf8.RuneCountInString()获取字符串中的字符数1
2
3
4import "unicode/utf8"
str := "Hello,世界"
fmt.Println(utf8.RuneCountInString(str)) // 输出 8(Hello, 占 6 字符,世界占 2 字符)len([]rune(str))也可以获取字符数,但会先将字符串转换为[]rune,效率较低,若后续本来就要按 rune 切片处理,可以一举两得1
2str := "Hello,世界"
fmt.Println(len([]rune(str))) // 输出 8
1.4.2 字符串遍历
按字节遍历
- 使用索引或普通
for循环遍历字符串,获取的是原始字符 - 中文等多字节字符会被拆分成多个字节,输出可能是乱码
1
2
3
4str := "Hello,世界"
for i := 0; i < len(str); i++ {
fmt.Printf("%c ", str[i]) // 输出 h e l l o , ä ¸ ç
}
按字符遍历
- 使用
for range循环遍历字符串,Go 会自动按 UTF-8 字符解码,每次迭代返回的是一个 rune 类型的字符和一个字符起始位置的索引1
2
3
4
5
6
7
8str := "Hello,世界"
for index, char := range str {
fmt.Printf("index: %d, char: %c\n", index, char)
}
// 输出:
// index: 0, char: H
// index: 1, char: e
// ...
1.4.3 字符串的修改
字符串是不可变的,不能直接修改其中的字符,但可以通过转换为 []rune 或 []byte 来修改后再转换回字符串。
[]byte适合修改 ASCII 字符,修改多字节字符可能会导致乱码[]rune适合修改 Unicode 字符,能正确处理多字节字符
1 | package main |
1.4.4 字符串的类型转换
数值转字符串
strconv.Itoa(i int):将整数转换为字符串strconv.FormatInt(i, base):将整型数转换为指定进制的字符串strconv.FormatFloat(f, fmt, prec, bitSize):将浮点数转换为字符串fmt参数指定格式(‘f’、‘e’、‘g’ 等)prec指定小数点后精度bitSize指定 float32 还是 float64
字符串转数值
strconv.Atoi(s string):将字符串转换为整数strconv.ParseInt(s, base, bitSize):将字符串转换为指定进制的整数strconv.ParseFloat(s, bitSize):将字符串转换为浮点数
1.4.5 字符串常用操作
| 函数 | 描述 | 示例 |
|---|---|---|
len(s) |
返回字符串的字节长度 | len(“go”) -> 2 |
s1 + s2 |
拼接字符串 | “go” + “lang” -> “golang” |
strings.Split(s, sep) |
分割字符串,返回切片 | strings.Split(“a,b,c”, “,”) -> [“a”, “b”, “c”] |
strings.Join(s, sep) |
连接切片元素,返回字符串 | strings.Join([]string{“a”,“b”}, “-”) -> “a-b” |
strings.Contains(s, substr) |
判断是否包含子串 | strings.Contains(“golang”, “go”) -> true |
strings.HasPrefix(s, prefix) |
判断是否以前缀开头 | strings.HasPrefix(“hello”, “he”) -> true |
strings.Index(s, substr) |
查找子串第一次出现的位置(字节索引) | strings.Index(“golang”, “a”) -> 3 |
fmt.Sprintf |
格式化拼接字符串(常用于复杂拼接) | fmt.Sprintf(“%s版本%d”, “Go”, 1) -> “Go版本1” |
1.5 布尔型
Go 语言中的布尔类型是 bool,只能取值 true 或 false。默认值为 false。
1 | package main |
1.6 复数
Go 语言内置了复数类型,分别是 complex64 和 complex128,分别对应于使用 float32 和 float64 作为实部和虚部的复数。
1 | var x complex128 = complex(1, 2) // 1+2i |
二、复合数据类型
Go 语言提供了多种复合数据类型,主要包括:
- 数组(Array)
- 切片(Slice)
- 映射(Map)
2.1 数组
数组是长度固定的同类型元素的集合,声明时必须指定长度。
1 | var arr [3]int = [3]int{1, 2, 3} // 声明一个长度为3的整数数组 |
2.1.1 初始化
Go 语言支持多种方式初始化数组,其中一种特殊的方式是按索引号初始化,可以指定某些元素的值,而其他元素会使用默认值(零值)进行填充。
1 | months := [...]string{1: "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} |
2.1.2 数组的遍历
传统 for 循环遍历(基于索引)
1 | for i := 0; i < len(arr); i++ { |
for range 循环遍历(基于元素)
1 | for index, value := range arr { |
2.2 切片
切片是动态数组,长度可变,更常用。切片是对数组的一个视图,底层依赖于数组。
2.2.1 切片的创建
通过数组进行切片
array[low:high]:左闭右开[low, high]1
2arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // 包含索引 1、2、3 的元素,即 [2, 3, 4]
使用 make() 构造
- 格式:
make([]T, len, cap)T是切片元素的类型len是切片的长度cap是切片的容量(底层数组的长度)
1
2s := make([]int, 5, 10) // 创建一个长度为5的切片,底层数组容量也为5
// s: [0 0 0 0 0] len=5 cap=10
直接声明和初始化
- 直接使用字面量创建切片
1
2s := []int{1, 2, 3}
var s2 []int // nil 切片,长度和容量都为 0
2.2.2 切片的遍历
按下标循环
- 使用
len(s)控制上界:1
2
3
4s := []int{1, 2, 3}
for i := 0; i < len(s); i++ {
fmt.Println(s[i])
} // 输出 1 2 3
for range 循环
- 每次迭代返回元素的索引和对应的值:
1
2
3
4
5
6
7
8s := []string{"a", "b", "c"}
for i := range s {
fmt.Printf("索引 %d\n", i)
} // 输出 索引 0 索引 1 索引 2
for _, value := range s {
fmt.Printf("值 %s\n", value)
} // 输出 值 a 值 b 值 c
2.2.3 切片常用操作
追加元素
- 使用
append()函数向切片追加元素,返回新的切片1
2
3s := []int{1, 2, 3}
s = append(s, 4) // s: [1 2 3 4]
s = append(s, 5, 6) // s: [1 2 3 4 5 6]
插入元素
- 通过切片分割后再拼接实现插入
1
2
3s := []int{1, 2, 4, 5}
s = append(s[:2], append([]int{3}, s[2:]...)...)
// s: [1 2 3 4 5]
删除元素
- 通过切片分割后再拼接实现删除
1
2
3s := []int{1, 2, 3, 4, 5}
s = append(s[:2], s[3:]...) // 删除索引为2的元素,即值3
// s: [1 2 4 5]
切片复制
- 使用
copy(dst, src)函数将 src 的内容复制到 dst 中dst需已分配长度,copy不会自动扩容
1
2
3src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src) // dst: [1 2 3]
2.2.4 切片与数组总结
| 特性 | 数组 (Array) | 切片 (Slice) |
|---|---|---|
| 长度 | 固定不变。长度是类型的一部分 | 动态可变,长度不是类型的一部分 ([]int) |
| 类型 | 值类型 (Value Type) | 引用类型 (Reference Type) |
| 赋值/传参 | 复制整个数组内容(全量复制) | 复制切片头(指针、长度、容量),底层数组共享 |
| 操作 | 长度固定,不支持 append 等动态操作 | 支持 append、copy 等操作,可动态增长 |
| 零值 | 元素的零值 | nil |
| 使用场景 | 长度已知且固定,性能要求高的底层结构 | 所有需要序列容器的场景,更灵活、常用 |
2.3 映射(Map)
Go 语言中的映射(Map)是一种无序的键值对集合,提供了快速的键值查找功能。Map 的键必须是可比较的类型(如字符串、整数等),值可以是任意类型。
2.3.1 Map 的定义
Map 的定义语法:map[KeyType]ValueType
1 | var m map[string]int // 声明一个键为 string,值为 int 的 Map |
2.3.2 Map 的创建
使用 make() 创建 Map
make(map[KeyType]ValueType, [cap])创建一个容量为 cap 的 Map1
2emptyM := make(map[string]int) // 创建一个空 Map
m := make(map[string]int, 10) // 创建一个容量为 10 的 Map
使用字面量创建 Map
- 直接使用字面量初始化 Map
1
2
3
4
5
6emptyM := map[int]string{} // 创建一个空 Map
m := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
2.3.3 Map 的遍历
使用 for range 循环遍历 Map
- 每次迭代返回一个键值对,顺序不固定
1
2
3
4
5m := map[string]int{"a": 1, "b": 2, "c": 3}
for key, value := range m {
fmt.Printf("键: %s, 值: %d\n", key, value)
}
// 输出的键值对顺序可能不同
按照指定顺序遍历 Map
- 先将 Map 的键提取到切片中,对切片排序后再遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19m := map[string]int{"b": 2, "a": 1, "c": 3}
// 提取键到切片
keys := make([]string, 0, len(m))
for key := range m {
keys = append(keys, key)
}
// 对键进行排序
sort.Strings(keys)
// 按照排序后的键遍历 Map
for _, key := range keys {
fmt.Printf("键: %s, 值: %d\n", key, m[key])
}
// 输出:
// 键: a, 值: 1
// 键: b, 值: 2
// 键: c, 值: 3
2.3.4 Map 的基本操作
存取键值对
- 通过
m[key]获取值,若键不存在返回值类型的零值 - 通过
m[key] = value设置键值对1
2
3m := map[string]int{"a": 1, "b": 2}
fmt.Println(m["a"]) // 输出 1
m["c"] = 3 // 添加新键值对
判断键是否存在
- 使用
value, ok := m[key]判断键是否存在value是对应键的值,如果键不存在则为值类型的零值ok为true表示键存在,false表示键不存在
1
2
3
4
5
6
7
8m := map[string]int{"a": 1, "b": 2}
value, ok := m["c"]
if ok {
fmt.Println("键存在,值为:", value)
} else {
fmt.Println("键不存在")
}
// 输出 键不存在
删除键值对
- 使用
delete(m, key)删除指定键的键值对1
2
3m := map[string]int{"a": 1, "b": 2}
delete(m, "a") // 删除键 "a"
fmt.Println(m) // 输出 map[b:2]