Go语言的数组、切片和映射

数组是切片和映射的基础数据结构。

使用数组(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]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值