在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
数组 Go 语言中数组在初始化之后大小就无法改变,存储元素类型相同、但是大小不同的数组类型在 Go 语言看来也是完全不同的,只有两个条件都相同才是同一个类型。 func NewArray(elem *Type, bound int64) *Type { if bound < 0 { Fatalf("NewArray: invalid bound %v", bound) } t := New(TARRAY) t.Extra = &Array{Elem: elem, Bound: bound} t.SetNotInHeap(elem.NotInHeap()) return t } 编译期间的数组类型是由上述的 Go 语言中的数组有两种不同的创建方式,一种是显式的指定数组的大小,另一种是使用 访问数组的索引是非整数时会直接报错 —— 访问数组的索引是负数时会直接报错 —— 访问数组的索引越界时会直接报错 —— 简而言之:数组包含两部分,一个是元素类型,一个是数组大小。只有两个都相等才是数组才相等。 索引非整数、负数、越界都会报错。 切片
func NewSlice(elem *Type) *Type { if t := elem.Cache.slice; t != nil { if t.Elem() != elem { Fatalf("elem mismatch") } return t } t := New(TSLICE) t.Extra = Slice{Elem: elem} elem.Cache.slice = t return t } 编译期间的切片是 type SliceHeader struct { Data uintptr Len int Cap int } Go 语言中的切片有三种初始化的方式:
扩容 在分配内存空间之前需要先确定新的切片容量,Go 语言根据切片的当前容量选择不同的策略进行扩容:
哈希表 实现哈希表的关键点在于如何选择哈希函数,哈希函数的选择在很大程度上能够决定哈希表的读写性能,在理想情况下,哈希函数应该能够将不同键能够地映射到不同的索引上,这要求哈希函数输出范围大于输入范围,但是由于键的数量会远远大于映射的范围,所以在实际使用时,这个理想的结果是不可能实现的。 冲突解决 开放寻址法 对数组中的元素依次探测和比较以判断目标键值对是否存在于哈希表中,如果发生冲突,就会将键值写到下一个不为空的位置 开放寻址法中对性能影响最大的就是装载因子,它是数组中元素的数量与数组大小的比值,随着装载因子的增加,线性探测的平均用时就会逐渐增加,这会同时影响哈希表的读写性能,当装载率超过 70% 之后,哈希表的性能就会急剧下降,而一旦装载率达到 100%,整个哈希表就会完全失效,这时查找任意元素都需要遍历数组中全部的元素,所以在实现哈希表时一定要时刻关注装载因子的变化。 拉链法 实现拉链法一般会使用数组加上链表,如果发生冲突,将键值写道链表的下一个位置 与开放地址法一样,拉链法的装载因子越大,哈希的读写性能就越差,在一般情况下使用拉链法的哈希表装载因子都不会超过 1,当哈希表的装载因子较大时就会触发哈希的扩容,创建更多的桶来存储哈希中的元素,保证性能不会出现严重的下降。如果有 1000 个桶的哈希表存储了 10000 个键值对,它的性能是保存 1000 个键值对的 1/10,但是仍然比在链表中直接读写好 1000 倍。 Go 语言运行时同时使用了多个数据结构组合表示哈希表,其中使用 type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Pointer nevacuate uintptr extra *mapextra }
type bmap struct { tophash [bucketCnt]uint8 } 访问 字符串 字符串虽然在 Go 语言中是基本类型 type StringHeader struct { Data uintptr Len int } type SliceHeader struct { Data uintptr Len int Cap int } 与切片的结构体相比,字符串少了一个表示容量的 作为只读的数据类型,我们无法改变其本身的结构,但是在做拼接和类型转换等操作时时一定要注意性能的损耗,遇到需要极致性能的场景一定要尽量减少类型转换的次数。 |
请发表评论