go基础之map-迭代(四)

本文深入分析Go语言中map的迭代过程,包括hiter结构体、迭代不同情况的处理,如正常迭代、扩容时的迭代以及溢出桶的处理。文章通过代码分析,解释了迭代过程中关键步骤和注意事项,旨在帮助读者理解Go map迭代的内部机制。
摘要由CSDN通过智能技术生成

写在之前

在文章《go基础之map-写在前面(一)》的示例代码

for k, v := range m3 {
   
		fmt.Println(k, v)
}

就是go的map的迭代方法,查看该代码的字节码,发现它调用了底层runtime.mapiterinit方法。本篇会详细分析map的迭代方法。如果想详细查看源码的注释,可以查看我的GitHub,欢迎批评指正。我的打算是把一些常用的数据结构都分析一遍,如果有志同道合的人,可以联系我。

hiter结构体

这个结构体非常重要,该结构体记录了迭代过程中当前的信息,在迭代下个数据的时候会使用当前的hiter。让我们看看他的结构体:

// A hash iteration structure.
// If you modify hiter, also change cmd/compile/internal/gc/reflect.go to indicate
// the layout of this structure.
// 8 * 9 + 4 * 1 + (偏移4位) + 8 * 2 = 8 * 12
type hiter struct {
   
	// 8个字节
	key unsafe.Pointer // Must be in first position.  Write nil to indicate iteration end (see cmd/internal/gc/range.go).
	// 8个字节
	elem unsafe.Pointer // Must be in second position (see cmd/internal/gc/range.go).
	// 8个字节
	t *maptype
	// 8个字节
	h *hmap
	// 8个字节
	buckets unsafe.Pointer // bucket ptr at hash_iter initialization time
	// 8个字节
	bptr *bmap // current bucket
	// 8个字节
	overflow *[]*bmap // keeps overflow buckets of hmap.buckets alive
	// 8个字节
	oldoverflow *[]*bmap // keeps overflow buckets of hmap.oldbuckets alive
	// 8个字节
	startBucket uintptr // bucket iteration started at
	// 1个字节
	offset uint8 // intra-bucket offset to start from during iteration (should be big enough to hold bucketCnt-1)
	// 1个字节
	wrapped bool // already wrapped around from end of bucket array to beginning
	// 1个字节
	B uint8
	// 1个字节
	i uint8
	// 8个字节
	bucket uintptr
	// 8个字节
	checkBucket uintptr
}

我用人话简答说明下字段的意思:

  1. keyelem表示当前跌倒获取到的key和value,直白的说fmt.Println(k, v)打印出来的值。
  2. buckets就是hmap的buckets。
  3. bptr比较清晰就是表示当前我正在迭代哪个bucket。
  4. overflowoldoverflow很容易联想到和hamp的extral里面的overflowoldoverflow,我这里忽略他就不分析了。
  5. startBucket表示从哪个bucket开始迭代,这个startBucket在每次迭代的开始设置的,而且这个值是不确定的。也就是说每次for k, v := range m3的迭代打印出来的k的顺序很有可能都不是一致的。
  6. offset表示从startBucket这个bucket的第几个key开始迭代。
  7. wrapped表示已经迭代到最后一个桶了。
  8. B无需多说,i表示当前迭代的桶的进度了。
  9. bucket会记录当前迭代到哪个桶了,如果该值等于startBucket值了,表示就迭代完毕了。
  10. checkBucket这个字段的用处在下面的分析过程中说明,单独拎出来讲不清楚。

前景提要说明白了,就开始分析mapiterinit这个入口方法了:

// mapiterinit initializes the hiter struct used for ranging over maps.
// The hiter struct pointed to by 'it' is allocated on the stack
// by the compilers order pass or on the heap by reflect_mapiterinit.
// Both need to have zeroed hiter since the struct contains pointers.
func mapiterinit(t *maptype, h *hmap, it *hiter) {
   
    ...
	size := unsafe.Sizeof(hiter{
   })
	// 意义在哪里
	if size/sys.PtrSize != 12 {
   
		throw("hash_iter size incorrect") // see cmd/compile/internal/gc/reflect.go
	}
	it.t = t
	it.h = h

	// grab snapshot of bucket state
	it.B = h.B
	it.buckets = h.buckets
	if t.bucket.ptrdata == 0 {
   
		// Allocate the current slice and remember pointers to both current and old.
		// This preserves all relevant overflow buckets alive even if
		// the table grows and/or overflow buckets
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangshen023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值