go语言基础(一):数组、字符串、切片
今天学习go语言里面的数组、字符串和切片。
在go语言中,数组、字符串和切片有着相似的数据结构,是三种联系紧密的数据类型。
数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。数组的长度是数组类型的组成部分。因为数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类型。
在go语言中,我们可以通过一下方式创建数组:
var a [3]int // 定义长度为3的int型数组, 元素全部为0
var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6
在go语言中数组是一种值类型,在进行数组赋值和参数传递的时候是整体的值复制,如果数组较大的话,有可能影响程序的运行性能。一个数组变量即表示整个数组,不是表示指向数组第一个元素的指针。
为了避免数组的复制产生较大的性能开销,我们可以传递数组的指针,但数组的指针并不是数组。
array := [3]int {1,2,3} // 创建一个元素为1,2,3的整型数组
b := &array // 将array的地址赋值给变量b,b为指向array数组的指针
fmt.Println(b[0]) // 1
fmt.Println(b[1]) // 2
fmt.Println(b[2]) // 3
数组访问
数组中的元素可以通过下标的方式,如array[index]。除此之外,go语言还可以通过for range的方式遍历数组。
for index, value := range array {
fmt.Println(index, value)
}
修改元素
通过array[index] = value 的方式,可以向数组中添加元素或者修改元素。
获取数组长度
通过len(array) 方法可以获取当前数组的长度,cap(array)可以获取数组最大长度,对于数组类型来说,len和cap函数返回的结果始终是一样的,都是对应数组类型的长度。
切片(slice)
由于数组不够灵活,我们在实际中使用的比较少,go中又引入了切片,也就是动态数组。
在go语言中,切片的结构定义为:
// reflect.SliceHeader
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
包含一个指向底层数组的变量、表示切片长度的变量和表示切片容量的变量。
切片的定义方式:
var (
a []int // nil切片, 和 nil 相等, 一般用来表示一个不存在的切片
b = []int{} // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
c = []int{1, 2, 3} // 有3个元素的切片, len和cap都为3
d = c[:2] // 有2个元素的切片, len为2, cap为3
e = c[0:2:cap(c)] // 有2个元素的切片, len为2, cap为3
f = c[:0] // 有0个元素的切片, len为0, cap为3
g = make([]int, 3) // 有3个元素的切片, len和cap都为3
h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)
同数组一样,切片能够使用len()、cap()方法获取切片的长度和容量。
与数组不同的是,切片中的长度和容量是可以不相等的,当切片已满时,长度等于容量,当切片未满时,长度小于容量,此时可以通过append()方法,向切片中添加元素:
- 向切片末尾追加元素:
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
- 向切片开头添加元素:
var a = []int{1,2,3}
a = append([]int{0}, a...) // 在开头添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片
- 向切片中间添加元素:
var a []int
a = append(a[:i], append([]int{x}, a[i:]...)...) // 在第i个位置插入x
a = append(a[:i], append([]int{1,2,3}, a[i:]...)...) // 在第i个位置插入切片
在切片中删除元素,可以通过以下方式实现:
- 删除尾部元素:
a = []int{1, 2, 3}
a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素
- 删除头部元素:
a = []int{1, 2, 3}
a = a[1:] // 删除开头1个元素
a = a[N:] // 删除开头N个元素
删除开头的元素也可以不移动数据指针,但是将后面的数据向开头移动。可以用append原地完成(所谓原地完成是指在原有的切片数据对应的内存区间内完成,不会导致内存空间结构的变化):
a = []int{1, 2, 3}
a = append(a[:0], a[1:]...) // 删除开头1个元素
a = append(a[:0], a[N:]...) // 删除开头N个元素
也可以用copy完成删除开头的元素:
a = []int{1, 2, 3}
a = a[:copy(a, a[1:])] // 删除开头1个元素
a = a[:copy(a, a[N:])] // 删除开头N个元素
- 删除中间元素:
a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
切片的遍历和数组类似:
for i := range a {
fmt.Printf("a[%d]: %d\n", i, a[i])
}
for i, v := range b {
fmt.Printf("b[%d]: %d\n", i, v)
}
for i := 0; i < len(c); i++ {
fmt.Printf("c[%d]: %d\n", i, c[i])
}
字符串
一个字符串是一个不可改变的字节序列,与数组不同的是,字符串的元素不可修改,是一个只读数组。