常用集合

数据和切片

数组声明

func TestArrayInit(t *testing.T) {
	var arr [3]int
	arr1 := [4]int{1, 2, 3, 4}
	arr3 := [...]int{1, 2, 3, 4, 5}

	arr1[1] = 6

	t.Log(arr[1], arr[2]) // 0 0
	t.Log(arr1)           // [1, 6, 3, 4]
	t.Log(arr3)           // [1, 2, 3, 4, 5]
}
go

数组元素遍历

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

	// 1. method1
	for i := 0; i < len(arr); i++ {
		t.Log((arr[i]))
	}

	// 2. method2
	for idx, e := range arr {
		t.Log(idx, e)
	}

	// 3. method3
	for _, e := range arr {
		t.Log(e)
	}
}
go

数组截取

a[开始索引(包含), 结束索引(不包含)]
func TestArraySection(t *testing.T) {
	arr := [...]int{1, 2, 3, 4, 5}

	t.Log(arr[:3]) // [1 2 3]
	t.Log(arr[3:]) // [4 5]
	t.Log(arr[:])  // [1 2 3 4 5]
}
go

数组截取之后,其实就是一个 slice。

切片声明

slice.png

func TestSliceInit(t *testing.T) {
	var s0 []int
	t.Log(len(s0), cap(s0)) // 0 0

	s0 = append(s0, 1)
	t.Log(len(s0), cap(s0)) // 1 1

	s1 := []int{1, 2, 3, 4}
	t.Log(len(s1), cap(s1)) // 4 4

	s2 := make([]int, 3, 5)
	t.Log(len(s2), cap(s2)) // 3 5
	// cap 容量,len 可访问元素个数
	// t.Log(s2[0], s2[1], s2[2], s2[3], s2[4]) // index out of range [3] with length 3 [recovered]
	t.Log(s2[0], s2[1], s2[2]) // 0 0 0

	s2 = append(s2, 5)
	t.Log(len(s2), cap(s2))           // 4 5
	t.Log(s2[0], s2[1], s2[2], s2[3]) // 0 0 0 5
}
go

切片共享存储结构

func TestSliceGrowing(t *testing.T) {
	s := []int{}

	for i := 0; i < 10; i++ {
		s = append(s, i)
		t.Log(len(s), cap(s))
	}
	// 当容量不够时,会乘 2 倍的方式进行扩展
	// 当扩展时,会开启新的存储空间,并将原有空间的元素拷贝到新空间中间
	// 1 1
	// 2 2
	// 3 4
	// 4 4
	// 5 8
	// 6 8
	// 7 8
	// 8 8
	// 9 16
	// 10 16
}
go

slice02.png

func TestSliceShareMemory(t *testing.T) {
	year := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}

	Q2 := year[3:6]
	t.Log(Q2, len(Q2), cap(Q2)) // [Apr May Jun] 3 9

	summer := year[5:8]
	t.Log(summer, len(summer), cap(summer)) // [Jun Jul Aug] 3 7

	summer[0] = "Unknow"
	t.Log(Q2)   // [Apr May Unknow]
	t.Log(year) // [Jan Feb Mar Apr May Unknow Jul Aug Sep Oct Nov Dec]
}
go

数组 vs 切片

  • 容量是否可伸缩
    • 数组定长
    • 切片不定长
  • 是否可以进行比较
    • 相同维数、相同长度的数组是可以比较的
    • 切片不能比较
func TestSliceComparing(t *testing.T) {
	a := []int{1, 2, 3, 4}
	b := []int{1, 2, 3, 4}

	if a == b { // cannot compare a == b (slice can only be compared to nil)
		t.Log("equal")
	}
}
go

Map

Map 声明

m := map[string]int{"one": 1, "two": 2, "three": 3}

m1 := map[string]int{}
m1["one"] = 1

m2 := make(map[string]int, 10 /**Initial Capacity*/)
// 为什么不初始化 len ?
// 切片中 len 都会赋值为零值,map 没有办法确认零值
go
func TestInitMap(t *testing.T) {
	m1 := map[int]int{1: 1, 2: 4, 3: 9}
	t.Log(m1[2])                 // 4
	t.Logf("len m1=%d", len(m1)) // len m1=3

	m2 := map[int]int{}
	m2[4] = 16
	t.Logf("len m2=%d", len(m2)) // len m2=1

	m3 := make(map[int]int, 10)
	t.Logf("len m3=%d", len(m3)) // len m3=0
}
go

Map 元素访问

在访问的 key 不存在时,会返回零值,不能通过返回 nil 来判断元素是否存在。

func TestAccessNotExistingKey(t *testing.T) {
	m1 := map[int]int{}
	t.Log(m1[1]) // 0

	m1[2] = 0
	t.Log(m1[1]) // 0

	// 无法区分不存在值或实际值为 0
	// 需要自主判断

	// m1[3] = 0

	if _, ok := m1[3]; ok {
		t.Logf("key 3 value is %d", m1[3])
	} else {
		t.Log("key 3 is not existing.")
	}
}
go

Map 遍历

m := map[string]int{"one": 1, "two": 2, "three": 3}
for k, v := range m {
  t.Log(k, v)
}
go
func TestTravelMap(t *testing.T) {
	m1 := map[int]int{1: 1, 2: 4, 3: 9}

	for k, v := range m1 {
		t.Log(k, v)
		// 1 1
		// 2 4
		// 3 9
	}
}
go

Map 底层使用使用 hash 表机制,所以遍历结果并不总是有序的。

Map 与工厂模式

  • Map 的 value 可以是一个方法
    • Go 语言中,函数是一等公民
  • 与 Go 的 Dock type 接口方式一起,可以方便的实现单一方法对象的工厂模式
package map_test

import "testing"

func TestMapWithFunValue(t *testing.T) {
	m := map[int]func(op int) int{}

	m[1] = func(op int) int { return op }
	m[2] = func(op int) int { return op * op }
	m[3] = func(op int) int { return op * op * op }

	t.Log(m[1](2), m[2](2), m[3](2)) // 2 4 8 
}
go

实现 Set

Go 语言没有提供内置的 Set,我们可以使用 Map 实现一个 Set。

  • 元素唯一性
  • 基本操作
    • 添加元素
    • 判断元素是否存在
    • 删除元素
    • 元素个数
func TestMapForSet(t *testing.T) {
	set := map[int]bool{}

	set[1] = true

	n := 1

	if set[n] {
		t.Logf("%d is existing", n)
	} else {
		t.Logf("%d is not existing", n)
	}

	set[2] = true
	t.Log(len(set)) // 2

	delete(set, 2)
	t.Log(len(set)) // 1
}
go