Maps
1.创建map make(map[键类型]值类型)
2.设置键值对 name[key]=value;
3. name[key]获取键值
3.1 key不存在 则返回 0
4.len()方法 返回 map 上 键值对数量 len(name)
5.delete()方法 从map中删除 键值对 delete(name,key)
6.clear()方法 map中删除所有键值对 clear(name)
7.map获取值时,可选第二个返回值,第二个值表明map中是否有该键名
第二个返回值 false 表明 没有 该键, true 表示 有该键
用于区分 没有键名 返回的 0 和 有键值对 返回的 0
8.同时声明和初始化map n:=map[键类型]值类型{key1:value1,key2:value2}
9.maps包中有许多使用方法api
9.1 maps.Equal() 判断 两个map是否相同
package main
import (
"fmt"
"maps"
)
func main() {
//1.创建map make(map[键类型]值类型)
m := make(map[string]int)
//2.设置键值对 name[key]=value
m["k1"] = 7
m["k2"] = 13
fmt.Println("map:", m)
//3. name[key]获取键值
v1 := m["k1"]
fmt.Println("v1:", v1) //7
//3.1 key不存在 则返回 0
v3 := m["k3"]
fmt.Println("v3:", v3)//0
//4.len()方法 返回 map 上 键值对数量 len(name)
fmt.Println("len:", len(m))
//5.delete()方法 从map中删除 键值对 delete(name,key)
delete(m, "k2")
fmt.Println("map:", m)
//6.clear()方法 map中删除所有键值对 clear(name)
clear(m)
fmt.Println("map:", m)
//7.map获取值时,可选第二个返回值,第二个值表明map中是否有该键名
//第二个返回值 false 表明 没有 该键, true 表示 有该键
//用于区分 没有键名 返回的 0 和 有键值对 返回的 0
_, prs := m["k2"]
fmt.Println("prs:", prs)
//8.同时声明和初始化map n:=map[键类型]值类型{key1:value1,key2:value2}
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
//9.maps包中有许多使用方法api
//9.1 maps.Equal() 判断 两个map是否相同
n2 := map[string]int{"foo": 1, "bar": 2}
if maps.Equal(n, n2) {
fmt.Println("n == n2")
}
}
使用 fmt.Println.打印时,映射以 map[k:v k:v] 格式显示
打印如下
$ go run maps.go
map: map[k1:7 k2:13]
v1: 7
v3: 0
len: 2
map: map[k1:7]
map: map[]
prs: false
map: map[bar:2 foo:1]
n == n2
Functions
//0.定义函数 func name(args argesType) returnType { return value}
//1.Go 需要显式返回 return, 他不会自动返回最后一个表达式的值
//2.有多个相同类型参数 可以省略前面的类型定义
//3.调用函数 name(args)
package main
import "fmt"
//0.定义函数 func name(args argesType) returnType { return value}
//1.Go 需要显式返回 return, 他不会自动返回最后一个表达式的值
func plus(a int, b int) int {
return a + b
}
//2.有多个相同类型参数 可以省略前面的类型定义
func plusPlus(a, b, c int) int {
return a + b + c
}
//3.调用函数 name(args)
func main() {
res := plus(1, 2)
fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res)
}
打印
$ go run functions.go
1+2 = 3
1+2+3 = 6
Multiple Return Values 多返回值
//1. Go 内置了 多个返回值的支持 reurn 3,7
//2.不同 变量 接收不同返回值
//3.只需要接受 部分返回值,使用空标识符_
package main
import "fmt"
//1. Go 内置了 多个返回值的支持 reurn 3,7
func vals() (int, int) {
return 3, 7
}
func main() {
//2.不同 变量 接收不同返回值
a, b := vals()
fmt.Println(a)
fmt.Println(b)
//3.只需要接受 部分返回值,使用空标识符_
_, c := vals()
fmt.Println(c)
}
运行:
$ go run multiple-return-values.go
3
7
7
Variadic Functions 可变参数函数
//0.可变参数 可以使用 任意数量的参数调用,例如fmt.Println
//1.将任意数量int 作为函数参数 nums …int
//1.1 该函数中 nums类型等效于 int[],可以调用 len(),用range迭代
//2.可变参数函数可以 以通常的方式 使用单个参数调用。
//3.如果一个切片slice中已经有多个args 可以这样使用 nums…
在 Go 语言中,nums… 是一种 语法糖,用于将切片(slice)展开为可变参数(variadic arguments)。它的作用是“动态解包切片”,而不是“写死”参数。以下是详细解释:
核心概念
-
可变参数函数
函数定义时使用 … 表示接受多个参数(数量不固定): -
调用时的 slice…
当已有数据存储在切片中时,通过 slice… 动态解包切片元素,作为可变参数传递:
场景 | 写法 | 说明 |
---|---|---|
定义可变参数函数 | func f(args ...T) | ...T 表示接受多个 T 类型的参数,函数内 args 类型为 []T (切片) |
传递切片给函数 | f(slice...) | 必须显式使用 ... 解包切片,否则类型不匹配(需严格区分 []T 和 ...T ) |
直接传递多个参数 | f(1, 2, 3) | 无需 ... ,直接按可变参数传递 |
package main
import "fmt"
//0.可变参数 可以使用 任意数量的参数调用,例如fmt.Println
//1.将任意数量int 作为函数参数 nums ...int
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
//1.1 该函数中 nums类型等效于 int[],可以调用 len(),用range迭代
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func main() {
//2.可变参数函数可以 以通常的方式 使用单个参数调用。
sum(1, 2)
sum(1, 2, 3)
//3.如果一个切片slice中已经有多个args 可以这样使用 nums...
nums := []int{1, 2, 3, 4}
sum(nums...)
}
运行
$ go run variadic-functions.go
[1 2] 3
[1 2 3] 6
[1 2 3 4] 10
Closures 闭包
//0.Go 支持匿名函数 ,这些函数可以形成闭包 。当您想要内联定义函数而不必命名它时,匿名函数非常有用。
//1.函数 intSeq 返回另一个函数 为匿名函数 返回的函数在 变量 i 上 形成闭包
//2.调用 intSeq 返回一个函数 给 nextInt
//2.1每次调用nextInt 更新 i
//2.2 如果重新 初始化一个 函数 状态是独立的
package main
import "fmt"
//0.Go 支持匿名函数 ,这些函数可以形成闭包 。当您想要内联定义函数而不必命名它时,匿名函数非常有用。
//1.函数 intSeq 返回另一个函数 为匿名函数 返回的函数在 变量 i 上 形成闭包
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
func main() {
//2.调用 intSeq 返回一个函数 给 nextInt
nextInt := intSeq()
//2.1每次调用nextInt 更新 i
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
//2.2 如果重新 初始化一个 函数 状态是独立的
newInts := intSeq()
fmt.Println(newInts())
}
执行:
$ go run closures.go
1
2
3
1
Recursion 递归
//1. fact函数会自行调用直到达到 fact(0)
//2.匿名函数 也可以是递归的 但是需要在 定义前 显示声明变量
//2.1 fib是在main中 声明的 因此Go知道这里fib调用那个函数
package main
import "fmt"
//1. fact函数会自行调用直到达到 fact(0)
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
fmt.Println(fact(7))//7的阶乘
//2.匿名函数 也可以是递归的 但是需要在 定义前 显示声明变量
var fib func(n int) int
fib = func(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
//2.1 fib是在main中 声明的 因此Go知道这里fib调用那个函数
fmt.Println(fib(7))
}
执行:
$ go run recursion.go
5040
13
Range over Built-in Types ( Range over 内置类型)
//1.使用 range 对切片中数字求和,不需要索引 使用空标识符 _ 忽略
//2. range 在 切片和数组上 提供 索引 和 值 ,上面不需要索引 使用空标识符 _ 忽略
//3.range 迭代 map 返回 键和值
//3.1range 只迭代 map 键
//4.range 迭代字符串 返回 索引 和 Unicode 码点值
- 在 Go 语言中,使用 range 遍历字符串时,会逐个迭代字符串中的 Unicode 字符(rune),并返回两个值:
当前字符的起始字节索引(int 类型)
当前字符的 Unicode 码点值(rune 类型,即 int32 的别名)
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
//1.使用 range 对切片中数字求和,不需要索引 使用空标识符 _ 忽略
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//2. range 在 切片和数组上 提供 索引 和 值 ,上面不需要索引 使用空标识符 _ 忽略
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//3.range 迭代 map 返回 键和值
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//3.1range 只迭代 map 键
for k := range kvs {
fmt.Println("key:", k)
}
//4.range 迭代字符串 返回 索引 和 Unicode 码点值
for i, c := range "go" {
fmt.Println(i, c)
}
}
执行:
$ go run range-over-built-in-types.go
sum: 9
index: 1
a -> apple
b -> banana
key: a
key: b
0 103
1 111
Pointers 指针
//1.参数不使用指针 zeroval 获得的参数 是与传入参数 不同的副本
//2.参数使用指针 zeroptr 获得的参数 和 传入参数 是同一内存地址 修改值 会同时修改
//3.&i 语法 取出i的内存地址,即指向i的指针
package main
import "fmt"
//1.参数不使用指针 zeroval 获得的参数 是与传入参数 不同的副本
func zeroval(ival int) {
ival = 0
}
//2.参数使用指针 zeroptr 获得的参数 和 传入参数 是同一内存地址 修改值 会同时修改
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("initial:", i)
zeroval(i)
fmt.Println("zeroval:", i)
//3.&i 语法 取出i的内存地址,即指向i的指针
zeroptr(&i)
fmt.Println("zeroptr:", i)
fmt.Println("pointer:", &i)
}
执行:
$ go run pointers.go
initial: 1
//4.zeroval 不会更改 main 中的 i,但 zeroptr 会,是因为它引用了该变量的内存地址。
zeroval: 1
zeroptr: 0
pointer: 0x42131100
Strings and Runes 字符串和符文
**//1.字符串 等同于 []byte len()返回 bytes 长度
//2.字符串 索引 对应的 值 是原始字节值 构成字节的16进制值
//3.计算字符串中 有多少符文 utf8.RuneCountInString(s) 按顺序解码UTF-8 rune
// range 循环 字符串 解码每个 符文
//4. utf8.DecodeRuneInString(s) 获取 符文 和 对应符文的 字节长度
**
-
Go 字符串是只读的字节切片。该语言和标准库专门将字符串视为以 UTF-8 编码的文本容器。在其他语言中,字符串由 “字符” 组成。在 Go 中,字符的概念称为符文 - 它是 表示 Unicode 码位的整数
-
在 Go 语言中,utf8.DecodeRuneInString(s) 方法用于 解码字符串 s 中的第一个 UTF-8 字符,返回该字符的 Unicode 码点(rune)及其占用的字节数。该方法属于 unicode/utf8 包,专门处理 UTF-8 编码的字符串,适合需要逐字符解析的场景。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
//s 是一个字符串 泰语 表示 hello
const s = "สวัสดี"
//1.字符串 等同于 []byte len()返回 bytes 长度
fmt.Println("Len:", len(s))
//2.字符串 索引 对应的 值 是原始字节值 构成字节的16进制值
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
fmt.Println()
//3.计算字符串中 有多少符文 utf8.RuneCountInString(s) 按顺序解码UTF-8 rune
fmt.Println("Rune count:", utf8.RuneCountInString(s))
// range 循环 字符串 解码每个 符文
for idx, runeValue := range s {
fmt.Printf("%#U starts at %d\n", runeValue, idx)
}
//4. utf8.DecodeRuneInString(s) 获取 符文 和 对应符文的 字节长度
fmt.Println("\nUsing DecodeRuneInString")
for i, w := 0, 0; i < len(s); i += w {
runeValue, width := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%#U starts at %d\n", runeValue, i)
w = width
examineRune(runeValue)
}
}
func examineRune(r rune) {
//单引号
if r == 't' {
fmt.Println("found tee")
} else if r == 'ส' {
fmt.Println("found so sua")
}
}
执行:
$ go run strings-and-runes.go
Len: 18
e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5
Rune count: 6
U+0E2A 'ส' starts at 0
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15
Using DecodeRuneInString
U+0E2A 'ส' starts at 0
found so sua
U+0E27 'ว' starts at 3
U+0E31 'ั' starts at 6
U+0E2A 'ส' starts at 9
found so sua
U+0E14 'ด' starts at 12
U+0E35 'ี' starts at 15
Structs 结构体
1. 结构体定义
type person struct {
name string
age int
}
• 要点:使用 type 结构体名 struct
定义,字段通过 字段名 类型
声明。
• 代码对应:定义 person
结构体,包含 name
(字符串)和 age
(整型)字段。
2. 结构体初始化
// 方式1:顺序初始化(需全部字段)
fmt.Println(person{"Bob", 20})
// 方式2:命名字段初始化(可省略部分字段)
fmt.Println(person{name: "Alice", age: 30})
fmt.Println(person{name: "Fred"}) // age 默认为 0
• 要点:
• 支持顺序初始化或显式命名字段初始化。
• 未赋值的字段默认为零值(如 int
为 0
)。
• 代码对应:展示三种初始化方式,包括部分字段省略。
3. 结构体指针
// 直接返回结构体指针
fmt.Println(&person{name: "Ann", age: 40})
// 通过函数返回指针
func newPerson(name string) *person {
p := person{name: name}
p.age = 42
return &p
}
fmt.Println(newPerson("Jon"))
• 要点:
• 使用 &
直接获取结构体指针。
• 函数返回局部结构体的指针是安全的(Go 自动分配在堆上)。
• 代码对应:newPerson
函数返回指针,演示直接取址。
4. 访问结构体字段
s := person{name: "Sean", age: 50}
fmt.Println(s.name) // 直接访问
sp := &s
fmt.Println(sp.age) // 指针自动解引用
sp.age = 51 // 修改字段值
fmt.Println(sp.age)
• 要点:
• 通过 .
访问字段,指针类型会自动解引用。
• 修改指针指向的结构体会影响原变量。
• 代码对应:s.name
和 sp.age
的访问与修改。
5. 匿名结构体
dog := struct {
name string
isGood bool
}{
"Rex",
true,
}
fmt.Println(dog)
• 要点:
• 无需预定义类型,直接声明匿名结构体并初始化。
• 适用于一次性使用的场景。
• 代码对应:定义并打印 dog
匿名结构体。
要点 | 代码示例 | 说明 |
---|---|---|
结构体定义 | type person struct { ... } | 定义包含字段的结构体类型。 |
初始化方式 | person{"Bob", 20} 或 person{name: "Alice"} | 支持顺序和命名初始化。 |
结构体指针 | &person{...} 和 func newPerson() *person | 直接取址或函数返回指针。 |
字段访问/修改 | s.name , sp.age = 51 | 指针自动解引用,直接修改字段。 |
匿名结构体 | dog := struct { ... }{...} | 临时使用的未命名结构体。 |
通过代码示例清晰展示了 Go 结构体的核心操作,适用于日常开发参考。
package main
import "fmt"
//1.创建结构体 type 结构体名 struct {字段名 类型 }
type person struct {
name string
age int
}
//2.
func newPerson(name string) *person {
p := person{name: name}
p.age = 42
return &p
}
func main() {
fmt.Println(person{"Bob", 20})
fmt.Println(person{name: "Alice", age: 30})
fmt.Println(person{name: "Fred"})
fmt.Println(&person{name: "Ann", age: 40})
fmt.Println(newPerson("Jon"))
s := person{name: "Sean", age: 50}
fmt.Println(s.name)
sp := &s
fmt.Println(sp.age)
sp.age = 51
fmt.Println(sp.age)
dog := struct {
name string
isGood bool
}{
"Rex",
true,
}
fmt.Println(dog)
}```
执行:
```go
$ go run structs.go
{Bob 20}
{Alice 30}
{Fred 0}
&{Ann 40}
&{Jon 42}
Sean
50
51
{Rex true}