Array
array是固定长度的数组,使用前必须确定数组长度。所以动态数组slice更通用。数组是一个值类型 ,可以直接用它来进行赋值操作,但是数组的类型同时包括数组的长度和元素类型 ,数组类型完全相同才能相互赋值。
array 声明和初始化
//声明一个长度为10的int类型的数组
var array0 [10]int
//声明并初始化
var array1 [5]int = [5]int{1,2,3,4,5}
//声明并未索引(0开始)为1和4位置指定元素初始化
//剩余位置为0
var array2 [5]int = [5]int{1:1,4:5}
//声明并初始化
array3 :=[5]int{}
//go编译器推导数组长度2
array4 :=[...]{1,2}
//二维数组
var array5 [2][2]int
array的使用
使用[]操作符
go
array :=[2]int{1,2}
array[1] = 1
使用range关键字
for i, v := range a {
fmt.Printf("d% %d\n", i, v)
}
注意: 迭代过程中v是索引位置值的拷贝,因此变量v的地址每次循环都是相同的。 如果要得到每个元素的真实地址可以用&a[i]
特点总结
golang
中的数组是值类型,也就是说,如果你将一个数组赋值给另外一个数组,那么,实际上就是整个数组拷贝了一份。- 如果
golang
中的数组作为函数的参数,那么实际传递的参数是一份数组的拷贝,而不是数组的指针。 array
的长度也是Type
的一部分,这样就说明[10]int
和[20]int
是不一样的。
slice
slice基于数组构建,但是提供更强的功能。slice是引用类型,作为函数参数传递时,可以直接在函数内部修改,函数外部是可见的。 slice可以认为是动态数组。
声明和初始化
内建函数make创建slice
//指定slice长度,这是容量默认为长度
var slice1 = make([]int,5)
//同时指定长度3和容量5
var slice2 = make([]int,3,5)
//长度不允许大于容量
直接赋值的方式创建
// 创建一个字符串 slice
// 长度和容量都是 5
slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 创建一个字符串 slice
// 初始化一个有100个元素的空的字符串 slice
slice := []string{99: ""}
基于数组创建
months := [...]string{1: "January", /* ... */, 12:"December"}
Q2 := months[4:7] // ["April", "May", "June"], len=3, cap=9
summer := months[6:9] // ["June", "July", "August"], len=3, cap=7
empty slice和nil slice
//创建nil slice
var slice []int
//创建empty slice
slice1 :=make([]int,0)
//or
slice2 :=[]int{}
fmt.Println(slice1==nil, slice2==nil, slice3==nil)
//结果ture, false, false
- 二者是非常容易混淆的概念,原因是长度和容量都是0。区别在于nil slice没有底层数组的,nil slice被看作是未被初始化的slice。
- nil slice通常用于表示一个并不存在的slice,比如返回slice的函数中发生异常的时候。
- empty slice通常用于表示一个空集合,比如一个数据库查询返回0个结果。
- 判断slice是否为空时,应该用len(s) == 0。
append()和copy()函数
以下代码描述从拷贝切片的copy方法和向切片追加新元素的append方法的使用:
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
执行结果:
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
写出以下代码的执行结果:
go
// 创建一个长度和容量都为5的 slice
slice := []int{1, 2, 3, 4, 5}
// 创建一个新的 slice
newSlice := slice[1:3]
// 为新的 slice append 一个值
newSlice = append(newSlice, 6)
fmt.Println("slice", slice, "newSlice", newSlice)
执行结果:
slice [1 2 3 6 5] newSlice [2 3 6]
注: 重叠的slice会共享底层的数组,所以在未超过容量的条件下,append的新值在改变newSlice的值的同时也改变了slice的值。
对于容量不足的slice,append则会创建新的底层数组,拷贝已存在的值和将要被附加的新值。如果元素个数小于1000,容量会是现在元素的2倍;如果元素个数超过1000,容量则会以1.25倍来增长。
特点总结:
slice
是一个引用类型,是一个动态的指向数组切片的指针。slice
是一个不定长的,总是指向底层的数组array
的数据结构。
map
map是一种存放无序键值对的集合。map可以通过key来快速检索数据,key类似于索引,指向数据的值。可以像迭代数组和切片那样迭代它,不过无法决定map的返回顺序,因为map是用hash表来实现的。
声明和初始化
//声明变量,默认map是nil
var m map[KeyType] ValueType
//使用make声明并初始化map
m := make(map[KeyType]ValueType)
// 初始化 + 赋值一体化
m := map[string]string{
"a": "aa",
"b": "bb",
}
- slice、function和包含slice的struct类型不能作为map的KeyType。
- ValueType可以是任意类型,甚至是另一个map。
- 此处如果不初始化map,会创建一个nil map,往nil map中存储值会引起程序panic。
map使用
delete()函数
delete()函数用于删除集合的元素,参数为map和其对应的key。删除一个在map中不存在的key函数不会返回错误。具体使用如下:
m := map[string]int {
"xiaoming": 14,
"zhangqiang": 25,
"wangxiaofei": 21,
}
delete(m, "xiaoming")
查找键值是否存在
m := map[string]int {
"xiaoming": 14,
"zhangqiang": 25,
"wangxiaofei": 21,
}
if v, ok := m["xiaoming"]; ok {
fmt.Println(v)
} else {
fmt.Println("key not found")
}
遍历map
m := map[string]int {
"xiaoming": 14,
"zhangqiang": 25,
"wangxiaofei": 21,
}
for i, v := range m {
fmt.Printf("index:%s, value:%d", i, v)
}
特点总结:
- map是go语言的内置引用类型,所以多个map指向同一个底层的情况下,一个值发生变化,全部发生变。
- map是无序的,每次迭代的顺序都是不确定的。
- map只有len没有cap。
- map不是线程安全的,在多个go routine中使用的时候需要加锁。