常用集合
数据和切片
数组声明
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。
切片声明
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
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