数组和切片

数组是具有相同 唯一类型 的一组已编号且长度固定的数据项序列(这是一种同构的数据结构)。

数组的初始化

1
2
//只声明
var a [3]int
1
2
3
4
5
6
//声明并赋值
var b = [3]int{1,2,3}
c := [2]int{1,2}
//重新设置设置值
c[0] = 4

示例:

1
2
3
4
5
6
7
8
9
10
11
12
func TestArray(t *testing.T) {
var a [3]int
t.Log(len(a)) // 3
t.Log(a) //[0 0 0]

var b = [3]int{1,2,3}
t.Log(b) //[1 2 3]

b[0] = 10
b[1] = 20
t.Log(b) //[10 20 3]
}

数组的遍历

使用for 遍历数组 或for range 遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func TestArrayFor(t *testing.T) {
arr := [4]int{1,2,3,4}
//遍历1
for i:=0;i<len(arr);i++ {
t.Logf("index %d = value %d \n",i,arr[i])
}
t.Log("================================\n")
//遍历2
for index,data := range arr {
t.Logf("index %d = value %d \n",index,data)
}
}


数组的截取

将数组截取成小的数组,通过在数组中的索引的冒号的方式表示切割

arr2 := arr[2:3]

1
2
3
4
5
6
7
8
9
10
11
12
func TestArrayCut(t *testing.T) {
arr := [5]string{"你","好","啊","说","啥"}
//部分切
arrCut1 := arr[0:3]
t.Log(arrCut1) //[你 好 啊]
//全部切
arrCut2 := arr[:] //表示全部
t.Log(arrCut2) //[你 好 啊 说 啥]
//范围切
arrCut3 := arr[2:]
t.Log(arrCut3) //[啊 说 啥]
}

数组长度最大为 2GB

切片

数组的长度是固定的,所以在很多场景使用不太方便。
go中提供了切片这种数据类型,长度不固定,并且长度可自动扩容。
切片 (slice) 是对数组一个连续片段的引用,所以切片是一个引用类型。

切片对象内部指向的是一个固定的长度的数组的引用,一旦超过切面的最大容量,引用的数组扩容,既将数组的值拷贝到新数组,然后将引用再指向这个切片。

切片的相关方法
cap() 容量
len() 长度

切片是不能像数组那样通过 == 比较的。

切片的声明

  1. 不指定容量的声明

var s []int

  1. 指定容量的声明

通过make 函数来创建一个切片

slice := make([]type,length,capacity)

  1. 根据数组中创建

数组的切割方式获取的就是一个切片

slice := arr[2:5]

切片的相关方法

切片长度改变

sl = sl[0:len(sl)+1]

注意:改变的是长度而不是容量

1
2
3
4
5
6
7
8
9
func TestSlice(t *testing.T) {
s := make([]int,0,2)
t.Log(len(s)) //0
t.Log(cap(s)) //2
s = s[0:len(s)+1]
t.Log(len(s)) //1
t.Log(cap(s)) //2
}

切片的复制和追加

copy 完成切片的复制操作
append 向切片中追加元素

注意:append 不会在原来的值上修改,需要重新赋值给原切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func TestSlice(t *testing.T) {
s1 := []int{1,2,3,4}
s2 := make([]int,4)
//注意:第一个参数是dst
num := copy(s2,s1)
t.Logf("拷贝的数量 %d",num) // 拷贝的数量 4
t.Log(s2) //[1 2 3 4]

s2 = append(s2, 5)
s2 = append(s2,6)
//追加多个
s2 = append(s2,7,7,8)
t.Log(s2)
}

追加的一些操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//追加的操作
func TestAppend(t *testing.T) {
s1 := []int{1,2,3,4}
//对原切片去除一些元素
s2 := append(s1[:1],s1[3])
t.Log(s2)

//删除索引为 1 的元素
s3 := append(s1[:1],s1[2:]...)
t.Log(s3)

s5 := []int{1,2,3,4}
//指定位置插入元素
s5 = append(s5[:1],append([]int{10},s5[1:]...)...)
t.Log(s5) //[1 10 2 3 4]
}

切片的扩容机制

切片的引用的数组的容量是成倍扩容的

1
2
3
4
5
6
7
8
9
10
//追加的操作
func TestSpliceCap(t *testing.T) {
var s []int
t.Logf("len = %d cap = %d",len(s),cap(s))
for i := 0; i < 30; i++ {
s = append(s, i)
t.Logf("len = %d cap = %d",len(s),cap(s))
}
}

通过输出的结果来看,能够知道内部的数组是成倍扩容的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    type_test.go:259: len = 0 cap = 0
type_test.go:262: len = 1 cap = 1
type_test.go:262: len = 2 cap = 2
type_test.go:262: len = 3 cap = 4
type_test.go:262: len = 4 cap = 4
type_test.go:262: len = 5 cap = 8
type_test.go:262: len = 6 cap = 8
type_test.go:262: len = 7 cap = 8
type_test.go:262: len = 8 cap = 8
type_test.go:262: len = 9 cap = 16
type_test.go:262: len = 10 cap = 16
type_test.go:262: len = 11 cap = 16
type_test.go:262: len = 12 cap = 16
type_test.go:262: len = 13 cap = 16
type_test.go:262: len = 14 cap = 16
type_test.go:262: len = 15 cap = 16
type_test.go:262: len = 16 cap = 16
type_test.go:262: len = 17 cap = 32
type_test.go:262: len = 18 cap = 32
type_test.go:262: len = 19 cap = 32
type_test.go:262: len = 20 cap = 32
type_test.go:262: len = 21 cap = 32
type_test.go:262: len = 22 cap = 32
type_test.go:262: len = 23 cap = 32
type_test.go:262: len = 24 cap = 32
type_test.go:262: len = 25 cap = 32
type_test.go:262: len = 26 cap = 32
type_test.go:262: len = 27 cap = 32
type_test.go:262: len = 28 cap = 32
type_test.go:262: len = 29 cap = 32
type_test.go:262: len = 30 cap = 32
--- PASS: TestSpliceCap (0.00s)
PASS

Process finished with the exit code 0


数组和切片的主要区别在于切片可自动扩容。并且切片不能比较,数组可以比较.

Map

无序的键值对数据结构容器。

map初始化

声明map,只声明的map为nil 值

var map1 map[int]string

1
2
3
var map1 map[int]string
t.Log(map1 == nil) //true

声明并初始化,此时map可以正常赋值

1
2
3
4
map1 := map[int]string{}
map1[1] = "1"
map1[2] = "2"

初始值的时候指定值

1
2
map1 := map[int]string{1:"1",2:"2"}
t.Log(map1)

使用make来初始化 推荐

1
2
3
map1 := make(map[string]string)
map1["2"] = "2"
t.Log(map1)

指定容量初始化

可以在make的时候指定初始容量,但是 cap 并不能获取目前的容量大小。

1
2
map2 := make(map[string]string,10)
t.Logf("map2 len %d",len(map2))

基本操作

  • 向map中添加元素

map1[1] = 100

  • 删除元素

delete(map1,1)

  • 判断元素是否存在

data,ok := map1[2]

元素访问会有2个返回值一个是数据,一个是是否存在的标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
map1 := make(map[int]int)
//添加元素
map1[1] = 10
map1[2] = 20
t.Log(map1)
//覆盖元素
map1[1] = 100
t.Log(map1)
//删除元素
delete(map1,1)
t.Log(map1)
//判断元素是否存在
data,ok := map1[2]
if(ok){
t.Log(data)
}else{
t.Log("不存在")
}

map 的遍历

map的遍历只能使用for range来遍历

1
2
3
4
5
6
7
func TestMapFor(t *testing.T) {
map1 := map[string]string{"1":"一","2":"二"}
for k,v := range map1{
t.Logf("k = %s v = %s \n",k,v)
}
}

如果只需要value 不需要key 可以使用 **_ ** 表示无用的参数占位符

1
2
3
4
5
6
func TestMapFor(t *testing.T) {
map1 := map[string]string{"1":"一","2":"二"}
for _,v := range map1{
t.Log(v)
}
}

map 的切片

map类型的切片的意思是 切片的每个元素都是一个map,类似于java中的 List<Map<K,V>> 这种结构。
需要注意的是对切片内部的每个map 都需要再执行make完成初始化。

1
2
3
4
5
6
7
8
9
10
11
func TestSliceMap(t *testing.T) {
s1 := make([]map[int]int,5)
map1 := s1[0]
t.Log(map1 == nil) //true
t.Log(s1)
//切片内的map初始化
for i := 0;i< len(s1);i++{
s1[i] = make(map[int]int)
}
t.Log(s1[0] == nil) //false
}

map 排序

map 是没有顺序的,如果想有顺序的获取map中的元素,需要先将key 或 value 拷贝到一个切片,然后对切片排序

根据key排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

func TestSortByKey(t *testing.T) {
map1 := map[int]int{1:1,2:2,100:100,20:20,40:40,4:4}
keys := make([]int,len(map1))
i := 0
for k,_ := range map1{
keys[i] = map1[k]
i++
}
//对key进行排序
sort.Ints(keys)
for _,k := range keys{
t.Log(map1[k])
}
}

map 和工厂模式

map的value 可以是一个方法,所以可以通过map来实现一个简单的工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

func TestMapFactory(t *testing.T) {
map1 := map[int]func(op int)int {}
map1[1] = func(op int) int {

return op + 1
}
map1[2] = func(op int) int {

return op + 2
}

map1[3] = func(op int) int {

return op + 3
}

t.Log(map1[1](1))
t.Log(map1[2](1))
t.Log(map1[3](1))

}

不同的key分别有不同的实现

注意:go 中的传输都是值传递,外部的值不会被修改,而map的会被修改时因为传递map 的时候复制的新对象的内部引用是以一个。可以理解为 复制的时候是浅拷贝

Set

set 是一个无序的,不可重复的数据结构。在go中是没有set这个数据结构的,不过可以通过map来实现一个set。

一般使用一个value 为bool 的map 来实现set的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

func TestSet(t *testing.T) {
set := make(map[string]bool)

//添加元素
set["1"] = true
//size
t.Logf("size = %d",len(set))
//是否包含
if set["1"]{
t.Logf("包含 1")
}else{
t.Logf("不包含 1")
}

//删除操作
delete(set,"1")

if set["1"]{
t.Logf("包含 1")
}else{
t.Logf("不包含 1")
}
}

因为value 使用bool 类型,所以不用判断值是否存在,值为true 那么一定是存在