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 mainimport ("fmt""maps"
)func main() {//1.創建map make(map[鍵類型]值類型)m := make(map[string]int)//2.設置鍵值對 name[key]=valuem["k1"] = 7m["k2"] = 13fmt.Println("map:", m)//3. name[key]獲取鍵值v1 := m["k1"]fmt.Println("v1:", v1) //7//3.1 key不存在 則返回 0v3 := 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 mainimport "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 mainimport "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 mainimport "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 mainimport "fmt"//0.Go 支持匿名函數 ,這些函數可以形成閉包 。當您想要內聯定義函數而不必命名它時,匿名函數非常有用。
//1.函數 intSeq 返回另一個函數 為匿名函數 返回的函數在 變量 i 上 形成閉包
func intSeq() func() int {i := 0return 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 mainimport "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) intfib = 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 mainimport "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 mainimport "fmt"//1.參數不使用指針 zeroval 獲得的參數 是與傳入參數 不同的副本
func zeroval(ival int) {ival = 0
}
//2.參數使用指針 zeroptr 獲得的參數 和 傳入參數 是同一內存地址 修改值 會同時修改
func zeroptr(iptr *int) {*iptr = 0
}func main() {i := 1fmt.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 mainimport ("fmt""unicode/utf8"
)func main() {//s 是一個字符串 泰語 表示 helloconst 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 runefmt.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 = widthexamineRune(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 stringage 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 = 42return &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 stringisGood 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 mainimport "fmt"//1.創建結構體 type 結構體名 struct {字段名 類型 }
type person struct {name stringage int
}//2.
func newPerson(name string) *person {p := person{name: name}p.age = 42return &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 := &sfmt.Println(sp.age)sp.age = 51fmt.Println(sp.age)dog := struct {name stringisGood 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}