1. array
array就是数组,我们可以通过如下方式定义一个数组并对数组中的元素进行赋值
var arr [n]type // 定义一个大小为n,类型为type的数组
arr[0] = xx // 对数组中的元素进行赋值
其中[n]type中,n表示数组的长度,type表示数组的类型,由于长度也是数组类型的一部分,所以[3]int和[4]int是两种不同的类型,数组也就不能改变长度
简单例子:
package main
import (
"fmt"
)
func main() {
var arr [4]int
arr[0] = 1
fmt.Print(arr)
}
这里我们定义了一个长度为4的Int数组,并将数组中的第一个元素赋值为1,最后打印出该数组
可以看到,除了第一个元素外,其它三个元素都使用了默认值0
同样的,我们可以使用 := 来简化数组的声明,例子如下
package main
import (
"fmt"
)
func main() {
arr1 := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
arr2 := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
arr3 := [...]int{1, 2, 3, 4, 5} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
arr4 := [2][4]int{[4]int{3, 3, 3, 3}, [4]int{4, 4, 4, 4}}
fmt.Println(arr4)
// 上面的声明可以简化,直接忽略内部的类型
arr5 := [2][4]int{{1, 1, 1, 1}, {2, 2, 2, 2}}
fmt.Println(arr5)
}
我们可以通过以上方便的声明不同长度,不同类型的数组(包括二维数组),运行结果如下:
2. slice
很多时候,我们并不知道具体要用多大的数组,无法确定数组的长度,因此就有了slice(切片)的概念,slice可以理解成一个动态的数组,但它又不是一个真正意义上的动态数组,而是一个引用类型,总是指向一个array,它的声明和array类似,只是省略了长度
var sli []int
slice可以定义一个空slice并逐渐追加元素或者,从一个已经定义好的数组或者slice中再次声明(通过array[i:j]获取,包括从i到j-1元素,其中i和j都可以省略,这里用法和python一致),例子如下:
package main
import (
"fmt"
)
func main() {
// 定义一个空slice并逐渐追加元素
var aSlice []int
aSlice = append(aSlice, 0)
fmt.Println(aSlice)
// 从一个已经定义好的数组或者slice中再次声明
byteArray := [10]byte{'a', 'b', 'c'}
fmt.Println(byteArray)
byteS1 := byteArray[1:5]
fmt.Println(byteS1)
byteS2 := byteS1[1:3]
fmt.Println(byteS2)
}
运行结果如下:
由于slice是引用类型,当同时有多个slice指向同一个数组时,其中一个slice的修改会影响到其它的slice,比如,在上面的例子中增加如下代码
byteS2[0] = 10
fmt.Println("afte modify")
fmt.Println(byteS1)
fmt.Println(byteS2)
输出结果后,我们发现,两个slice的值都发生了变化
slice有一些有用的内置函数
len | 返回切片的长度 |
append | 往切片中追加元素,存在多个切片指向同一个数组时,更新一个切片可能会更新其它切片 |
cap | 返回切片的最大容量 |
copy | 从src切片复制元素到到目标dst,返回复制元素的个数(为src和dst切片长度的最小值) |
使用这些内置函数的例子如下:
package main
import (
"fmt"
)
func main() {
// 从一个已经定义好的数组或者slice中再次声明
byteArray := [10]byte{'a', 'b', 'c'}
fmt.Println(byteArray)
byteS1 := byteArray[1:5]
fmt.Println(byteS1)
byteS2 := []byte{'v', 'w'}
num := copy(byteS2, byteS1)
fmt.Printf("byteS2 = %v, num = %v\n", byteS2, num)
byteS2 = append(byteS2, 'f')
fmt.Println(byteS2)
fmt.Printf("the len of byteS2 = %v, and the cap of byteS2 = %v\n", len(byteS2), cap(byteS2))
}
在代码中,我们定义了两个切片,然后执行拷贝,添加,计算长度,容量操作,结果如下:
3. map
map,字典,键值对的形式,格式为map[keyType]valueType,map的读取和设置和slice类似,只不过slice的下标只能是int类型,而map可以是多种类型,可以是string, int, 或者其它定义了==与!=操作的类型
一个简单例子如下:
package main
import "fmt"
func main() {
var m map[string]int
m = make(map[string]int)
m["a"] = 1
m["b"] = 2
fmt.Printf("m[a] = %v, m[b] = %v", m["a"], m["b"])
}
以上代码定义了一个string到int的map,这种定义方式需要在使用之前使用make进行初始化,初始化后,给map中元素赋值,赋值之后并打印出相应的元素
使用map的时候需要注意以下几点
- map是无序的,不能通过index获取,只能通过key获取
- map的长度不固定,这点和slice一致,也就是说,map也是引用类型(两个map指向同一个底层,一个map的改变也会影响到另一个map)
- 内置函数len同样适用于map,返回的是map的key的个数
- map的值可以很方便修改,map[key] = newValue即可
- map和其它基本类型不同,它不是线程安全的,如果存在多个go-routine读取时,需要使用mutex lock机制
除了以上几点,map也存在简单初始化,判断key是否存在,以及删除key的操作,例子如下:
package main
import "fmt"
func main() {
m := map[string]string{
"myname": "lilei",
"teacher": "wang",
}
value, ok := m["myname"]
if !ok {
fmt.Println("key history not exists")
} else {
fmt.Printf("the value of key myname is %v\n", value)
}
delete(m, "myname")
fmt.Println(m)
}
这里初始化了一个map,查询map中的值并删除,结果如下: