Go的数组,slice切片,map的使用
在Go语言中,数组是一种固定长度的数据结构,可以存储同一类型的元素。数组的长度是数组类型的一部分,因此在定义时必须明确指定长度。数组的元素可以通过索引访问,索引从0开始。
1. 数组的声明与初始化
在Go中,可以使用多种方式声明和初始化数组。
示例 1:声明数组
package main
import "fmt"
func main() {
// 声明长度为5的int数组,默认值为0
var arr [5]int
fmt.Println("Array:", arr)
}
示例 2:初始化数组
package main
import "fmt"
func main() {
// 声明并初始化数组
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println("Initialized Array:", arr)
// 让Go根据元素数量自动推断数组长度
autoArr := [...]int{10, 20, 30}
fmt.Println("Auto-length Array:", autoArr)
}
2. 访问和修改数组元素
数组元素可以通过索引访问和修改。索引从0开始,到len(arr) - 1
结束。
示例
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
// 访问元素
fmt.Println("Element at index 2:", arr[2])
// 修改元素
arr[2] = 10
fmt.Println("Modified Array:", arr)
}
3. 数组的长度和容量
使用内置函数len()
可以获取数组的长度。数组的容量固定,无法在运行时改变。
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println("Length of the array:", len(arr))
}
4. 遍历数组
Go支持for
循环来遍历数组,可以使用索引或range
关键字。
使用索引遍历
package main
import "fmt"
func main() {
arr := [3]string{"Go", "Python", "Java"}
for i := 0; i < len(arr); i++ {
fmt.Printf("Element at index %d: %s\n", i, arr[i])
}
}
使用range
遍历
package main
import "fmt"
func main() {
arr := [3]string{"Go", "Python", "Java"}
for i, v := range arr {
fmt.Printf("Index %d: %s\n", i, v)
}
// 仅遍历值
for _, v := range arr {
fmt.Println("Value:", v)
}
}
5. 多维数组
Go支持多维数组,比如二维数组可以用于表示矩阵或表格。
示例:二维数组
package main
import "fmt"
func main() {
// 声明并初始化一个2x3的二维数组
var matrix [2][3]int = [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
// 访问元素
fmt.Println("Element at [1][2]:", matrix[1][2])
// 遍历二维数组
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
fmt.Printf("Element at [%d][%d]: %d\n", i, j, matrix[i][j])
}
}
}
6. 数组作为函数参数
在Go中,数组作为参数传递时是值传递,即传递数组的副本。因此,在函数内修改数组不会影响原数组。
示例
package main
import "fmt"
// 函数接收数组参数并修改它
func modifyArray(arr [3]int) {
arr[0] = 99
fmt.Println("Inside function:", arr)
}
func main() {
originalArray := [3]int{1, 2, 3}
modifyArray(originalArray)
fmt.Println("Outside function:", originalArray) // 不变,仍为{1, 2, 3}
}
7. 数组和切片的区别
- 数组:长度固定,存储在栈上,传递是值传递。
- 切片:动态长度,更灵活,存储在堆上,传递是引用传递。
8. 数组复制
在Go中,直接赋值一个数组会复制整个数组的数据。
package main
import "fmt"
func main() {
arr1 := [3]int{1, 2, 3}
arr2 := arr1 // arr2 是 arr1 的副本
arr2[0] = 99
fmt.Println("arr1:", arr1) // 不变,仍为{1, 2, 3}
fmt.Println("arr2:", arr2) // {99, 2, 3}
}
- 声明和初始化:数组的长度固定,类型为
[n]T
。 - 访问和修改:通过索引操作。
- 遍历:可以使用
for
或for range
循环。 - 多维数组:支持嵌套数组,用于复杂数据结构。
- 函数参数:数组是值传递,不会修改原数组。
- 复制:数组赋值会创建新副本。
在Go语言中,slice
(切片)是一种非常灵活和强大的数据结构,类似于动态数组。与数组不同,切片的长度是可变的,因此它们可以在程序运行时增长或缩减。
1. 声明和初始化切片
Go语言中,可以使用多种方式声明和初始化切片。
示例 1:使用字面量声明和初始化切片
package main
import "fmt"
func main() {
// 声明并初始化切片
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Slice:", slice)
}
示例 2:使用make
函数创建切片
make
函数用于创建切片,可以指定长度和容量。
package main
import "fmt"
func main() {
// 创建长度为3,容量为5的切片
slice := make([]int, 3, 5)
fmt.Println("Slice:", slice)
fmt.Println("Length:", len(slice))
fmt.Println("Capacity:", cap(slice))
}
示例 3:从数组或切片生成切片
package main
import "fmt"
func main() {
arr := [5]int{10, 20, 30, 40, 50}
// 从数组创建切片
slice := arr[1:4] // 包含索引1到3的元素
fmt.Println("Slice from array:", slice)
}
2. 访问和修改切片元素
切片元素可以通过索引访问和修改,与数组类似。
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
// 访问元素
fmt.Println("Element at index 2:", slice[2])
// 修改元素
slice[2] = 10
fmt.Println("Modified Slice:", slice)
}
3. 切片的长度和容量
使用len()
函数获取切片的长度,使用cap()
函数获取切片的容量。
package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Println("Length of slice:", len(slice))
fmt.Println("Capacity of slice:", cap(slice))
}
4. 切片的追加
切片可以使用append()
函数追加元素。如果切片容量不足,append()
会分配新的底层数组。
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4, 5)
fmt.Println("Appended Slice:", slice)
}
扩展示例:追加另一个切片
package main
import "fmt"
func main() {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
slice1 = append(slice1, slice2...) // 使用 `...` 解包
fmt.Println("Combined Slice:", slice1)
}
5. 切片的拷贝
使用copy()
函数可以将一个切片的内容拷贝到另一个切片中。copy()
返回拷贝的元素个数。
package main
import "fmt"
func main() {
src := []int{1, 2, 3, 4, 5}
dest := make([]int, len(src))
numCopied := copy(dest, src)
fmt.Println("Number of elements copied:", numCopied)
fmt.Println("Destination Slice:", dest)
}
6. 切片的底层数组共享
切片实际上是对底层数组的引用,因此多个切片可以共享同一个底层数组。
package main
import "fmt"
func main() {
arr := [5]int{10, 20, 30, 40, 50}
slice1 := arr[1:4]
slice2 := arr[2:5]
fmt.Println("Slice1:", slice1)
fmt.Println("Slice2:", slice2)
// 修改底层数组,影响到所有引用它的切片
arr[2] = 100
fmt.Println("Modified Slice1:", slice1)
fmt.Println("Modified Slice2:", slice2)
}
7. 切片的扩容机制
当切片的容量不足以容纳新元素时,append()
会创建一个新的底层数组,其容量通常是现有容量的2倍。
package main
import "fmt"
func main() {
slice := make([]int, 2, 3)
fmt.Println("Initial slice:", slice, "Length:", len(slice), "Capacity:", cap(slice))
slice = append(slice, 1, 2)
fmt.Println("Slice after append:", slice, "Length:", len(slice), "Capacity:", cap(slice))
}
这里创建了一个切片 slice,它的类型是 []int(整数切片),初始长度为2,容量为3。这意味着它会初始化一个包含两个零值(即0)的切片,同时预留了足够的空间来存储总共三个整数,而不需要立即分配新的内存。
8. 切片与多维切片
Go语言支持多维切片,但需要手动初始化每个内部切片。
package main
import "fmt"
func main() {
// 声明一个二维切片
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
for i := 0; i < len(matrix); i++ {
fmt.Println("Row", i, ":", matrix[i])
}
}
- 切片是动态数组,长度可以变化。
make()
和字面量可以用来创建切片。- **
append()
和copy()
**是切片操作的关键函数。 - 共享底层数组:修改切片可能会影响到同一底层数组的其他切片。
- 扩容机制:切片自动扩容,通常将容量增大到现有的2倍。
在Go语言中,map
是一种内置的数据结构,用于存储键值对。map
的键是唯一的,每个键都对应一个值。map
非常适合用于快速查找、添加和删除数据。
1. 声明和初始化map
可以使用make()
函数或者字面量语法来声明和初始化map
。
示例 1:使用make()
函数
package main
import "fmt"
func main() {
// 声明一个map,键是string类型,值是int类型
myMap := make(map[string]int)
// 添加键值对
myMap["apple"] = 5
myMap["banana"] = 3
fmt.Println("Map:", myMap)
}
示例 2:使用字面量初始化
package main
import "fmt"
func main() {
// 使用字面量初始化一个map
myMap := map[string]int{
"apple": 5,
"banana": 3,
"orange": 7,
}
fmt.Println("Map:", myMap)
}
2. 访问和修改map
元素
可以使用键访问和修改map
中的值。
package main
import "fmt"
func main() {
myMap := map[string]int{
"apple": 5,
"banana": 3,
}
// 访问元素
fmt.Println("Value for key 'apple':", myMap["apple"])
// 修改元素
myMap["apple"] = 10
fmt.Println("Modified value for key 'apple':", myMap["apple"])
// 添加新元素
myMap["cherry"] = 8
fmt.Println("Map after addition:", myMap)
}
3. 检查键是否存在
在Go中,通过索引操作返回值和布尔值可以检查键是否存在。
package main
import "fmt"
func main() {
myMap := map[string]int{
"apple": 5,
"banana": 3,
}
// 检查键是否存在
value, exists := myMap["orange"]
if exists {
fmt.Println("Value for key 'orange':", value)
} else {
fmt.Println("Key 'orange' does not exist")
}
}
4. 删除map
元素
可以使用delete()
函数从map
中删除键值对。
package main
import "fmt"
func main() {
myMap := map[string]int{
"apple": 5,
"banana": 3,
"cherry": 8,
}
fmt.Println("Map before deletion:", myMap)
// 删除键为"banana"的元素
delete(myMap, "banana")
fmt.Println("Map after deletion:", myMap)
}
5. 遍历map
使用for range
循环可以遍历map
中的键值对。遍历map
时的顺序是随机的。
package main
import "fmt"
func main() {
myMap := map[string]int{
"apple": 5,
"banana": 3,
"cherry": 8,
}
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}
6. 嵌套map
map
可以嵌套使用,即值也可以是map
类型。
package main
import "fmt"
func main() {
nestedMap := map[string]map[string]int{
"fruits": {
"apple": 5,
"banana": 3,
},
"vegetables": {
"carrot": 10,
"beet": 4,
},
}
fmt.Println("Nested Map:", nestedMap)
fmt.Println("Value for fruits -> apple:", nestedMap["fruits"]["apple"])
}
7. 初始化空map
在Go中,如果使用var
声明map
,需要使用make()
初始化后才能使用,否则会导致运行时错误。
package main
import "fmt"
func main() {
// 使用var声明,但未初始化
var myMap map[string]int
// 会引发panic错误,不能直接赋值
// myMap["apple"] = 5 // 错误
// 使用make初始化
myMap = make(map[string]int)
myMap["apple"] = 5
fmt.Println("Initialized Map:", myMap)
}
8. map
的并发访问
map
不是并发安全的,多个goroutine同时访问同一个map
可能导致竞态条件或崩溃。如果需要并发访问,请使用sync.Map
或者添加互斥锁(sync.Mutex
)。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
myMap := make(map[int]int)
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
mu.Lock()
myMap[i] = i * 10
mu.Unlock()
}(i)
}
wg.Wait()
fmt.Println("Map after concurrent access:", myMap)
}
总结
map
声明和初始化:可以使用make()
或字面量语法。- 访问和修改:通过键进行访问,支持更新和添加。
- 删除键:使用
delete()
函数。 - 检查键是否存在:使用索引返回的布尔值。
- 遍历
map
:使用for range
,但顺序不保证。 - 并发访问:
map
不是并发安全的,需使用同步机制。