【Goland】:数组与切片
目录
1. 数组 (Array)
1.1 定义及特点
1.2 遍历数组
1.3 值类型特性
2. 切片 (Slice)
2.1 定义及特点
2.2 切片创建方式
2.3 切片的增删查改
2.3.1. 添加切片元素
2.3.2. 删除切片元素
2.3.3. 访问元素 / 遍历
2.3.4 修改元素
2.4 string 切片
1. 数组 (Array)
1.1 定义及特点
数组(Array)是一个由 固定长度 的、相同类型元素组成的有序集合。
var arr [N]T // N 表示数组长度,T 表示元素类型var a [3]int // 长度为 3 的 int 数组,默认值为 [0 0 0]
b := [4]string{"Go", "Rust", "Java", "C++"}
c := [...]bool{true, false, true} // 使用 `...` 自动推断长度
特点
-
固定长度:长度在定义时确定,无法改变。
[3]int
和[4]int
属于不同类型。 -
元素类型相同:数组中所有元素必须是相同类型。
-
默认值机制:
-
数值类型:默认
0
-
字符串:默认
""
-
布尔值:默认
false
-
引用类型(指针、切片、map、chan 等):默认
nil
-
package mainimport "fmt"func main() {var a [5]intvar b [5]stringvar c [5]boolfmt.Println(a) // [0 0 0 0 0]fmt.Println(b) // [ ]fmt.Println(c) // [false false false false false]
}
1.2 遍历数组
数组可以通过 下标索引 或 for-range 遍历,在for-range循环中遍历数组时,每次迭代会返回两个值,第一个是元素的索引,第二个是元素的值
package mainimport "fmt"func main() {a := [4]string{"Go", "Rust", "Java", "C++"}for i := 0; i < len(a); i++ {fmt.Printf("%s ", a[i])}fmt.Println()for idx, val := range a {fmt.Printf("%d %s\n", idx, val)}
}
1.3 值类型特性
数组是 值类型,赋值或传参时会 拷贝整个数组,互不影响。
package mainimport "fmt"func main() {a := [4]int{1, 2, 3, 4}b := ab[0] = 100fmt.Println(a) // [1 2 3]fmt.Println(b) // [100 2 3 4]
}
如果希望在函数中对数组的操作影响到原始数组,可以通过数组指针的方式进行传参。
package mainimport "fmt"func modifyArray(arr *[4]int) {(*arr)[0] = 999arr[1] = 888
}func main() {a := [4]int{1, 2, 3, 4}modifyArray(&a) // 传入数组指针fmt.Println(a) // [999 888 3 4]
}
2. 切片 (Slice)
2.1 定义及特点
-
切片是 动态长度的序列 或 可变长数组,序列中每个元素类型相同。
-
切片 引用数组 的一部分或全部元素。
-
底层由 三部分 构成:
-
指针(ptr):指向底层数组起始位置
-
长度(len):切片中实际元素数量
-
容量(cap):切片从起始位置到底层数组末尾的最大容量
-
var s1 []int // []s2 := []int{1, 2, 3} // [ 1 2 3 ]s3 := make([]int, 3, 5) // [ 0 0 0 ], 长度3,容量5
2.2 切片创建方式
方式一:指定具体数组
-
直接初始化切片,底层数组由 Go 自动分配
-
len = 10
,cap = 10
-
底层数组元素值来自指定的字面量。
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}fmt.Printf("类型:%T\n", s) // 类型:[]intfmt.Println(len(s)) // 10fmt.Println(cap(s)) // 10
方式二:引用现有数组
-
底层依赖已有数组
-
注意:修改
slice
会影响arr
。
// slice 指针指向 arr[3]// len = 3 → 结束下标 - 起始下标// cap = 7 → 从起始下标到数组末尾的容量a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var s = a[3:6]fmt.Printf("类型:%T, 值:%d\n", s, s) // 类型:[]int, 值:[4 5 6]fmt.Println(len(s)) // 3fmt.Println(cap(s)) // 7
方式三:make 函数
-
底层数组由
make
在堆上分配 -
未赋值时使用类型默认值(如 int → 0, string → "", bool → false)。
// len = 5 → 元素个数// cap = 10 → 底层数组容量s := make([]int, 5, 10)fmt.Println(s) // [0 0 0 0 0]fmt.Println(len(s)) // 5fmt.Println(cap(s)) // 10
2.3 切片的增删查改
2.3.1. 添加切片元素
newSlice := append(oldSlice, elem1, elem2, ...)
// 第一个参数:目标切片
// 后续参数:要追加的一个或多个元素(可变参数)
// 返回值:一个新的切片(可能和原切片底层数组相同,也可能不一样)
一、容量够用
- 如果
oldSlice
的cap
足够容纳新元素 → 直接写入原来的底层数组。 - 新切片和旧切片共享底层数组,修改会互相影响。
package mainimport "fmt"func main() {s1 := make([]int, 3, 5)s1[0], s1[1], s1[2] = 1, 2, 3s2 := append(s1, 4)fmt.Println(s1) // [1 2 3]fmt.Println(s2) // [1 2 3 4]// 修改s1的s1[0],结果s2的s2[0]也改变了s1[0] = 100fmt.Println(s1) // [100 2 3]fmt.Println(s2) // [100 2 3 4]
}
二、容量不足
如果 cap
不够 → Go 运行时会:
-
重新分配一个更大的底层数组(通常是原来的 2 倍,但不固定)
-
把旧数据复制到新数组
-
把新元素放进去
-
返回一个新的切片(和旧切片不再共享底层数组)
package mainimport "fmt"func main() {s1 := make([]int, 3, 3)s1[0], s1[1], s1[2] = 1, 2, 3s2 := append(s1, 4)fmt.Println(s1) // [1 2 3]fmt.Println(s2) // [1 2 3 4]// 修改 s1 的 s1[0],s2 的 s2[0] 不会改变了s1[0] = 100fmt.Println(s1) // [100 2 3]fmt.Println(s2) // [1 2 3 4]
}
三、追加另一个切片
package mainimport "fmt"func main() {s1 := []int{1, 2, 3}s2 := []int{4, 5, 6}s3 := append(s1, s2...)fmt.Println(s3) // [1 2 3 4 5 6]
}
四、在中间插入元素
Go 没有直接的 insert
,需要用切片拼接:
package mainimport "fmt"func main() {s1 := []int{1, 2, 3}i := 1s3 := append(s1[:i], append([]int{100}, s1[i:]...)...)fmt.Println(s3) // [1 100 2 3]
}
2.3.2. 删除切片元素
Go 也没有内置 delete
,常见的写法是用切片拼接。
// 删除下标为2s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}s3 := append(s1[:2], s1[3:]...)fmt.Println(s3) // [1 2 4 5 6 7 8 9 10]// 删除开头的元素s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}s3 := append(s1[1:])fmt.Println(s3) // [2 3 4 5 6 7 8 9 10]// 删除末尾的元素s3 := append(s1[:len(s1)-1])fmt.Println(s3) // [1 2 3 4 5 6 7 8 9]
2.3.3. 访问元素 / 遍历
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}// 按下标访问fmt.Println(s1[4]) // 5fmt.Println(s1[6]) // 7// 遍历for i, v := range s1 {fmt.Println(i, v)}for i := 0; i < len(s1); i++ {fmt.Println(s1[i])}
2.3.4 修改元素
s := []int{1, 2, 3}
s[1] = 99
fmt.Println(s) // [1 99 3]
2.4 string 切片
-
在 Go 中,
string
本质上是一个 只读的字节序列(byte slice)。也就是说,string
底层是 不可变的[]byte
。一旦创建,里面的内容不能被修改。 -
string 是不可变的,修改字符串必须转换为
[]byte
或[]rune
。 -
切片操作不会复制数据,只是产生一个新的
string
(仍然只读)。 -
[]byte
适合 ASCII/英文,[]rune
适合中文或其他 Unicode 字符。
s := "hello"s1 := s[1:]fmt.Println(s) //hellofmt.Println(s1) // ellos[0] = '1' // 无法直接修改
修改字符串的方法
因为 string
不能直接修改,所以必须转换为 可变类型([]byte
或 []rune
):
s := "hello"a := []byte(s)a[0] = 'x'fmt.Println(string(a)) // xello s := "我爱中国"a := []rune(s)a[0] = '你'fmt.Println(string(a)) // 你爱中国