1 array
在Golang中,array(数组)是一种固定长度的序列数据结构,其中每个元素都具有相同的类型,其长度是固定的,一旦定义后不能改变。数组的元素可以通过索引访问,索引从0开始。
2 slice
在实际编程中array有许多限制,我们很多时候需要动态数组;Golang提供了动态数组即slice数据结构,其可以动态增大或减小。
2.1 slice结构
参考Golang源码,slice被定义为一种struct类型,其成员包含指向实际数组的指针,cap和len两个int类型的变量维护slice的容量和有效长度。
type slice struct {
array unsafe.Pointer
len int
cap int
}
2.2 slice的状态
在实际运行中slice可能处于三种状态:零切片、空切片、nil切片。
零切片:slice底层有指向数组但无赋值的状态。
空切片:slice底层指针指向非nil数组,但是len为0。
nil切片:slice的底层数组指向nil,即未分配空间。
2.3 slice的扩容机制
参考源码,当slice发生扩容时,对于新数组的cap选择slice分为了三种情况;当新数组的len大于两倍旧数组的cap两倍,新数组的cap为新数组的len;当旧数组的cap小于设定阈值(默认为256)时,新数组的cap为旧数组cap的两倍;前两者均不满足时,新数组的cap为旧数组cap的1.25倍+0.75阈值(默认为256)。
newcap := oldCap
doublecap := newcap + newcap
if newLen > doublecap {
newcap = newLen
} else {
const threshold = 256
if oldCap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < newLen {
// Transition from growing 2x for small slices
// to growing 1.25x for large slices. This formula
// gives a smooth-ish transition between the two.
newcap += (newcap + 3*threshold) / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = newLen
}
}
}
3 map
Golang语言内置了map的数据结构,其底层为哈希实现,但其存在线程不安全的局限性。map底层为hmap结构,其中count变量记录map中的键值对数量;B为hmap中桶的数量的二进制长度,即通数量为2^B;overflow为溢出桶的数量;buckets和oldbuckets分别为桶指针和扩容时旧桶的指针;extra为溢出桶的地址;
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
4 sync.Map
针对map的并发不安全缺陷,Go 语言标准库中提供的一个并发安全的映射(map)实现即sync.Map。其内部使用了锁机制来确保多个goroutine读写时的一致性,因此多goroutine访问时不会出现竞态条件;对于读多写少的场景,sync.Map使用了读写锁来提高性能,因此sync.Map适用于高并发场景。
5 栈与队列
栈与队列是两种常见的数据结构,栈有着先进后出(FILO)的特点;队列为先进先出(FIFO)的特点;很遗憾Golang官方没有提供两种数据结构的实现,Golang确实有点点简陋了,我们可以在github上找三方实现或自己手搓。