数组类型,是一组同类型数据的集合,通过从0开始的下标,访问元素值。数组初始化后长度是固定的,无法修改数组长度。
1.数组定义
语法:
var 变量名 [数组大小]元素类型
例子:
// 定义拥有10个元素的数组a,元素类型是int
var a [10]int
// 定义拥有2个元素的字符串数组
var arr [2]string
2.数组初始化
可以在定义数组的时候直接初始化数组元素
// 数组大小为6, 后面花括号直接初始化6个元素
primes := [6]int{2, 3, 5, 7, 11, 13}
3.读写数组元素
通过下标引用数组元素,下标从0开始计算,最大不能超过数组大小。
引用数组语法:
数组变量名[下标]
例子:
// 定义字符串数组a, 数组大小等于2
var a [2]string
// 给数组第一个元素赋值
a[0] = "Hello"
// 给数组第二个元素赋值
a[1] = "World"
// 打印数组第1和2个元素
fmt.Println(a[0], a[1])
输出:
Hello World
4.获取数组大小
可以通过len获取数组大小
例子:
primes := [6]int{2, 3, 5, 7, 11, 13}
// 打印数组大小
fmt.Println(len(primes))
5.slice(切片)
数组的长度是固定的,在实际应用中非常不方便,因此go语言提供了slice机制,我们一般翻译成切片,可以将切片当成动态数组用,动态数组指的是数组的长度可以动态调整。
切片底层依赖数组存储数据,切片本身是不存储数据,如果底层数组无法存储更多的数据,就会自动新申请一个更大存储空间的数组,将老的数组中的数据拷贝到新的数组,这样我们看起来slice就像动态数组一样可以存储任意数量的数据。
从底层存储角度看,切片(slice)就是数组的引用。
5.1. 定义切片
切片类型语法:
[]数据类型
切片类型跟数组类型的区别就是没有数组大小。
切片语法:
数组变量或者切片[low : high]
从数组变量中切一块 low <= 元素下标范围 < high 的数组引用, 所以叫做切片,就是数组中切了一块数据。
例子:
primes := [6]int{2, 3, 5, 7, 11, 13}
// 定义一个切片s, 通过切片语法,从primes数组中,切割了数组下标从1到3,3个元素
// 因为切片语法要求要小于4,所以不包括4
var s []int = primes[1:4]
// 打印切片s,引用的数组
fmt.Println(s)
输出:
[3 5 7]
提示:切片只是引用数组,所以效率非常高,例如在函数传参的时候,使用切片传递数组参数,不会复制数组。
例子2:
// 直接创建一个切片,底层会定义一个数组,由这个切片引用
r := []bool{true, false, true, true, false, true}
fmt.Println(r)
切片的定义和初始化跟数组非常类似,区别就是没有指定数组大小。
5.2. 切片语法应用
切片语法应用,可以根据需要,忽略切片的开始位置,或者结束位置,甚至全部忽略。
有下面4中情况:
// 切割 0 <= 下标范围 < 10 范围的元素
a[0:10]
// 忽略开始位置,代表从0开始,这里的含义为:切割 0 <= 下标范围 < 10 范围的元素
a[:10]
// 忽略结束位置,代表结束位置等于数组大小,这里含义为:切割 0 <= 下标范围 < len(a) 范围的元素
a[0:]
// 忽略开始和结束位置,代表切割所有数据,相当于引用整个数组
a[:]
例子:
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
s = s[1:4]
fmt.Println(s)
// 注意,这里是从上面切片中,定义一个新的切片
s = s[:2]
fmt.Println(s)
// 注意,这里是从上面切片中,定义一个新的切片
s = s[1:]
fmt.Println(s)
}
输出:
[3 5 7]
[3 5]
[5]
提示:可以从数组中定义切片,也可以从切片中定义新的切片。
5.3. 切片的大小和容量
- 切片大小指的是切片包含多少个元素
- 切片容量指的是切片引用的数组大小
通过len函数可以获取切片大小
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println(len(s))
通过cap函数,获取切片容量
fmt.Println(cap(s))
5.4. 切片默认值
切片如果没有初始化,默认值是nil
var s []int
if s == nil {
fmt.Println("nil!")
}
5.5. 通过make创建切片
创建int类型切片,包含5个元素
a := make([]int, 5) // len(a)=5
创建int类型切片,包含0个元素,切片容量为5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
提示:如果可以提前预估数组大小,建议根据预估的大小,创建切片,避免切片动态调整数组大小带来的数据拷贝的性能消耗。
5.6. 向切片添加元素
我们可以通过append函数,向切片尾部添加元素。
例子:
package main
import "fmt"
func main() {
var s []int
printSlice(s)
// 将0增加到切片尾部
s = append(s, 0)
printSlice(s)
// 将1增加到切片尾部
s = append(s, 1)
printSlice(s)
// append函数支持一次性添加多个元素
// 将2, 3, 4添加到切片尾部
s = append(s, 2, 3, 4)
printSlice(s)
}
// 打印切片数据
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
输出:
len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]
5.7. 如何读写切片元素
切片的读写操作跟数组一样,通过下标引用即可。
需要注意的是,因为切片底层引用的是数组,如果多个切片引用同一个数组,修改其中一个切片的元素,会影响关联的所有切片。
例子:
package main
import "fmt"
func main() {
names := [4]string{
"John",
"Paul",
"George",
"Ringo",
}
fmt.Println(names)
// 创建a,b两个切片
a := names[0:2]
b := names[1:3]
fmt.Println(a, b)
// 修改切片b的第一个元素
b[0] = "XXX"
fmt.Println(a, b)
fmt.Println(names)
}
输出:
[John Paul George Ringo]
[John Paul] [Paul George]
[John XXX] [XXX George]
[John XXX George Ringo]
5.8. 遍历切片
通过range关键词结合for语句遍历切片
例子:
package main
import "fmt"
// 定义切片
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
// range 切片变量名
for i, v := range pow {
// 这里循环的时候定义了i,v两个变量,i 代表切片下标,v 代表对应的元素值
fmt.Printf("2**%d = %d\n", i, v)
}
}
当然我们可以忽略下标或者元素值,例子:
// 忽略元素值
for i, _ := range pow
// 忽略下标
for _, value := range pow
// 如果只写一个变量,则忽略元素值
for i := range pow