Go基础2:数据结构(一)

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。

1.数组

数组是一段固定长度的连续内存区域。
在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。

1.1 声明数组

var team [3]string
team[0] = "hammer"
team[1] = "soldier"
team[2] = "mum"

1. 2 初始化数组

var team = [3]string{"hammer", "soldier", "mum"}
var team = [...]string{"hammer", "soldier", "mum"}

arr := [3]int{1, 2, 3}

... 表示让编译器确定数组大小。上面例子中,编译器会自动为这个数组设置元素个数为3。

1.3 遍历数组

for k, v := range team {
     fmt.Println(k, v)
}
/*
0 hammer
1 soldier
2 mum
*/

1.4 读取文本文件

data.txt

70.5
90.3
88.2
func main() {
	// 读取文件
	file, err := os.Open("../data.txt")
	if err != nil {
		log.Fatal(err)
	}
	// 为文件创建一个扫描器,返回一个从文件中读取的bufio.Scanner值。
	scanner := bufio.NewScanner(file)
	// 循环读取行, 一旦到达文件的末尾(或出现错误),Scan将返回false,循环将退出。
	for scanner.Scan() { 
		fmt.Println(scanner.Text())
	}
	err = file.Close()
	if err != nil {
		log.Fatal(err)
	}
	// 如果扫描文件时出现错误,报告并退出程序
	if scanner.Err() != nil {
		log.Fatal(scanner.Err())
	}
}

读取文本文件到数组

var numbers [3]float64
file, err := os.Open(filename)

i := 0
scanner := bufio.NewScanner(file)
for scanner.Scan() {
	numbers[i], err = strconv.ParseFloat(scanner.Text(), 64)
	i++
}

2. 切片(slice)

动态分配大小的连续空间

Go语言切片的内部结构包含地址、大小和容量。切片一般用于快速地操作一块数据集合。

2.0 声明切片

区别于数组,声明切片变量并不会自动创建一个切片,可以调用make函数,传递切片类型及长度:

var notes []string
notes = make([]string, 3)

2.1 从数组或切片生成新的切片

切片默认指向一段连续内存区域,可以是数组,也可以是切片本身。

var a  = [3]int{1, 2, 3}
fmt.Println(a, a[1:2])

切片有点像C语言里的指针。指针可以做运算,但代价是内存操作越界。切片在指针的基础上增加了大小,约束了切片对应的内存区域,切片使用中无法对切片内部的地址和大小进行手动调整,因此切片比指针更安全、强大。

表示原有的切片
a[:]
重置切片,清空拥有的元素
a[0:0]

切片是动态结构,只能与nil判定相等,不能互相判等时。

2.2 使用make()函数构造切片

如果需要动态地创建一个切片,可以使用make()内建函数,格式如下:

make([]T, size, cap)
  • T:切片的元素类型。
  • size:就是为这个类型分配多少个元素。
  • cap:预分配的元素数量,这个值设定后不影响size,只是能提前分配空间,降低多次分配空间造成的性能问题。

make创造的切片后,后续运用该切片创造的新切片类似于指针只是指向旧切片,不创造新空间。

2.3 使用append()函数为切片添加元素

内建函数append()可以为切片动态添加元素。
每个切片会指向一片内存空间,这片空间能容纳一定数量的元素。当空间不能容纳足够多的元素时,切片就会进行“扩容”。“扩容”操作往往发生在append()函数调用时。

切片扩容规则:
如果切片的容量小于1024个元素,那么扩容的时候slice的cap就乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25。
如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。

var car []string
// 添加1个元素
car = append(car, "Old Driver")
// 添加多个元素
car = append(car, "Ice", "Sniper", "Monk")
// 添加切片
team := []string{"Pig", "Flyingcake", "Chicken"}
car = append(car, team...)
fmt.Println(car)

通常情况append只是用来往切片中加一个一个的元素,如果要往切片中加另一个切片,需要使用

2.4 复制切片元素到另一个切片

copy(destSlice, srcSlice []T) int
  • src Slice为数据来源切片。
  • dest Slice为复制的目标。
    目标切片必须分配过空间且足够承载复制的元素个数。来源和目标的类型一致,copy的返回值表示实际发生复制的元素个数。

2.5 从切片中删除元素

Go语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素

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)

3. map

Go语言提供的映射关系容器为map。map使用散列表(hash)实现。

散列表可以简单描述为一个数组(俗称“桶”),数组的每个元素是一个列表。
根据散列函数获得每个元素的特征值,将特征值作为映射的键。
如果特征值重复,表示元素发生碰撞。碰撞的元素将被放在同一个特征值的列表中进行保存。
散列表查找复杂度为O(1),和数组一致。最坏的情况为O(n),n为元素总数。
散列需要尽量避免元素碰撞以提高查找效率,这样就需要对“桶”进行扩容,每次扩容,元素需要重新放入桶中,较为耗时。
平衡树类似于有父子关系的一棵数据树,每个元素在放入树时,都要与一些节点进行比较。平衡树的查找复杂度始终为O(log n)

声明一个映射

var map[KeyType]ValueType

// 声明一个映射,并创建它
scene := make(map[string]int)
scene["route"] = 66
fmt.Println(scene["route"])
v := scene["route2"]

需要明确知道查询中某个键是否在map中存在

v, ok := scene["route"]  // 66 true

使用delete()内建函数从map中删除一组键值对
delete(map, 键)

清空map的唯一办法就是重新make一个新的map。不用担心垃圾回收的效率,Go语言中的并行垃圾回收效率比写一个清空函数高效多了。

posted on 2022-05-14 15:48  micromatrix  阅读(48)  评论(0编辑  收藏  举报

导航