第4章 数组、切片、映射

数组

  • 长度固定,内存连续
  • 声明一个数组
var arr [5]int
arr :=[...]int{10,20,30,40,50}
arr :=[5]int{10,20,30,40,50}
  • 指针数组
sp := [3]*string{new(string), new(string), new(string)} //使用new(type)的方式来初始化对象
*sp[0] = "red" // *sp[0]指针的引用
*sp[1] = "blue"
*sp[2] = "green"
  • 复制数组的时候需要长度和类型都保持一致
  • 数组在做为入参的时候因copy数组消耗比较大,所以一般传递的时候都会传递指针对象

切片

  • 切片当中的容量与长度的概念是什么?
  • 切片分为三块,头部指针,长度、容量,所以在64位架构设备上是24字节,所以做为参数传递或者复制开销小
  • 空切片与nil切片的不同在于指针对象是否为nil
  • 切片是动态增长的,可以自动增长和缩小;增长需要使用 append 来实现
  • append出来的切片会与原切片共享底层数组
a := []byte("ba")

a1 := append(a, 'd')
a2 := append(a, 'g')

fmt.Println(string(a1)) // bag
fmt.Println(string(a2)) // bag
  • 初始化切片
slice := make([]int, 5, 3)  // 长度不能大于容量

sl := make([]int, 3, 5)
fmt.Println(len(sl))  //3
fmt.Println(cap(sl))  //5

sl := make([]int,2)  // 初始化了两个0值的切片;长度和容量都为2
sl := make([][]interface) // 初始化一个interface数组的切片
  • 切片截取
origin := []int{10, 20, 30, 40, 50}
current := origin[1:3]
// 容量: cap(origin)-1, 长度: 3-1
fmt.Printf("current len %d, cap %d, value %v\n", len(current), cap(current), current)
// current 与 origin 共享了一套数组,所以值会被连动修改
current[1] = 35
fmt.Printf("current len %d, cap %d, value %v\n", len(current), cap(current), current)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

输出
current len 2, cap 4, value [20 30]
current len 2, cap 4, value [20 35]
origin len 5, cap 5, value [10 20 35 40 50]
  • append出来的新元素,容量会增长2倍,注释容量的输出
origin := []int{10, 20, 30, 40, 50}
current := append(origin, 60)

fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)
fmt.Printf("current len %d, cap %d, value %v\n", len(current), cap(current), current)

//origin len 5, cap 5, value [10 20 30 40 50]
//current len 6, cap 10, value [10 20 30 40 50 60]
var origin []int
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 2)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 7)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 1)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 3)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 8)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)

origin = append(origin, 4)
fmt.Printf("origin len %d, cap %d, value %v\n", len(origin), cap(origin), origin)
//origin len 0, cap 0, value []
//origin len 1, cap 1, value [2]
//origin len 2, cap 2, value [2 7]
//origin len 3, cap 4, value [2 7 1]
//origin len 4, cap 4, value [2 7 1 3]
//origin len 5, cap 8, value [2 7 1 3 8]
//origin len 6, cap 8, value [2 7 1 3 8 4]
  • 第三个索引的作用
origin := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}

// 引入第三个索引进行创建切片
// 长度为3-2,容量为4-2
current := origin[2:3:4]

fmt.Printf("current len %d, cap %d, value %v\n", len(current), cap(current), current)
//current len 1, cap 2, value [Plum]
  • range是copy一个属性,而不是原有属性
origin := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
for i, v := range origin {
	fmt.Printf("index: %d, value: %s, value_point %v\n", i, v, &v)
}
//	value的指针地址都是同一个
//index: 0, value: Apple, value_point 0xc00004d110
//index: 1, value: Orange, value_point 0xc00004d110
//index: 2, value: Plum, value_point 0xc00004d110
//index: 3, value: Banana, value_point 0xc00004d110
//index: 4, value: Grape, value_point 0xc00004d110
  • 数据若定义为指针,请小心;数组的内部是共享数据对象

func TestAppend2(t *testing.T) {
	src := []int{1, 2, 3, 4, 5}

	// 输出55555
	for _, p := range copySlicePoint(src) {
		fmt.Print(*p)
	}

	fmt.Println()
	
	// 输出12345
	for _, p := range copySlice(src) {
		fmt.Print(p)
	}
}

func copySlicePoint(src []int) []*int {
	var dst2 []*int
	for _, i := range src {
		dst2 = append(dst2, &i)
	}
	return dst2
}

func copySlice(src []int) []int {
	var dst2 []int
	for _, i := range src {
		dst2 = append(dst2, i)
	}
	return dst2
}

copySlicePoint函数的操作过程与下面是一致的

func copySlicePoint2(src []int) []*int {
	var dst2 []*int
	var j *int
	for _, i := range src {
		j = &i
		dst2 = append(dst2, j)
	}
	return dst2
}

Map

  • go-maps-in-action: 官方文档,讲的很透
  • Key不能使用slice和func,不能使用==号进行判断这种衡量方式比较清奇
  • map需要进行初始化使用
	var mapA map[string]string
	mapA["name"] = "zhangsan"  // 空指针异常
  • working with map
	mapA := make(map[string]string)
	mapA["name"] = "zhangsan"
	mapA["age"] = "10"
	mapA["address"] = "浙江杭州"
	mapA["company"] = "中科院"

	// map len
	t.Logf(" mapA length is %d", len(mapA))

	// get value
	name := mapA["name"]
	t.Logf("name is %s", name)

	// is exist
	age, ok := mapA["age"]
	t.Logf("age value is exist? %t, value is %s", ok, age)

	// delete one
	delete(mapA, "company")
	t.Logf("mapA %v", mapA)

	// loop
	for k, v := range mapA {
		t.Logf(" k: %s, v : %s", k, v)
	}
  • map是线程不安全的,所以并发的时候需要使用sync.RWMutex.