为什么分析map

在计算机编程语言当中,用的最多的数据结构估计就是map。map以他近乎o(1)的查找效率和修改效率让他在大多数场景下都比较受青睐。map的常规的实现方式都是hash+其他数据结构,如java是hash+红黑树,而我现在即将要分析的go的实现方式是hash+链表。我会分析map的几乎每段代码,并且在我的GitHub可以查看到我的分析,注释十分详尽,欢迎批评指正。我的打算是把一些常用的数据结构都分析一遍,如果有志同道合的人,可以联系我。

我的环境

为了给那些感兴趣看源码分析我的博客的同学阅读得更加清晰,列举了我的环境: 1. go1.14.7 amd64; 2. windows和mac接口,linux没有测试,估计没问题 ; 3. goland,目前最好的golang ide,虽然笔者用起来感觉还是不太完美; 4. 着重分析map[string]string,其他类型的源码变化不大

本篇重点

1.go的map和java的map有区别,go中是一个关键字,而java确实可以直接查看源码,那么如何分析go的map源码? 2.调试过程当中,go充斥着大量指针,如何知道指针指向的内容?map的最小单位bmap除了查看tophash,怎么查看里面的其他隐藏字段? 3.go的map源码为何给不同的key的类型设计不同的实现? 4.其他一些查看源码的小技巧:内存对齐、指针偏移、类型大小等等

如何找到map对应的源码

go的map的结构体是hmap,代码位于runtime/map.go,当编译器编译你申明使用map的源码时候,其实是使用了runtime/map*.go,后面对map的增删改查都是执行该代码,故要弄懂map的底层原理只需要分析该源码,也可以断点调试。 我下面有个自己测试的源码,注意我编译之后查看,就可以发现端倪:

package mainimport "fmt"func main() {m1 := make(map[string]string)fmt.Println(m1)m2 := make(map[string]string, 8)fmt.Println(m2)m3 := make(map[string]string, 9)fmt.Println(m3)m4 := map[string]string{}fmt.Println(m4)m3["1"] = "2"for k, v := range m3 {fmt.Println(k)fmt.Println(v)}v1 := m3["1"]fmt.Println(v1)if v2, ok := m3["1"]; ok {fmt.Println(v2)}
}

上面代码有几种不同申明map的方式,对应runtime/map.go也不同。还有赋值和遍历查找的代码。 下面编译得到汇编指令文件,执行

go tool compile -N -l -S main.go > main.txt

生成了汇编指令文件:

"".main STEXT size=1891 args=0x0 locals=0x2880x0000 00000 (main.go:5)    TEXT    "".main(SB), ABIInternal, $648-00x0000 00000 (main.go:5)    MOVQ    TLS, CX0x0009 00009 (main.go:5)    PCDATA  $0, $-20x0009 00009 (main.go:5)    MOVQ    (CX)(TLS*2), CX0x0010 00016 (main.go:5)    PCDATA  $0, $-10x0010 00016 (main.go:5)    LEAQ    -520(SP), AX0x0018 00024 (main.go:5)    CMPQ    AX, 16(CX)0x001c 00028 (main.go:5)    PCDATA  $0, $-20x001c 00028 (main.go:5)    JLS 18810x0022 00034 (main.go:5)    PCDATA  $0, $-10x0022 00034 (main.go:5)    SUBQ    $648, SP0x0029 00041 (main.go:5)    MOVQ    BP, 640(SP)0x0031 00049 (main.go:5)    LEAQ    640(SP), BP0x0039 00057 (main.go:5)    PCDATA  $0, $-20x0039 00057 (main.go:5)    PCDATA  $1, $-20x0039 00057 (main.go:5)    FUNCDATA    $0, gclocals·ad6397d3d33bbb6a31b1320349e23274(SB)0x0039 00057 (main.go:5)    FUNCDATA    $1, gclocals·1bbf1965a34fa71a63e073b1cf2d752a(SB)0x0039 00057 (main.go:5)    FUNCDATA    $2, gclocals·658035074399f748be6dd39603d5113c(SB)0x0039 00057 (main.go:5)    FUNCDATA    $3, "".main.stkobj(SB)0x0039 00057 (main.go:6)    PCDATA  $0, $00x0039 00057 (main.go:6)    PCDATA  $1, $00x0039 00057 (main.go:6)    CALL    runtime.makemap_small(SB)0x003e 00062 (main.go:6)    PCDATA  $0, $10x003e 00062 (main.go:6)    MOVQ    (SP), AX0x0042 00066 (main.go:6)    MOVQ    AX, "".m1+80(SP)0x0047 00071 (main.go:7)    PCDATA  $0, $00x0047 00071 (main.go:7)    PCDATA  $1, $10x0047 00071 (main.go:7)    MOVQ    AX, ""..autotmp_10+200(SP)0x004f 00079 (main.go:7)    PCDATA  $1, $20x004f 00079 (main.go:7)    XORPS   X0, X00x0052 00082 (main.go:7)    MOVUPS  X0, ""..autotmp_9+272(SP)0x005a 00090 (main.go:7)    PCDATA  $0, $10x005a 00090 (main.go:7)    PCDATA  $1, $10x005a 00090 (main.go:7)    LEAQ    ""..autotmp_9+272(SP), AX0x0062 00098 (main.go:7)    MOVQ    AX, ""..autotmp_15+192(SP)0x006a 00106 (main.go:7)    TESTB   AL, (AX)0x006c 00108 (main.go:7)    PCDATA  $0, $20x006c 00108 (main.go:7)    PCDATA  $1, $00x006c 00108 (main.go:7)    MOVQ    ""..autotmp_10+200(SP), CX0x0074 00116 (main.go:7)    PCDATA  $0, $30x0074 00116 (main.go:7)    LEAQ    type.map[string]string(SB), DX0x007b 00123 (main.go:7)    PCDATA  $0, $20x007b 00123 (main.go:7)    MOVQ    DX, ""..autotmp_9+272(SP)0x0083 00131 (main.go:7)    PCDATA  $0, $10x0083 00131 (main.go:7)    MOVQ    CX, ""..autotmp_9+280(SP)0x008b 00139 (main.go:7)    TESTB   AL, (AX)0x008d 00141 (main.go:7)    JMP 1430x008f 00143 (main.go:7)    MOVQ    AX, ""..autotmp_14+520(SP)0x0097 00151 (main.go:7)    MOVQ    $1, ""..autotmp_14+528(SP)0x00a3 00163 (main.go:7)    MOVQ    $1, ""..autotmp_14+536(SP)0x00af 00175 (main.go:7)    PCDATA  $0, $00x00af 00175 (main.go:7)    MOVQ    AX, (SP)0x00b3 00179 (main.go:7)    MOVQ    $1, 8(SP)0x00bc 00188 (main.go:7)    MOVQ    $1, 16(SP)0x00c5 00197 (main.go:7)    CALL    fmt.Println(SB)0x00ca 00202 (main.go:8)    CALL    runtime.makemap_small(SB)0x00cf 00207 (main.go:8)    PCDATA  $0, $10x00cf 00207 (main.go:8)    MOVQ    (SP), AX0x00d3 00211 (main.go:8)    MOVQ    AX, "".m2+72(SP)0x00d8 00216 (main.go:9)    PCDATA  $0, $00x00d8 00216 (main.go:9)    PCDATA  $1, $10x00d8 00216 (main.go:9)    MOVQ    AX, ""..autotmp_10+200(SP)0x00e0 00224 (main.go:9)    PCDATA  $1, $20x00e0 00224 (main.go:9)    XORPS   X0, X00x00e3 00227 (main.go:9)    MOVUPS  X0, ""..autotmp_9+272(SP)0x00eb 00235 (main.go:9)    PCDATA  $0, $10x00eb 00235 (main.go:9)    PCDATA  $1, $10x00eb 00235 (main.go:9)    LEAQ    ""..autotmp_9+272(SP), AX0x00f3 00243 (main.go:9)    MOVQ    AX, ""..autotmp_17+184(SP)0x00fb 00251 (main.go:9)    TESTB   AL, (AX)0x00fd 00253 (main.go:9)    PCDATA  $0, $20x00fd 00253 (main.go:9)    PCDATA  $1, $00x00fd 00253 (main.go:9)    MOVQ    ""..autotmp_10+200(SP), CX0x0105 00261 (main.go:9)    PCDATA  $0, $30x0105 00261 (main.go:9)    LEAQ    type.map[string]string(SB), DX0x010c 00268 (main.go:9)    PCDATA  $0, $20x010c 00268 (main.go:9)    MOVQ    DX, ""..autotmp_9+272(SP)0x0114 00276 (main.go:9)    PCDATA  $0, $10x0114 00276 (main.go:9)    MOVQ    CX, ""..autotmp_9+280(SP)0x011c 00284 (main.go:9)    TESTB   AL, (AX)0x011e 00286 (main.go:9)    JMP 2880x0120 00288 (main.go:9)    MOVQ    AX, ""..autotmp_16+496(SP)0x0128 00296 (main.go:9)    MOVQ    $1, ""..autotmp_16+504(SP)0x0134 00308 (main.go:9)    MOVQ    $1, ""..autotmp_16+512(SP)0x0140 00320 (main.go:9)    PCDATA  $0, $00x0140 00320 (main.go:9)    MOVQ    AX, (SP)0x0144 00324 (main.go:9)    MOVQ    $1, 8(SP)0x014d 00333 (main.go:9)    MOVQ    $1, 16(SP)0x0156 00342 (main.go:9)    CALL    fmt.Println(SB)0x015b 00347 (main.go:10)   PCDATA  $0, $10x015b 00347 (main.go:10)   LEAQ    type.map[string]string(SB), AX0x0162 00354 (main.go:10)   PCDATA  $0, $00x0162 00354 (main.go:10)   MOVQ    AX, (SP)0x0166 00358 (main.go:10)   MOVQ    $9, 8(SP)0x016f 00367 (main.go:10)   MOVQ    $0, 16(SP)0x0178 00376 (main.go:10)   CALL    runtime.makemap(SB)0x017d 00381 (main.go:10)   PCDATA  $0, $10x017d 00381 (main.go:10)   MOVQ    24(SP), AX0x0182 00386 (main.go:10)   PCDATA  $1, $30x0182 00386 (main.go:10)   MOVQ    AX, "".m3+64(SP)0x0187 00391 (main.go:11)   PCDATA  $0, $00x0187 00391 (main.go:11)   PCDATA  $1, $40x0187 00391 (main.go:11)   MOVQ    AX, ""..autotmp_10+200(SP)0x018f 00399 (main.go:11)   PCDATA  $1, $50x018f 00399 (main.go:11)   XORPS   X0, X00x0192 00402 (main.go:11)   MOVUPS  X0, ""..autotmp_9+272(SP)0x019a 00410 (main.go:11)   PCDATA  $0, $10x019a 00410 (main.go:11)   PCDATA  $1, $40x019a 00410 (main.go:11)   LEAQ    ""..autotmp_9+272(SP), AX0x01a2 00418 (main.go:11)   MOVQ    AX, ""..autotmp_19+176(SP)0x01aa 00426 (main.go:11)   TESTB   AL, (AX)0x01ac 00428 (main.go:11)   PCDATA  $0, $20x01ac 00428 (main.go:11)   PCDATA  $1, $30x01ac 00428 (main.go:11)   MOVQ    ""..autotmp_10+200(SP), CX0x01b4 00436 (main.go:11)   PCDATA  $0, $30x01b4 00436 (main.go:11)   LEAQ    type.map[string]string(SB), DX0x01bb 00443 (main.go:11)   PCDATA  $0, $20x01bb 00443 (main.go:11)   MOVQ    DX, ""..autotmp_9+272(SP)0x01c3 00451 (main.go:11)   PCDATA  $0, $10x01c3 00451 (main.go:11)   MOVQ    CX, ""..autotmp_9+280(SP)0x01cb 00459 (main.go:11)   TESTB   AL, (AX)0x01cd 00461 (main.go:11)   JMP 4630x01cf 00463 (main.go:11)   MOVQ    AX, ""..autotmp_18+472(SP)0x01d7 00471 (main.go:11)   MOVQ    $1, ""..autotmp_18+480(SP)0x01e3 00483 (main.go:11)   MOVQ    $1, ""..autotmp_18+488(SP)0x01ef 00495 (main.go:11)   PCDATA  $0, $00x01ef 00495 (main.go:11)   MOVQ    AX, (SP)0x01f3 00499 (main.go:11)   MOVQ    $1, 8(SP)0x01fc 00508 (main.go:11)   MOVQ    $1, 16(SP)0x0205 00517 (main.go:11)   CALL    fmt.Println(SB)0x020a 00522 (main.go:12)   CALL    runtime.makemap_small(SB)0x020f 00527 (main.go:12)   PCDATA  $0, $10x020f 00527 (main.go:12)   MOVQ    (SP), AX0x0213 00531 (main.go:12)   PCDATA  $0, $00x0213 00531 (main.go:12)   PCDATA  $1, $60x0213 00531 (main.go:12)   MOVQ    AX, "".m4+56(SP)0x0218 00536 (main.go:13)   PCDATA  $0, $10x0218 00536 (main.go:13)   PCDATA  $1, $30x0218 00536 (main.go:13)   MOVQ    "".m4+56(SP), AX0x021d 00541 (main.go:13)   PCDATA  $0, $00x021d 00541 (main.go:13)   PCDATA  $1, $40x021d 00541 (main.go:13)   MOVQ    AX, ""..autotmp_10+200(SP)0x0225 00549 (main.go:13)   PCDATA  $1, $50x0225 00549 (main.go:13)   XORPS   X0, X00x0228 00552 (main.go:13)   MOVUPS  X0, ""..autotmp_9+272(SP)0x0230 00560 (main.go:13)   PCDATA  $0, $10x0230 00560 (main.go:13)   PCDATA  $1, $40x0230 00560 (main.go:13)   LEAQ    ""..autotmp_9+272(SP), AX0x0238 00568 (main.go:13)   MOVQ    AX, ""..autotmp_23+168(SP)0x0240 00576 (main.go:13)   TESTB   AL, (AX)0x0242 00578 (main.go:13)   PCDATA  $0, $20x0242 00578 (main.go:13)   PCDATA  $1, $30x0242 00578 (main.go:13)   MOVQ    ""..autotmp_10+200(SP), CX0x024a 00586 (main.go:13)   PCDATA  $0, $30x024a 00586 (main.go:13)   LEAQ    type.map[string]string(SB), DX0x0251 00593 (main.go:13)   PCDATA  $0, $20x0251 00593 (main.go:13)   MOVQ    DX, ""..autotmp_9+272(SP)0x0259 00601 (main.go:13)   PCDATA  $0, $10x0259 00601 (main.go:13)   MOVQ    CX, ""..autotmp_9+280(SP)0x0261 00609 (main.go:13)   TESTB   AL, (AX)0x0263 00611 (main.go:13)   JMP 6130x0265 00613 (main.go:13)   MOVQ    AX, ""..autotmp_22+448(SP)0x026d 00621 (main.go:13)   MOVQ    $1, ""..autotmp_22+456(SP)0x0279 00633 (main.go:13)   MOVQ    $1, ""..autotmp_22+464(SP)0x0285 00645 (main.go:13)   PCDATA  $0, $00x0285 00645 (main.go:13)   MOVQ    AX, (SP)0x0289 00649 (main.go:13)   MOVQ    $1, 8(SP)0x0292 00658 (main.go:13)   MOVQ    $1, 16(SP)0x029b 00667 (main.go:13)   CALL    fmt.Println(SB)0x02a0 00672 (main.go:14)   PCDATA  $0, $10x02a0 00672 (main.go:14)   LEAQ    type.map[string]string(SB), AX0x02a7 00679 (main.go:14)   PCDATA  $0, $00x02a7 00679 (main.go:14)   MOVQ    AX, (SP)0x02ab 00683 (main.go:14)   PCDATA  $0, $10x02ab 00683 (main.go:14)   MOVQ    "".m3+64(SP), AX0x02b0 00688 (main.go:14)   PCDATA  $0, $00x02b0 00688 (main.go:14)   MOVQ    AX, 8(SP)0x02b5 00693 (main.go:14)   PCDATA  $0, $10x02b5 00693 (main.go:14)   LEAQ    go.string."1"(SB), AX0x02bc 00700 (main.go:14)   PCDATA  $0, $00x02bc 00700 (main.go:14)   MOVQ    AX, 16(SP)0x02c1 00705 (main.go:14)   MOVQ    $1, 24(SP)0x02ca 00714 (main.go:14)   CALL    runtime.mapassign_faststr(SB)0x02cf 00719 (main.go:14)   PCDATA  $0, $40x02cf 00719 (main.go:14)   MOVQ    32(SP), DI0x02d4 00724 (main.go:14)   MOVQ    DI, ""..autotmp_24+160(SP)0x02dc 00732 (main.go:14)   TESTB   AL, (DI)0x02de 00734 (main.go:14)   MOVQ    $1, 8(DI)0x02e6 00742 (main.go:14)   PCDATA  $0, $-20x02e6 00742 (main.go:14)   PCDATA  $1, $-20x02e6 00742 (main.go:14)   CMPL    runtime.writeBarrier(SB), $00x02ed 00749 (main.go:14)   JEQ 7560x02ef 00751 (main.go:14)   JMP 18640x02f4 00756 (main.go:14)   LEAQ    go.string."2"(SB), AX0x02fb 00763 (main.go:14)   MOVQ    AX, (DI)0x02fe 00766 (main.go:14)   JMP 7680x0300 00768 (main.go:15)   PCDATA  $0, $10x0300 00768 (main.go:15)   PCDATA  $1, $30x0300 00768 (main.go:15)   MOVQ    "".m3+64(SP), AX0x0305 00773 (main.go:15)   PCDATA  $0, $00x0305 00773 (main.go:15)   PCDATA  $1, $40x0305 00773 (main.go:15)   MOVQ    AX, ""..autotmp_10+200(SP)0x030d 00781 (main.go:15)   PCDATA  $0, $40x030d 00781 (main.go:15)   PCDATA  $1, $70x030d 00781 (main.go:15)   LEAQ    ""..autotmp_11+544(SP), DI0x0315 00789 (main.go:15)   XORPS   X0, X00x0318 00792 (main.go:15)   PCDATA  $0, $00x0318 00792 (main.go:15)   LEAQ    -32(DI), DI0x031c 00796 (main.go:15)   DUFFZERO    $2730x032f 00815 (main.go:15)   PCDATA  $0, $10x032f 00815 (main.go:15)   LEAQ    type.map[string]string(SB), AX0x0336 00822 (main.go:15)   PCDATA  $0, $00x0336 00822 (main.go:15)   MOVQ    AX, (SP)0x033a 00826 (main.go:15)   PCDATA  $0, $10x033a 00826 (main.go:15)   PCDATA  $1, $80x033a 00826 (main.go:15)   MOVQ    ""..autotmp_10+200(SP), AX0x0342 00834 (main.go:15)   PCDATA  $0, $00x0342 00834 (main.go:15)   MOVQ    AX, 8(SP)0x0347 00839 (main.go:15)   PCDATA  $0, $10x0347 00839 (main.go:15)   LEAQ    ""..autotmp_11+544(SP), AX0x034f 00847 (main.go:15)   PCDATA  $0, $00x034f 00847 (main.go:15)   MOVQ    AX, 16(SP)0x0354 00852 (main.go:15)   CALL    runtime.mapiterinit(SB)0x0359 00857 (main.go:15)   JMP 8590x035b 00859 (main.go:15)   CMPQ    ""..autotmp_11+544(SP), $00x0364 00868 (main.go:15)   JNE 8750x0366 00870 (main.go:15)   JMP 13290x036b 00875 (main.go:15)   PCDATA  $0, $10x036b 00875 (main.go:15)   MOVQ    ""..autotmp_11+552(SP), AX0x0373 00883 (main.go:15)   TESTB   AL, (AX)0x0375 00885 (main.go:15)   MOVQ    8(AX), CX0x0379 00889 (main.go:15)   MOVQ    (AX), AX0x037c 00892 (main.go:15)   PCDATA  $0, $00x037c 00892 (main.go:15)   PCDATA  $1, $90x037c 00892 (main.go:15)   MOVQ    AX, ""..autotmp_25+288(SP)0x0384 00900 (main.go:15)   MOVQ    CX, ""..autotmp_25+296(SP)0x038c 00908 (main.go:15)   PCDATA  $0, $10x038c 00908 (main.go:15)   MOVQ    ""..autotmp_11+544(SP), AX0x0394 00916 (main.go:15)   TESTB   AL, (AX)0x0396 00918 (main.go:15)   MOVQ    8(AX), CX0x039a 00922 (main.go:15)   MOVQ    (AX), AX0x039d 00925 (main.go:15)   PCDATA  $0, $00x039d 00925 (main.go:15)   PCDATA  $1, $100x039d 00925 (main.go:15)   MOVQ    AX, "".k+256(SP)0x03a5 00933 (main.go:15)   MOVQ    CX, "".k+264(SP)0x03ad 00941 (main.go:15)   MOVQ    ""..autotmp_25+296(SP), AX0x03b5 00949 (main.go:15)   PCDATA  $0, $50x03b5 00949 (main.go:15)   PCDATA  $1, $110x03b5 00949 (main.go:15)   MOVQ    ""..autotmp_25+288(SP), CX0x03bd 00957 (main.go:15)   PCDATA  $0, $00x03bd 00957 (main.go:15)   PCDATA  $1, $120x03bd 00957 (main.go:15)   MOVQ    CX, "".v+240(SP)0x03c5 00965 (main.go:15)   MOVQ    AX, "".v+248(SP)0x03cd 00973 (main.go:16)   MOVQ    "".k+264(SP), AX0x03d5 00981 (main.go:16)   PCDATA  $0, $50x03d5 00981 (main.go:16)   PCDATA  $1, $130x03d5 00981 (main.go:16)   MOVQ    "".k+256(SP), CX0x03dd 00989 (main.go:16)   PCDATA  $0, $00x03dd 00989 (main.go:16)   MOVQ    CX, (SP)0x03e1 00993 (main.go:16)   MOVQ    AX, 8(SP)0x03e6 00998 (main.go:16)   CALL    runtime.convTstring(SB)0x03eb 01003 (main.go:16)   PCDATA  $0, $10x03eb 01003 (main.go:16)   MOVQ    16(SP), AX0x03f0 01008 (main.go:16)   PCDATA  $0, $00x03f0 01008 (main.go:16)   PCDATA  $1, $140x03f0 01008 (main.go:16)   MOVQ    AX, ""..autotmp_26+152(SP)0x03f8 01016 (main.go:16)   PCDATA  $1, $150x03f8 01016 (main.go:16)   XORPS   X0, X00x03fb 01019 (main.go:16)   MOVUPS  X0, ""..autotmp_9+272(SP)0x0403 01027 (main.go:16)   PCDATA  $0, $10x0403 01027 (main.go:16)   PCDATA  $1, $140x0403 01027 (main.go:16)   LEAQ    ""..autotmp_9+272(SP), AX0x040b 01035 (main.go:16)   MOVQ    AX, ""..autotmp_28+144(SP)0x0413 01043 (main.go:16)   TESTB   AL, (AX)0x0415 01045 (main.go:16)   PCDATA  $0, $20x0415 01045 (main.go:16)   PCDATA  $1, $130x0415 01045 (main.go:16)   MOVQ    ""..autotmp_26+152(SP), CX0x041d 01053 (main.go:16)   PCDATA  $0, $30x041d 01053 (main.go:16)   LEAQ    type.string(SB), DX0x0424 01060 (main.go:16)   PCDATA  $0, $20x0424 01060 (main.go:16)   MOVQ    DX, ""..autotmp_9+272(SP)0x042c 01068 (main.go:16)   PCDATA  $0, $10x042c 01068 (main.go:16)   MOVQ    CX, ""..autotmp_9+280(SP)0x0434 01076 (main.go:16)   TESTB   AL, (AX)0x0436 01078 (main.go:16)   JMP 10800x0438 01080 (main.go:16)   MOVQ    AX, ""..autotmp_27+424(SP)0x0440 01088 (main.go:16)   MOVQ    $1, ""..autotmp_27+432(SP)0x044c 01100 (main.go:16)   MOVQ    $1, ""..autotmp_27+440(SP)0x0458 01112 (main.go:16)   PCDATA  $0, $00x0458 01112 (main.go:16)   MOVQ    AX, (SP)0x045c 01116 (main.go:16)   MOVQ    $1, 8(SP)0x0465 01125 (main.go:16)   MOVQ    $1, 16(SP)0x046e 01134 (main.go:16)   CALL    fmt.Println(SB)0x0473 01139 (main.go:17)   MOVQ    "".v+248(SP), AX0x047b 01147 (main.go:17)   PCDATA  $0, $50x047b 01147 (main.go:17)   PCDATA  $1, $80x047b 01147 (main.go:17)   MOVQ    "".v+240(SP), CX0x0483 01155 (main.go:17)   PCDATA  $0, $00x0483 01155 (main.go:17)   MOVQ    CX, (SP)0x0487 01159 (main.go:17)   MOVQ    AX, 8(SP)0x048c 01164 (main.go:17)   CALL    runtime.convTstring(SB)0x0491 01169 (main.go:17)   PCDATA  $0, $10x0491 01169 (main.go:17)   MOVQ    16(SP), AX0x0496 01174 (main.go:17)   PCDATA  $0, $00x0496 01174 (main.go:17)   PCDATA  $1, $160x0496 01174 (main.go:17)   MOVQ    AX, ""..autotmp_29+136(SP)0x049e 01182 (main.go:17)   PCDATA  $1, $170x049e 01182 (main.go:17)   XORPS   X0, X00x04a1 01185 (main.go:17)   MOVUPS  X0, ""..autotmp_9+272(SP)0x04a9 01193 (main.go:17)   PCDATA  $0, $10x04a9 01193 (main.go:17)   PCDATA  $1, $160x04a9 01193 (main.go:17)   LEAQ    ""..autotmp_9+272(SP), AX0x04b1 01201 (main.go:17)   MOVQ    AX, ""..autotmp_31+128(SP)0x04b9 01209 (main.go:17)   TESTB   AL, (AX)0x04bb 01211 (main.go:17)   PCDATA  $0, $20x04bb 01211 (main.go:17)   PCDATA  $1, $80x04bb 01211 (main.go:17)   MOVQ    ""..autotmp_29+136(SP), CX0x04c3 01219 (main.go:17)   PCDATA  $0, $30x04c3 01219 (main.go:17)   LEAQ    type.string(SB), DX0x04ca 01226 (main.go:17)   PCDATA  $0, $20x04ca 01226 (main.go:17)   MOVQ    DX, ""..autotmp_9+272(SP)0x04d2 01234 (main.go:17)   PCDATA  $0, $10x04d2 01234 (main.go:17)   MOVQ    CX, ""..autotmp_9+280(SP)0x04da 01242 (main.go:17)   TESTB   AL, (AX)0x04dc 01244 (main.go:17)   JMP 12460x04de 01246 (main.go:17)   MOVQ    AX, ""..autotmp_30+400(SP)0x04e6 01254 (main.go:17)   MOVQ    $1, ""..autotmp_30+408(SP)0x04f2 01266 (main.go:17)   MOVQ    $1, ""..autotmp_30+416(SP)0x04fe 01278 (main.go:17)   PCDATA  $0, $00x04fe 01278 (main.go:17)   MOVQ    AX, (SP)0x0502 01282 (main.go:17)   MOVQ    $1, 8(SP)0x050b 01291 (main.go:17)   MOVQ    $1, 16(SP)0x0514 01300 (main.go:17)   CALL    fmt.Println(SB)0x0519 01305 (main.go:17)   JMP 13070x051b 01307 (main.go:15)   PCDATA  $0, $10x051b 01307 (main.go:15)   LEAQ    ""..autotmp_11+544(SP), AX0x0523 01315 (main.go:15)   PCDATA  $0, $00x0523 01315 (main.go:15)   MOVQ    AX, (SP)0x0527 01319 (main.go:15)   CALL    runtime.mapiternext(SB)0x052c 01324 (main.go:15)   JMP 8590x0531 01329 (main.go:19)   PCDATA  $0, $10x0531 01329 (main.go:19)   PCDATA  $1, $30x0531 01329 (main.go:19)   LEAQ    type.map[string]string(SB), AX0x0538 01336 (main.go:19)   PCDATA  $0, $00x0538 01336 (main.go:19)   MOVQ    AX, (SP)0x053c 01340 (main.go:19)   PCDATA  $0, $10x053c 01340 (main.go:19)   MOVQ    "".m3+64(SP), AX0x0541 01345 (main.go:19)   PCDATA  $0, $00x0541 01345 (main.go:19)   MOVQ    AX, 8(SP)0x0546 01350 (main.go:19)   PCDATA  $0, $10x0546 01350 (main.go:19)   LEAQ    go.string."1"(SB), AX0x054d 01357 (main.go:19)   PCDATA  $0, $00x054d 01357 (main.go:19)   MOVQ    AX, 16(SP)0x0552 01362 (main.go:19)   MOVQ    $1, 24(SP)0x055b 01371 (main.go:19)   CALL    runtime.mapaccess1_faststr(SB)0x0560 01376 (main.go:19)   PCDATA  $0, $10x0560 01376 (main.go:19)   MOVQ    32(SP), AX0x0565 01381 (main.go:19)   PCDATA  $0, $20x0565 01381 (main.go:19)   MOVQ    (AX), CX0x0568 01384 (main.go:19)   PCDATA  $0, $50x0568 01384 (main.go:19)   MOVQ    8(AX), AX0x056c 01388 (main.go:19)   MOVQ    CX, "".v1+224(SP)0x0574 01396 (main.go:19)   MOVQ    AX, "".v1+232(SP)0x057c 01404 (main.go:20)   PCDATA  $0, $00x057c 01404 (main.go:20)   MOVQ    CX, (SP)0x0580 01408 (main.go:20)   MOVQ    AX, 8(SP)0x0585 01413 (main.go:20)   CALL    runtime.convTstring(SB)0x058a 01418 (main.go:20)   PCDATA  $0, $10x058a 01418 (main.go:20)   MOVQ    16(SP), AX0x058f 01423 (main.go:20)   PCDATA  $0, $00x058f 01423 (main.go:20)   PCDATA  $1, $180x058f 01423 (main.go:20)   MOVQ    AX, ""..autotmp_32+120(SP)0x0594 01428 (main.go:20)   PCDATA  $1, $190x0594 01428 (main.go:20)   XORPS   X0, X00x0597 01431 (main.go:20)   MOVUPS  X0, ""..autotmp_9+272(SP)0x059f 01439 (main.go:20)   PCDATA  $0, $10x059f 01439 (main.go:20)   PCDATA  $1, $180x059f 01439 (main.go:20)   LEAQ    ""..autotmp_9+272(SP), AX0x05a7 01447 (main.go:20)   MOVQ    AX, ""..autotmp_34+112(SP)0x05ac 01452 (main.go:20)   TESTB   AL, (AX)0x05ae 01454 (main.go:20)   PCDATA  $0, $20x05ae 01454 (main.go:20)   PCDATA  $1, $30x05ae 01454 (main.go:20)   MOVQ    ""..autotmp_32+120(SP), CX0x05b3 01459 (main.go:20)   PCDATA  $0, $30x05b3 01459 (main.go:20)   LEAQ    type.string(SB), DX0x05ba 01466 (main.go:20)   PCDATA  $0, $20x05ba 01466 (main.go:20)   MOVQ    DX, ""..autotmp_9+272(SP)0x05c2 01474 (main.go:20)   PCDATA  $0, $10x05c2 01474 (main.go:20)   MOVQ    CX, ""..autotmp_9+280(SP)0x05ca 01482 (main.go:20)   TESTB   AL, (AX)0x05cc 01484 (main.go:20)   JMP 14860x05ce 01486 (main.go:20)   MOVQ    AX, ""..autotmp_33+376(SP)0x05d6 01494 (main.go:20)   MOVQ    $1, ""..autotmp_33+384(SP)0x05e2 01506 (main.go:20)   MOVQ    $1, ""..autotmp_33+392(SP)0x05ee 01518 (main.go:20)   PCDATA  $0, $00x05ee 01518 (main.go:20)   MOVQ    AX, (SP)0x05f2 01522 (main.go:20)   MOVQ    $1, 8(SP)0x05fb 01531 (main.go:20)   MOVQ    $1, 16(SP)0x0604 01540 (main.go:20)   CALL    fmt.Println(SB)0x0609 01545 (main.go:21)   XORPS   X0, X00x060c 01548 (main.go:21)   MOVUPS  X0, ""..autotmp_12+336(SP)0x0614 01556 (main.go:21)   PCDATA  $0, $10x0614 01556 (main.go:21)   LEAQ    type.map[string]string(SB), AX0x061b 01563 (main.go:21)   PCDATA  $0, $00x061b 01563 (main.go:21)   MOVQ    AX, (SP)0x061f 01567 (main.go:21)   PCDATA  $0, $10x061f 01567 (main.go:21)   PCDATA  $1, $00x061f 01567 (main.go:21)   MOVQ    "".m3+64(SP), AX0x0624 01572 (main.go:21)   PCDATA  $0, $00x0624 01572 (main.go:21)   MOVQ    AX, 8(SP)0x0629 01577 (main.go:21)   PCDATA  $0, $10x0629 01577 (main.go:21)   LEAQ    go.string."1"(SB), AX0x0630 01584 (main.go:21)   PCDATA  $0, $00x0630 01584 (main.go:21)   MOVQ    AX, 16(SP)0x0635 01589 (main.go:21)   MOVQ    $1, 24(SP)0x063e 01598 (main.go:21)   CALL    runtime.mapaccess2_faststr(SB)0x0643 01603 (main.go:21)   PCDATA  $0, $10x0643 01603 (main.go:21)   MOVQ    32(SP), AX0x0648 01608 (main.go:21)   PCDATA  $0, $00x0648 01608 (main.go:21)   PCDATA  $1, $200x0648 01608 (main.go:21)   MOVQ    AX, ""..autotmp_35+104(SP)0x064d 01613 (main.go:21)   MOVBLZX 40(SP), AX0x0652 01618 (main.go:21)   MOVB    AL, ""..autotmp_13+55(SP)0x0656 01622 (main.go:21)   PCDATA  $0, $10x0656 01622 (main.go:21)   PCDATA  $1, $00x0656 01622 (main.go:21)   MOVQ    ""..autotmp_35+104(SP), AX0x065b 01627 (main.go:21)   MOVQ    8(AX), CX0x065f 01631 (main.go:21)   MOVQ    (AX), AX0x0662 01634 (main.go:21)   MOVQ    AX, ""..autotmp_12+336(SP)0x066a 01642 (main.go:21)   MOVQ    CX, ""..autotmp_12+344(SP)0x0672 01650 (main.go:21)   PCDATA  $0, $00x0672 01650 (main.go:21)   PCDATA  $1, $210x0672 01650 (main.go:21)   MOVQ    AX, "".v2+208(SP)0x067a 01658 (main.go:21)   MOVQ    CX, "".v2+216(SP)0x0682 01666 (main.go:21)   MOVBLZX ""..autotmp_13+55(SP), AX0x0687 01671 (main.go:21)   MOVB    AL, "".ok+54(SP)0x068b 01675 (main.go:21)   CMPB    "".ok+54(SP), $00x0690 01680 (main.go:21)   JNE 16870x0692 01682 (main.go:21)   JMP 18620x0697 01687 (main.go:22)   MOVQ    "".v2+216(SP), AX0x069f 01695 (main.go:22)   PCDATA  $0, $50x069f 01695 (main.go:22)   PCDATA  $1, $00x069f 01695 (main.go:22)   MOVQ    "".v2+208(SP), CX0x06a7 01703 (main.go:22)   PCDATA  $0, $00x06a7 01703 (main.go:22)   MOVQ    CX, (SP)0x06ab 01707 (main.go:22)   MOVQ    AX, 8(SP)0x06b0 01712 (main.go:22)   CALL    runtime.convTstring(SB)0x06b5 01717 (main.go:22)   PCDATA  $0, $10x06b5 01717 (main.go:22)   MOVQ    16(SP), AX0x06ba 01722 (main.go:22)   PCDATA  $0, $00x06ba 01722 (main.go:22)   PCDATA  $1, $220x06ba 01722 (main.go:22)   MOVQ    AX, ""..autotmp_36+96(SP)0x06bf 01727 (main.go:22)   PCDATA  $1, $230x06bf 01727 (main.go:22)   XORPS   X0, X00x06c2 01730 (main.go:22)   MOVUPS  X0, ""..autotmp_9+272(SP)0x06ca 01738 (main.go:22)   PCDATA  $0, $10x06ca 01738 (main.go:22)   PCDATA  $1, $220x06ca 01738 (main.go:22)   LEAQ    ""..autotmp_9+272(SP), AX0x06d2 01746 (main.go:22)   MOVQ    AX, ""..autotmp_38+88(SP)0x06d7 01751 (main.go:22)   TESTB   AL, (AX)0x06d9 01753 (main.go:22)   PCDATA  $0, $20x06d9 01753 (main.go:22)   PCDATA  $1, $00x06d9 01753 (main.go:22)   MOVQ    ""..autotmp_36+96(SP), CX0x06de 01758 (main.go:22)   PCDATA  $0, $30x06de 01758 (main.go:22)   LEAQ    type.string(SB), DX0x06e5 01765 (main.go:22)   PCDATA  $0, $20x06e5 01765 (main.go:22)   MOVQ    DX, ""..autotmp_9+272(SP)0x06ed 01773 (main.go:22)   PCDATA  $0, $10x06ed 01773 (main.go:22)   MOVQ    CX, ""..autotmp_9+280(SP)0x06f5 01781 (main.go:22)   TESTB   AL, (AX)0x06f7 01783 (main.go:22)   JMP 17850x06f9 01785 (main.go:22)   MOVQ    AX, ""..autotmp_37+352(SP)0x0701 01793 (main.go:22)   MOVQ    $1, ""..autotmp_37+360(SP)0x070d 01805 (main.go:22)   MOVQ    $1, ""..autotmp_37+368(SP)0x0719 01817 (main.go:22)   PCDATA  $0, $00x0719 01817 (main.go:22)   MOVQ    AX, (SP)0x071d 01821 (main.go:22)   MOVQ    $1, 8(SP)0x0726 01830 (main.go:22)   MOVQ    $1, 16(SP)0x072f 01839 (main.go:22)   CALL    fmt.Println(SB)0x0734 01844 (main.go:22)   JMP 18460x0736 01846 (<unknown line number>)    PCDATA  $0, $-10x0736 01846 (<unknown line number>)    PCDATA  $1, $-10x0736 01846 (<unknown line number>)    MOVQ    640(SP), BP0x073e 01854 (<unknown line number>)    ADDQ    $648, SP0x0745 01861 (<unknown line number>)    RET0x0746 01862 (main.go:21)   JMP 18460x0748 01864 (main.go:14)   PCDATA  $0, $-20x0748 01864 (main.go:14)   PCDATA  $1, $-20x0748 01864 (main.go:14)   LEAQ    go.string."2"(SB), AX0x074f 01871 (main.go:14)   CALL    runtime.gcWriteBarrier(SB)0x0754 01876 (main.go:14)   JMP 7680x0759 01881 (main.go:14)   NOP0x0759 01881 (main.go:5)    PCDATA  $1, $-10x0759 01881 (main.go:5)    PCDATA  $0, $-20x0759 01881 (main.go:5)    CALL    runtime.morestack_noctxt(SB)0x075e 01886 (main.go:5)    PCDATA  $0, $-10x075e 01886 (main.go:5)    JMP 0

东西有点多,但是我们只分析几个重点 - main.go的第6行、第8行和第12行的map申明分别对应着main.txt23行、57行和132行,都是调用的runtime.makemap_small方法 - main.go的第10行对应着main.txt的97行runtime.makemap方法 - main.go的第14行的赋值对应着main.txt的184行runtime.mapassign_faststr方法 - main.go的第15行的循环对应着main.txt第224行的runtime.mapiterinit方法初始化得到迭代器 - main.go的第19行和第21行分别对着main.txt2个不同的方法runtime.mapaccess1_faststr和runtime.mapaccess2_faststr

runtime/map.go的方法包括上面列举的方法:runtime.makemap_small、runtime.makemap和runtime.mapiterinit方法

由于我的示例代码的map的key是string类型,故runtime.mapassign_faststr、runtime.mapaccess1_faststr和runtime.mapaccess2_faststr方法在runtime/map_faststr.go代码里面,该文件优化了string类型的key的操作:

go的map对应的结构体hmap

go的map的基础结构体是hmap,在runtime/map.go中,

// A header for a Go map.
type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.// Make sure this stays in sync with the compiler's definition.// map存储的键值对个数count     int // # live cells == size of map.  Must be first (used by len() builtin)// 表示map的一些标志位flags     uint8// map的桶的2的对数就是B值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// hash种子hash0     uint32 // hash seed// 桶,真正存数据的地方,2^B个桶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// 从oldbuckets迁移到新的buckets的进度nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)// 当key不是指针类型的数据的时候,里面会存溢出桶,这样会避免go的扫描extra *mapextra // optional fields
}

既然是map结构体,为何注释说是一个header呢?这是因为buckets和oldbuckets这2个字段并没有真正存取数据,只是存了一个指针,指向存取buckets的地址,故我们在使用过程中拷贝hmap的时候,并没有真正拷贝map的数据,只是拷贝了hmap这个结构体的一些数据。 在看看hmap的字段mapextra:

// mapextra holds fields that are not present on all maps.
type mapextra struct {// If both key and elem do not contain pointers and are inline, then we mark bucket// 如果key和value都不包括指针并且内敛,然后我们就标记bucket没有指针// type as containing no pointers. This avoids scanning such maps.// 这样会避免gc扫描// However, bmap.overflow is a pointer. In order to keep overflow buckets// 但是,虽然key和value不包括指针,但是逸出桶却是个指针,为了让逸出桶一直// alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow.// 存在,所以就把逸出桶和需要迁移的逸出桶的指针存到hamp的extra字段里面// overflow and oldoverflow are only used if key and elem do not contain pointers.// 如果key和elme包含指针,overflow和oldoverflow就不会被使用了// overflow contains overflow buckets for hmap.buckets.// oldoverflow contains overflow buckets for hmap.oldbuckets.// The indirection allows to store a pointer to the slice in hiter.overflow    *[]*bmapoldoverflow *[]*bmap// nextOverflow holds a pointer to a free overflow bucket.nextOverflow *bmap
}

如mapextra结构体为啥说有些字段并不会在所有map里面全部都有呢?mapextra是一个为了优化bucket而设计的,当key或value是指针的时候,此时overflow和oldoverflow就不会被使用,只有nextOverflow会被使用,该字段保存了预先申请的逸出桶,在没有发生扩容的时候,而一个桶或者说bmap的8个tophash都被使用完了,那么就要考虑使用逸出桶。 当key和value都没有指针的时候bucket的bmap的_type的ptrdata就是0,意味着该结构体是没有指针的,申请bmap内存的时候,会申请一个没有指针的span,这样会避免gc扫描该内存,会提高效率,但是bmap的最后一个内存块是确确实实存放指针的,所以用uintptr存储着该map的逸出桶的地址,但是由于没有指向下一个逸出桶,可能会被gc回收掉,所以就需要overflow存取指向该逸出桶的指针避免被gc回收掉。 overflow和oldoverflow的用处差不多,只是oldoverflow为了迁移使用,后面的系列会详说。

bucket的结构体bmap

bmap就是真正存数据的结构体了,bmap在源码中定义十分简单:

// A bucket for a Go map.
type bmap struct {// tophash generally contains the top byte of the hash value// for each key in this bucket. If tophash[0] < minTopHash,// tophash[0] is a bucket evacuation state instead.tophash [bucketCnt]uint8// Followed by bucketCnt keys and then bucketCnt elems.// NOTE: packing all the keys together and then all the elems together makes the// code a bit more complicated than alternating key/elem/key/elem/... but it allows// us to eliminate padding which would be needed for, e.g., map[int64]int8.// Followed by an overflow pointer.
}

但其实真正的在内存中分配的结构体是这样的:

// A bucket for a Go map.
type bmap struct {// tophash generally contains the top byte of the hash value// for each key in this bucket. If tophash[0] < minTopHash,// tophash[0] is a bucket evacuation state instead.tophash [bucketCnt]uint8keys [bucketCnt]string // 由于我举例的key是string,故我这里写stringvalues [bucketCnt]string // 由于上面我的例子的value的类型是string,故这里也是stringpointer unsafe.pointer  // 由于key和value都是指针,所以这里是一个指针// Followed by bucketCnt keys and then bucketCnt elems.// NOTE: packing all the keys together and then all the elems together makes the// code a bit more complicated than alternating key/elem/key/elem/... but it allows// us to eliminate padding which would be needed for, e.g., map[int64]int8.// Followed by an overflow pointer.
}

隐藏的字段在map源码中都是靠地址偏移来得到,tophash我们能轻易找到位置,但是如何找到keys、values和pointer呢? 在map中是这么做的:

dataOffset = unsafe.Offsetof(struct {b bmapv int64}{}.v)

在64位机器上,需要对齐8个字节,这里int64正好也是8个字节,所以恰好解决内存对齐的问题,找到v的起始地址也就是values的起始地址:

dataOffset+bucketCnt*2*sys.PtrSize // string16个字节,相当于2个指针,bucketCnt等于8

pointer的起始地址

dataOffset+bucketCnt*2*sys.PtrSize+sys.PtrSize

tophash存的是key的hash高8位,为了方便查找key,为什么keys和values分别存在一堆呢?不是k|v|k|v这种呢?由于key和value的变量类型可能不一样,对齐系数不一致,可能导致内存不紧凑而浪费内存,所以把8个keys存到一堆,8个values存到一堆,然后最后在pointer之前对齐就可以了。所以此时也能算出来整个bmap的大小:

bucketCnt*(uint8的字节为1)+bucketCnt*(string的字节16)+bucketCnt*(string的字节16)+指针大小8字节 = 272

在真正调试的时候如查看bucket里面的具体的key和value呢?这里给大家展示一个小技巧,如果大家有其他方法,可以留言讨论:

type dmap struct {tophash        [bucketCnt]uint8debugKeys      [bucketCnt]stringdebugElems     [bucketCnt]string//debugOverflows unsafe.PointerdebugOverflows uintptr
}

这是我定义的调试结构体,将该代码和bmap的结构体放到一堆,当你获取到bmap桶的地址的时候,就可以如下转换,就可以查看bmap的具体值了:

b0 := (*dmap)(add(buckets, uintptr(0)*uintptr(t.bucketsize)))println(b0.debugOverflows)

buckets假设是桶数组的起始地址,加上bucketsize就会得到第二个桶的起始地址,然后直接转型为*dmap,最后你可以可以打印出来你想查看的真实的值了。

map的整体结构

如果申请内存桶的时候又多余的溢出桶,那么mapextra的nextOverflow就会指向[]bmap其中的某块桶的地址,地址后之后的桶都是溢出桶。在一个桶装不下的时候,会去溢出桶拿桶然后bmap的overflow指向溢出桶。

总结

上面大概介绍了map的数据结构,后面系列map的具体代码分析,包括增、删、改、查、扩容等都会分篇细解,如有不足之处,请共同讨论学习。

go 生成hash_go基础之map-写在前面(一)相关推荐

  1. t - sql的阶梯:超越基础水平2:写子查询

    t - sql的阶梯:超越基础水平2:写子查询 原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/104517/ 通过格雷戈里 ...

  2. 【阿里云课程】深度生成模型基础,自编码器与变分自编码器

    大家好,继续更新有三AI与阿里天池联合推出的深度学习系列课程,本次更新内容为第11课中两节,介绍如下: 第1节:生成模型基础 本次课程是阿里天池联合有三AI推出的深度学习系列课程第11期,深度生成模型 ...

  3. 正常情况下ffmpeg生成moov是在mdat写完成之后写入

    可以分析MP4封装格式的工具比较多除了FFmpeg之外,还有一些常用工具,例如Elecard Stream Eye.MP4BOX.mp4info等:下面简单介绍一下这几款常见工具: 8.1 Eleca ...

  4. Puppet基础篇7-编写第一个完整测试模块puppet

    Puppet基础篇7-编写第一个完整测试模块puppet 零基础学习Puppet自动化配置管理系列文档 将Puppet部署到生产中第一个要编写的模块就是puppet本身,虽然puppet可以运行其它所 ...

  5. JavaScript基础小节——手写重点整理02

    接上一篇博客<JavaScript基础小节--手写重点整理01>:点这里  继续手写整理了一些我认为容易混淆的一些知识点 如图所示:

  6. 【CXY】JAVA基础 之 Map

    概述: 1.键值对,key-value,具有映射关系的数据 2.Map的key不允许重复,value可以重复 3.Map里的key类似一个Set,甚至可以通过map.keySet()拿到key的Set ...

  7. go where 不等于_go基础之map迭代(四)

    写在之前 在文章<go基础之map-写在前面(一)>的示例代码 for k, v := range m3 { fmt.Println(k, v)} 就是go的map的迭代方法,查看该代码的 ...

  8. OpenPose(一):根据关键点生成置信图(Confidence Map)

    最近读了Openpose论文,关于OpenPose的相关资料,博主整理后,留下了一个传送门,将网上写的比较好的博文和github地址记录在传送门里. 文章目录 一.置信图(Confidence Map ...

  9. java基础复习(练习写博客)

    文章目录 Java特性和优势 java三大版本 JDK.JRE.JVM(从前到后包含) Java开发环境搭建步骤 Java程序运行机制 IDE Java基础语法 一.注释.标识符.关键字 二.数据类型 ...

最新文章

  1. ios uiview 如何刷新_UIView的重绘及布局刷新
  2. send/recv阻塞和非租塞不同
  3. Java继承、重写与重载 笔记
  4. oracle db-link 分布式数据库网络配置协议错误,Oracle学习(18)【DBA向】:分布式数据库...
  5. java---面试题 丑数
  6. (二)boost库之字符串格式化
  7. mac 下载的破解软件显示 文件已损坏
  8. 2017-06-02 前端日报
  9. 读者专属福利: Git面试宝典分享
  10. java视图扩展_java – 可扩展列表视图 – 子项,不同的布局
  11. 微信小程序复制就好-JS工具eval5,用JS替换VBA处理Excel数据
  12. 单片机原理及应用 张鑫_单片机原理及应用 张鑫 课后习题答案 电子工业出版社 单片机原理及应用 张鑫 课后习题答案 电子工业出版社.doc...
  13. PHP 使用 hprose RPC 服务 系列文章之三——Laravel5.8中使用Hprose
  14. 全球及中国菖蒲根提取物行业发展规模及投资方向分析报告2022-2028年
  15. Bestcoder13 1003.Find Sequence(hdu 5064) 解题报告
  16. 环境搭建—3.0 Linaro gcc
  17. 小智-微直播 使用教程
  18. OpenSSL sm2 签名源码讲解
  19. Win10 Build 18237发布:登录屏幕启用毛玻璃特效
  20. 婚宴座位图html5,婚宴酒席座位安排图 20桌婚宴座位安排图

热门文章

  1. 【快讯】阿里云张建锋:数据成为经济发展的新生产要素
  2. 阿里云开放国内首个云端数据库测试平台,云已成为数据库新标准;华为5G随行WiFi发布;科大讯飞推出 AI 专用语音芯片系列……...
  3. 靓仔落泪,性能问题定位难倒我了
  4. 使用云原生buildpacks将你的代码转换成Docker Image | 技术头条
  5. mysql创建操作用户
  6. springboot 排除 默认的loggback 和slf4j的依赖
  7. Microsoft Windows Server 2019 1809 iso镜像 企业版本
  8. (Docker实战) 第1篇:Centos7 环境准备和安装Docker-ce
  9. SpringBoot2.0整合Mybatis-Plus多数据源
  10. 企业实战(Jenkins+GitLab+SonarQube)_02_Jenkins运行