切片
切片的底层是数组构成,包含:头部指针、len(长度)、cap(容量)。
无论怎么操作([:2]、[:0]),只要头部指针位置改变,cap 就会改变。
扩容机制
- • < 1024:翻倍扩容
- • > 1024:扩容 1.25 倍
为什么翻倍扩容?
扩容的时候会把所有数据(旧 + 新数据)拷贝,然后放入新数组。
+1 翻倍拷贝 过于频繁:
总拷贝次数 = 1 + 2 + 3 + ... + n
= n(n+1)/2 = O(n²)
翻倍扩容 则更优:
假设最终容量达到 n(即 2^k ≈ n),总拷贝次数:
T(n) = 1+2+4+8+...+2^k−1
= 2^k − 1 ≈ n − 1 = O(n)
过大扩容有可能会导致内存浪费,这在大于 1024 的时候只是扩容 1.25 倍可以看出。
Map
Go 语言采用的是哈希查找表,并且使用链表解决哈希冲突。
哈希查找表:使用哈希函数将 Key 分配到不同的桶(数组的不同 index)。开销主要在哈希函数的计算以及数组的常数访问时间。- 碰撞问题:不同的 key 被哈希到了同一个 bucket。一般有两种应对方法:
- •
链表法:将一个 bucket 实现成链表,落在同一个 bucket 中的 key 都会插入这个链表 - •
开放地址法:碰撞发生后,通过一定的规律,在数组后面挑选"空位"放置新的 key
- •
- 搜索树法:一般采用自平衡搜索树,包括 AVL 树、红黑树。自平衡搜索树法的最差搜索效率是 O(logN),而哈希查找表最差是 O(N)。哈希查找表的平均查找效率是 O(1)。
遍历自平衡搜索树,返回的 key 序列会按照从小到大的顺序;而哈希查找表则是乱序的。
闭包
✓理解
○整理
方法
一类带特殊的接受者参数的函数。
type Vertex struct {X, Y float64}
带方法的函数:func (v Vertex) Abs() float64
普通函数:func Abs(v Vertex) float64
两者功能其实没有变化。
注意:"你只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法。"
type MyInt int // 类型定义:创建全新类型,需要显式转换
type IntAlias = int // 类型别名:只是别名,完全等价于原类型
// 不能为类型别名声明方法!- • 指针接收者:可以修改接收者指向的值。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
- • 值接收者:方法会对原始值的副本进行操作。方法必须用指针接收者来更改函数中声明的值。
方法与指针重定向
- • 带指针参数的函数必须接受一个指针。而以指针为接收者的方法被调用时,接收者既能为值又能为指针。
- • 为什么选择值或者指针作为接受者?
- ◦ 方法可以修改其接受者指向的值
- ◦ 可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,会更加高效
Interface
接口,是一种类型,一种抽象的类型。你能知道的仅仅是它能做什么,而不能知道它是什么。
- • 实现接口的条件:一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
- • 接口内嵌
- • 接口值:一个具体的类型和该类型的值
- •
sort.Interface - •
http.Handler接口 - •
error接口