【Go实战基础】最常用的高效数据结构 map

目录

一、简介

二、数据结构

三、实战

1、创建  g004.go

2、编译和运行

3、运行结果

一、简介

实际编程开发中,在任何语言里面,除了数组之外,最常用的数据结构莫就是映射 map。map是一种数据结构,在很多语言里面都内置了map类型,可以看作是键值对的映射,例如:

a => 18
b => 20
c => 25

Go 的 Map 底层是一个 hash 表,表面上看 Map 只有键值对结构,实际上在存储的过程中涉及了数组和链表,之所以高效是因为其集合了顺序存储和链式存储两种存储结构。数组是 HashMap 的主干,数组的每个元素关联了一个链表。

扫描二维码关注公众号,回复: 15096367 查看本文章

二、数据结构

在源码中,Go 表示 map 的结构体是 hmap,它是 hashmap 的“缩写”,其结构如下:

// Go hmap 结构

type hmap struct {
    // 元素个数,调用 len(map) 时,直接返回此值
    count     int

    // 标记读写状态,避免并发读写
    flags     uint8

    // 扩容常量, 可以容纳 2 ^ B 个bucket
    B         uint8

    // 溢出的 的 bucket 近似数
    noverflow uint16

    // hash种子 计算 key 的哈希的时候会传入哈希函数
    hash0     uint32

    // 指向 buckets 数组指针,大小为 2^B
    // 如果元素个数为0,就为 nil
    buckets    unsafe.Pointer

    // 扩容的时候,用于复制的 buckets 数组
    oldbuckets unsafe.Pointer

    // 扩容时已迁移的个数,小于此地址的 buckets 迁移完成
    nevacuate  uintptr

    // 记录溢出相关
    extra *mapextra // optional fields
}

// 溢出指针
type mapextra struct {
    // overflow 包含的是 hmap.buckets 的 overflow 的 bucket
    overflow    *[]*bmap

    // oldoverflow 包含扩容时的 hmap.oldbuckets 的 overflow 的 bucket
    oldoverflow *[]*bmap

    // 指向空闲的 overflow bucket 的指针
    nextOverflow *bmap
}

// 桶
type bmap struct {
    // 记录着每个key的高8个bits
    tophash [bucketCnt]uint8     
}

每个 map 的底层结构是 hmap,是有若干个结构为 bmap 的 bucket 组成的数组,每个 bucket 可以存放若干个元素(通常是 8 个),当一个bucket中的元素超过 8 个的时候,hmap 会使用 extra 中的overflow来扩展存储key。

三、菜鸟实战

实战需求: Map 的基础操作

马上安排!

1、创建  g004.go

/*
 * @Author: 菜鸟实战
 * @FilePath: /go110/go-004/g004.go
 * @Description: map 实战
 */

package main

import (
	"fmt"
	"runtime"
)

func mapTest() {

}

// 主函数
func main() {
	// 使用内置函数打印
	println("Hello", "菜鸟实战")

	// 先声明 map,再赋值
	var m1 map[string]string
	m1 = make(map[string]string)
	m1["a1"] = "1"
	m1["b1"] = "2"

	// 直接创建,然后赋值
	m2 := make(map[string]string)
	m2["a2"] = "3"
	m2["b2"] = "4"

	// 初始化赋值一体化
	m3 := map[string]string{
		"a3": "5",
		"b3": "6",
	}
	fmt.Println("## m3 信息: ")
	fmt.Println(m3)

	// 查找键值是否存在
	fmt.Println("## 检查 m2 是否存在 a2 : ")
	if v, ok := m2["a2"]; ok {
		fmt.Println(v)
	} else {
		fmt.Println("键值不存在")
	}

	// 遍历 map
	fmt.Println("## 遍历 m1 : ")
	for k, v := range m1 {
		fmt.Printf("%v => %v \n", k, v)
	}

	// 使用包函数打印
	fmt.Printf("版本: %s \n", runtime.Version())
}

2、编译和运行

# 1、生成模块依赖
go mod init g004
 
# 2、编译
go build g004.go 
 
# 3、编译后的目录结构
 
└── go-004
    ├── g004
    ├── g004.go
    └── go.mod
 
# 4、运行
go run g004

3、运行结果

Hello 菜鸟实战
## m3 信息: 
map[a3:5 b3:6]
## 检查 m2 是否存在 a2 : 
3
## 遍历 m1 : 
a1 => 1 
b1 => 2 
版本: go1.17.10 

菜鸟实战,持续学习!

猜你喜欢

转载自blog.csdn.net/qq_39816613/article/details/124911198#comments_25349574
今日推荐