线程安全泛型(Map)

1 阅读3分钟

xmap - 线程安全的泛型Map库

参考代码 点击直达

简介

xmap 是一个基于Go泛型实现的线程安全Map库,提供了丰富的操作方法,适用于并发场景下的键值对存储需求。通过读写锁(RWMutex) 保证并发安全,同时利用泛型特性支持任意可比较的键类型和任意值类型。

特性

  • 线程安全:使用读写锁(RWMutex)保证并发安全
  • 泛型支持:支持任意可比较的键类型和任意值类型
  • 丰富API:提供完整的增删改查操作方法
  • 性能优化:支持初始化容量设置,减少map扩容开销
  • 实用功能:提供遍历、条件设置等便捷方法

快速开始

创建XMap实例

// 创建一个初始容量为10的XMap
var m = xmap.New[string, int](10)

基本操作

// 设置键值对
m.Set("age", 25)
m.Set("score", 100)

// 获取值
if val, ok := m.Get("age"); ok {
    fmt.Println("age:", val)
}

// 检查键是否存在
if m.Contains("name") {
    fmt.Println("name exists")
}

// 获取元素个数
fmt.Println("size:", m.Len())

// 删除键
m.Remove("score")
fmt.Println("size:", m.Len())

// 清空所有元素
m.Clear()
fmt.Println("size:", m.Len())

类型定义

XMap[K, V] 泛型线程安全的Map结构体。

  • K:键类型,必须实现comparable接口
  • V:值类型,可以是任意类型

RecordType[K, V]

键值对记录类型,用于返回键值对组合。

type RecordType[K comparable, V any] struct {
    Key   K
    Value V
}

构造函数

New[K comparable, V any](size int) *XMap[K, V]

创建指定初始容量的XMap实例。

  • 参数:

    • size 初始容量,用于底层map的初始化
  • 返回值:

    • *XMap[K, V]:XMap实例指针

核心方法

Set(key K, value V)

设置键值对。如果键已存在,则更新对应的值。

m.Set("name", "张三")
m.Set("age", 30)

Get(key K) (V, bool)

获取指定键的值。

返回值:

  • V:键对应的值
  • bool:键是否存在
if val, ok := m.Get("name"); ok {
    fmt.Println(val)
}

Remove(key K)

删除指定键及其对应的值。

m.Remove("name")

Clear()

清空Map中的所有数据。重新分配新的map,保持初始容量。

m.Clear()

查询方法

Keys() []K

返回所有键的切片。

var keys = m.Keys()
for _, k := range keys {
    fmt.Println(k)
}

Values() []V

返回所有值的切片。

var values = m.Values()
for _, v := range values {
    fmt.Println(v)
}

Records() []*RecordType[K, V]

返回所有键值对记录的切片。

var records = m.Records()
for _, r := range records {
    fmt.Printf("Key: %v, Value: %v\n", r.Key, r.Value)
}

All() map[K]V

返回底层map。注意:返回的是原始map引用,修改会影响内部数据。

var data = m.All()
data["new"] = 100 // 会修改原始数据

AllCopy() map[K]V

返回底层map的副本。修改副本不会影响内部数据。

var data = m.AllCopy()
data["new"] = 100 // 不会影响原始数据

Len() int

返回当前元素个数。

size := m.Len()

Contains(key K) bool

检查指定键是否存在。

if m.Contains("name") {
    fmt.Println("键存在")
}

实用方法

GetOrSet(key K, defaultValue V) V

获取指定键的值,如果键不存在则设置默认值并返回。

// 如果"count"不存在,则设置为0并返回0
count := m.GetOrSet("count", 0)

SetIfAbsent(key K, value V) bool

如果键不存在则设置值。

返回值:

  • true:设置成功(键不存在)
  • false:设置失败(键已存在)
if m.SetIfAbsent("name", "李四") {
    fmt.Println("设置成功")
} else {
    fmt.Println("键已存在")
}

ForEach(fn func(key K, value V))

遍历所有元素,对每个键值对执行指定函数。

m.ForEach(func(key string, value int) {
    fmt.Printf("Key: %s, Value: %d\n", key, value)
})

性能优化

  • 初始化容量:通过New(size)指定合适的初始容量,可以减少map扩容带来的性能开销
  • 读多写少场景:使用读写锁,多个goroutine可以同时读取
  • 返回副本:AllCopy()方法返回map副本,避免外部修改影响内部数据

注意事项

  • All()方法返回的map是内部数据的直接引用,修改会影响原始数据
  • 迭代顺序不保证与插入顺序一致
  • 在遍历过程中(ForEach)不允许修改map结构(增删元素)