数组是切片和映射的基础数据结构。
使用数组(array)
数组的写法如下:
var 数组变量名 [元素数量] T
数组一旦声明,它存储的数据类型和数组长度便都不能修改。数组长度最大为2GB。
声明数组并给它赋值的程序清单如下:
package main
import(
"fmt"
)
func main(){
var cheeses [2]string
cheeses[0]="Mariolles"
cheeses[1]="Époisses de Bourgogne"
fmt.Println(cheeses[0])
fmt.Println(cheeses[1])
fmt.Println(cheeses)
}
运行结果如下:
Mariolles
Époisses de Bourgogne
[Mariolles Époisses de Bourgogne]
访问元素,代码如下:
package main
import (
"fmt"
)
func main() {
arr := [5]int{1, 2, 3, 4}
for i := 0; i < len(arr); i++ {
fmt.Printf("Array at index %d is %d\n", i, arr[i])
}
}
运行结果如下:
Array at index 0 is 1
Array at index 1 is 2
Array at index 2 is 3
Array at index 3 is 4
Array at index 4 is 0
代码如下:
package main
import (
"fmt"
)
func main() {
var arr1 = new([5]int)
arr := arr1
arr1[2] = 100
fmt.Println(arr1[2], arr[2])
var arr2 [5]int
newarr := arr2
arr2[2] = 100
fmt.Println(arr2[2], newarr[2])
}
运行结果如下:
100 100
100 0
new([5]int)创建的是数组指针。
使用切片(slice)
切片类似于数组,但不同于数组的是,在切片中可以添加和删除元素,也可以复制切片中的元素。
一种创建切片的方法是使用内置的make()函数。当使用make()时,需要传入一个参数,指定切片的长度。不允许创建容量小于底层数组长度的切片。
切片的初始化格式是:
var slice1 []type=arr1[start:end]
表示slice1是由数组arr1从start索引到end-1索引之间的元素构成的子集。
切片也可以用类似数组的方式初始化:
var x =[] int {2,3,5,7,11}
可以使用make()函数来创建一个切片:
var slice1 []type=make([]type,len,cap)
也可以简写为slice1:=make([]type,len),len是数组的长度并且也是slice的初始长度。cap是容量,是个可选参数。
从数组或者切片中生成一具新的切片,表达式:
a[low:high:max]
max-low的结果表示容量,hight-low的结果表示长度。例如:
a:=[5]int{1,2,3,4,5}
t:=a[1:3:5]
在切片中添加元素的程序清单如下:
package main
import(
"fmt"
)
func main(){
var cheeses = make([]string,2)
cheeses[0]="Mariolles"
cheeses[1]="Époisses de Bourgogne"
cheeses=append(cheeses,"Camembert")
fmt.Println(cheeses[2])
}
Go语言提供了内置函数append,能增大切片的长度。
运行结果如下:
Camembert
在切片中末尾添加多个元素的程序清单如下:
package main
import(
"fmt"
)
func main(){
var cheeses = make([]string,2)
cheeses[0]="Mariolles"
cheeses[1]="Époisses de Bourgogne"
cheeses=append(cheeses,"Camembert","Reblochon","Picodon")
fmt.Println(cheeses)
}
运行结果如下:
[Mariolles Époisses de Bourgogne Camembert Reblochon Picodon]
在切片中删除元素的程序清单如下:(没明白append。)
package main
import(
"fmt"
)
func main(){
var cheeses = make([]string,3)
cheeses[0]="Mariolles"
cheeses[1]="Époisses de Bourgogne"
cheeses[2]="Camembert"
fmt.Println(len(cheeses))
fmt.Println(cheeses)
cheeses=append(cheeses[:2],cheeses[2+1:]...)
fmt.Println(len(cheeses))
fmt.Println(cheeses)
}
要从切片中删除元素,也可使用内置函数append。
运行结果如下:
3
[Mariolles Époisses de Bourgogne Camembert]
2
[Mariolles Époisses de Bourgogne]
复制切片中的元素的程序清单如下:
package main
import(
"fmt"
)
func main(){
var cheeses = make([]string,2)
cheeses[0]="Mariolles"
cheeses[1]="Époisses de Bourgogne"
var smellyCheeses=make([]string,2)
copy(smellyCheeses,cheeses)
fmt.Println(smellyCheeses)
}
要复制切片的全部或部分元素,可使用内置函数copy。在复制切片中的元素前,必须再声明一个类型与该切片相同的切片。
运行结果如下:
[Mariolles Époisses de Bourgogne]
截取子切片的代码如下:
package main
import(
"fmt"
)
func main(){
var ary []int
ary=make([]int,3)
ary[0]=2
ary[1]=4
ary[2]=8
fmt.Println(ary)
var subAry []int
subAry=ary[1:3]
fmt.Println(subAry)
}
运行结果如下:
[2 4 8]
[4 8]
遍历切片:
代码如下:
package main
import (
"fmt"
)
func main() {
slice := []int{10, 20, 30, 40}
for index, value := range slice {
fmt.Printf("index : %d value: %d\n", index, value)
}
}
运行结果如下:
index : 0 value: 10
index : 1 value: 20
index : 2 value: 30
index : 3 value: 40
迭代切片时,range会返回两个值。第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值的一份副本。
代码如下:
package main
import (
"fmt"
)
func main() {
slice := []int{10, 20, 30, 40}
for index, value := range slice {
fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X\n", value, &value, &slice[index])
}
}
运行结果如下:
Value: 10 Value-Addr: C000134008 ElemAddr: C000136020
Value: 20 Value-Addr: C000134008 ElemAddr: C000136028
Value: 30 Value-Addr: C000134008 ElemAddr: C000136030
Value: 40 Value-Addr: C000134008 ElemAddr: C000136038
代码如下:
package main
import (
"fmt"
)
func main() {
slice := []int{10, 20, 30, 40}
for _, value := range slice {
fmt.Printf("Valuue:%d\n", value)
}
}
运行结果:
Valuue:10
Valuue:20
Valuue:30
Valuue:40
代码如下:
package main
import (
"fmt"
)
func main() {
slice := []int{10, 20, 30, 40}
for index := 2; index < len(slice); index++ {
fmt.Printf("Index:%d Value:%d\n", index, slice[index])
}
}
运行结果:
Index:2 Value:30
Index:3 Value:40
限制容量:
在创建切片,可以使用第三个索引选项。第三个索引可以用来控制新切片的容量,其目的并不是要增加容量,而是要限制容量。
代码如下:使用...运算符,可以将一个切片的所有元素追加到另一个切片。
package main
import (
"fmt"
)
func main() {
s1 := []int{1, 2}
s2 := []int{3, 4}
fmt.Printf("%v\n", append(s1, s2...))
}
运行结果如下:
[1 2 3 4]
声明切片:
切片类型声明格式如下:
var name []T
name表示切片类型的变量名。
T表示切片类型对应的元素类型。
代码如下:
package main
import (
"fmt"
)
func main() {
var strList []string
var numList []int
var numListEmpty = []int{}
fmt.Println(strList, numList, numListEmpty)
fmt.Println(len(strList), len(numList), len(numListEmpty))
fmt.Println(strList == nil)
fmt.Println(numList == nil)
fmt.Println(numListEmpty == nil)
}
运行结果如下:
[] [] []
0 0 0
true
true
false
使用make()函数构造切片
格式如下:
make ( []T, size, cap)
T:切片的元素类型。
size:就是为这个类型分配多少个元素。长度
cap:预分配的元素数量,这个值设定后不影响size,只是能提前分配空间,降低多次分配空间造成的性能问题。容量
使用append()函数为切片添加元素:
代码如下:
package main
import (
"fmt"
)
func main() {
var numbers []int
for i := 0; i < 10; i++ {
numbers = append(numbers, i)
fmt.Printf("len: %d cap:: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
}
}
运行结果如下:
len: 1 cap:: 1 pointer: 0xc00012a008
len: 2 cap:: 2 pointer: 0xc00012a030
len: 3 cap:: 4 pointer: 0xc00012c040
len: 4 cap:: 4 pointer: 0xc00012c040
len: 5 cap:: 8 pointer: 0xc000130040
len: 6 cap:: 8 pointer: 0xc000130040
len: 7 cap:: 8 pointer: 0xc000130040
len: 8 cap:: 8 pointer: 0xc000130040
len: 9 cap:: 16 pointer: 0xc000132000
len: 10 cap:: 16 pointer: 0xc000132000
代码如下:
package main
import (
"fmt"
)
func main() {
var car []string
car = append(car, "OldDriver")
car = append(car, "Ice", "Sniper", "Monk")
team := []string{"Pig", "Flyingcake", "Chicken"}
car = append(car, team...)
fmt.Println(car)
}
运行结果如下:
[OldDriver Ice Sniper Monk Pig Flyingcake Chicken]
复制切片元素到另一个切片:
格式如下:
copy(destSlice, srcSlice []T) int
srcSlice为数据来源切片。
destSlice为复制的目标。
copy (t,o)
t是to目的。o是origin起点。
代码如下:
package main
import (
"fmt"
)
func main() {
const elementCount = 1000
srcData := make([]int, elementCount)
for i := 0; i < elementCount; i++ {
srcData[i] = i
}
refData := srcData
copyData := make([]int, elementCount)
copy(copyData, srcData)
srcData[0] = 999
fmt.Println(refData[0])
fmt.Println(copyData[0], copyData[elementCount-1])
copy(copyData, srcData[4:6])
for i := 0; i < 5; i++ {
fmt.Printf("%d ", copyData[i])
}
}
运行结果如下:
999
0 999
4 5 2 3 4
从切片中删除元素:
代码如下:
package main
import (
"fmt"
)
func main() {
seq := []string{"a", "b", "c", "d", "e"}
index := 2
fmt.Println(seq[:index], seq[index+1:])
seq = append(seq[:index], seq[index+1:]...)
fmt.Println(seq)
}
运行结果如下:
[a b] [d e]
[a b d e]
切片操作
添加切片b
a=append(a,b...)
复制
b=make([]T,len(a))
copy(b,a)
删除[i:j]
a=append(a[:i],a[j:]...)
删除第i个元素
a=append(a[:i],a[i+1:]...)
扩展j个空元素
a=append(a, make([]T,j)...)
插入j个空元素
a=append(a[:i],append(make([]T,j), a[i:]...)...)
插入元素x
a=append(a[:i],append([]T(x},a[i:]...)...)
插入切片b
a=append(a[:i],append(b,a[i:]...)...)
弹出最后一个元素
x, a=a[len(a)-1],a[:len(a)-1]
压入x
a=append(a,x)
代码如下:
package main
import "fmt"
func main() {
sli := make([]int, 5, 10)
fmt.Printf("切片sli长度和容量:%d, %d\n", len(sli), cap(sli))
fmt.Println(sli)
newsli := sli[:cap(sli)]
fmt.Println(newsli)
var x = []int{2, 3, 5, 7, 11}
fmt.Printf("切片x长度和容量:%d, %d\n", len(x), cap(x))
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5] // a[low : high : max] max-low的结果表示容量 high-low为长度
fmt.Printf("切片t长度和容量:%d, %d\n", len(t), cap(t))
// fmt.Println(t[2]) // panic ,索引不能超过切片的长度
}
运行结果如下:
切片sli长度和容量:5, 10
[0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
切片x长度和容量:5, 5
切片t长度和容量:2, 4
切片重组(reslice)
slice1:=make([]type,length,capacity)
代码如下:
package main
import "fmt"
func get() []byte {
raw := make([]byte, 10000)
fmt.Println(len(raw), cap(raw), &raw[0]) // prints: 10000 10000 <byte_addr_x>
return raw[:3] // 10000个字节实际只需要引用3个,其他空间浪费
}
func main() {
data := get()
fmt.Println(len(data), cap(data), &data[0]) // prints: 3 10000 <byte_addr_x>
}
运行结果如下:
10000 10000 0xc000102000
3 10000 0xc000102000
代码如下:
package main
import "fmt"
func get() []byte {
raw := make([]byte, 10000)
fmt.Println(len(raw), cap(raw), &raw[0]) // 显示: 10000 10000 数组首字节地址
res := make([]byte, 3)
copy(res, raw[:3]) // 利用copy 函数复制,raw 可被GC释放
return res
}
func main() {
data := get()
fmt.Println(len(data), cap(data), &data[0]) // 显示: 3 3 数组首字节地址
}
运行结果如下:
10000 10000 0xc000102000
3 3 0xc0000140ca
陈旧的切片(Stale Slices)
代码如下:
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
fmt.Println(len(s1), cap(s1), s1) // 输出 3 3 [1 2 3]
s2 := s1[1:]
fmt.Println(len(s2), cap(s2), s2) // 输出 2 2 [2 3]
for i := range s2 {
s2[i] += 20
}
// s2的修改会影响到数组数据,s1输出新数据
fmt.Println(s1) // 输出 [1 22 23]
fmt.Println(s2) // 输出 [22 23]
s2 = append(s2, 4) // append s2容量为2,这个操作导致了slice s2扩容,会生成新的底层数组。
for i := range s2 {
s2[i] += 10
}
// s1 的数据现在是老数据,而s2扩容了,复制数据到了新数组,他们的底层数组已经不是同一个了。
fmt.Println(len(s1), cap(s1), s1) // 输出3 3 [1 22 23]
fmt.Println(len(s2), cap(s2), s2) // 输出3 4 [32 33 14]
}
运行结果如下:
3 3 [1 2 3]
2 2 [2 3]
[1 22 23]
[22 23]
3 3 [1 22 23]
3 4 [32 33 14]
使用映射(map)
数组和切片是可通过索引值访问的元素集合,而映射是通过键来访问的无序元素编组。
字典(map)是一种键-值对的无序集合,一组称为值元素value,另一组称为唯一键索引Key。
未初始化字典的值为nil。字典是引用类型。
简单地说,映射可视为键-值对集合。
var map1 map[keytype]valuetype
[keytype]和valuetype之间允许有空格。
在声明的时候不老百姓知道字典的长度,字典是可以动态增长。
map [KeyType] ValueType
KeyType为键类型
ValueType是键对应的值类型。
map的遍历过程使用for range循环完成,代码如下:
scene:=make(map[string]int)
var map1=make(map[keytype]valuetype)
使用delete()内建函数从map中删除一组键值对,delete()函数的格式如下:
delete(map, 键)
映射的代码如下:
package main
import(
"fmt"
)
func main(){
var map1 map[string]string
map1=make(map[string]string,3)
map1["Name"]="张三"
map1["Gender"]="男"
fmt.Println(map1)
map2:=make(map[int]string,3)
map2[1]="李四"
map2[5]="女"
fmt.Println(map2)
}
运行结果如下:
map[Gender:男 Name:张三]
map[1:李四 5:女]
在映射中添加元素的程序清单如下:
package main
import(
"fmt"
)
func main(){
var players=make(map[string]int)
players["cook"]=32
players["bairstow"]=27
players["stokes"]=26
fmt.Println(players["cook"])
fmt.Println(players["bairstow"])
}
运行结果如下:
32
27
从映射中删除元素的程序清单如下:
package main
import(
"fmt"
)
func main(){
var players=make(map[string]int)
players["cook"]=32
players["bairstow"]=27
players["stokes"]=26
delete(players,"cook")
fmt.Println(players)
}
要从映射中删除元素,可使用内置函数delete。
运行结果如下:
map[bairstow:27 stokes:26]
代码如下:
package main
import (
"fmt"
)
func main() {
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
}
运行结果如下:
Key: AliceBlue Value: #f0f8ff
Key: Coral Value: #ff7F50
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
将映射传递给函数:
代码如下:
package main
import (
"fmt"
)
func main() {
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
fmt.Println("")
removeColor(colors, "Coral")
for key, value := range colors {
fmt.Printf("Key:%s Value:%s\n", key, value)
}
}
func removeColor(colors map[string]string, key string) {
delete(colors, key)
}
运行结果如下:
Key: AliceBlue Value: #f0f8ff
Key: Coral Value: #ff7F50
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
Key:AliceBlue Value:#f0f8ff
Key:DarkGray Value:#a9a9a9
Key:ForestGreen Value:#228b22
各国的GDP,用切片的代码如下:
package main
import "fmt"
const (
China int = iota
USA
Japan
Total
)
type GDP struct {
k string
v float64
}
func main() {
GDPof := make([]GDP, Total)
GDPof[China] = GDP{"China", 5.92}
GDPof[USA] = GDP{"USA", 14.58}
GDPof[Japan] = GDP{"Japan", 5.45}
for k, v := range GDPof {
fmt.Printf("%d. %s:%g\n", k, v.k, v.v)
}
}
运行结果如下:
0. China:5.92
1. USA:14.58
2. Japan:5.45
各国的GDP,用映射的代码如下:
package main
import "fmt"
func main() {
GDPof := map[string]float64{
"USA": 14.58,
"Japan": 5.45,
}
GDPof["China"] = 5.92
for k, v := range GDPof {
fmt.Printf("%s:%g\n", k, v)
}
}
运行结果如下:
Japan:5.45
China:5.92
USA:14.58
代码如下:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
type Lockmap struct {
sync.Mutex
m map[string]int
}
var lockmap = make([]Lockmap, 10)
func init() {
for i := range lockmap {
lockmap[i].m = make(map[string]int)
}
}
func counter(s string) {
i := int(s[0] - '0')
time.Sleep(time.Duration(i) * time.Millisecond)
lockmap[i].Lock()
defer lockmap[i].Unlock()
lockmap[i].m[s]++
}
func main() {
for i := 0; i < 20; i++ {
r := fmt.Sprintf("%d", rand.Uint32())
go counter(r)
}
time.Sleep(time.Second)
for i := range lockmap {
for k, v := range lockmap[i].m {
fmt.Printf("[%d] %s = %d\n", i, k, v)
}
}
}
运行结果如下:
[1] 1823804162 = 1
[1] 1215622422 = 1
[1] 1879968118 = 1
[1] 1292406600 = 1
[1] 1634910179 = 1
[1] 1366049456 = 1
[1] 1258862891 = 1
[2] 2854263694 = 1
[2] 2916646474 = 1
[2] 2212821389 = 1
[2] 281908850 = 1
[2] 2949882636 = 1
[2] 2013866549 = 1
[2] 2596996162 = 1
[3] 3494557023 = 1
[4] 416480912 = 1
[4] 4039455774 = 1
[6] 672245080 = 1
[9] 920256325 = 1
[9] 938678213 = 1
并发安全的sync.map,代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
var scene sync.Map
scene.Store("greece", 97)
scene.Store("london", 100)
scene.Store("egypt", 200)
fmt.Println(scene.Load("london"))
scene.Delete("london")
scene.Range(func(k, v interface{}) bool {
fmt.Println("iterate:", k, v)
return true
})
}
运行结果如下:
100 true
iterate: greece 97
iterate: egypt 200
代码如下:
package main
import "fmt"
func main() {
data := []int{1, 2, 3}
for _, v := range data {
v *= 10 // 通常数据项不会改变
}
fmt.Println("data:", data) // 程序输出: [1 2 3]
}
运行结果如下:
data: [1 2 3]
代码如下:
package main
import "fmt"
func main() {
data := []int{1, 2, 3}
for i, _ := range data {
data[i] *= 10
}
fmt.Println("data:", data) // 程序输出 data: [10 20 30]
}
运行结果如下:
data: [10 20 30]