Files
bl/common/utils/emap.go
昔念 83fb06a229 feat(utils): 重构 OrderedMap 为 OrderMap,支持排序和非阻塞遍历
- 将原有的 `OrderedMap` 替换为 `OrderMap`,基于 `map` 和 `slice` 实现,提升性能并简化逻辑
- 支持自定义 key 排序规则,若未提供则按插入顺序维护
- 提供 `Set`、`Get`、`Delete`、`Keys`、`Values` 等基本操作,均并发安全
- 引入非阻塞遍历机制(`Iter`、`Iter
2025-10-22 21:30:05 +08:00

221 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package utils
import (
"encoding/json"
"sort"
"sync"
)
// OrderMap 是一个支持排序、有序遍历、并发安全的泛型 map。
// 遍历Range / Iter / IterBuffered为非阻塞进入遍历时创建快照短时间持锁
// 然后在快照上遍历(不持锁),因此回调可以做耗时操作而不阻塞写操作。
type OrderMap[K comparable, V any] struct {
mu sync.RWMutex
data map[K]V
keys []K
less func(a, b K) bool // key 排序规则;为 nil 时按插入顺序
}
// NewOrderedMap 创建一个 OrderMapless 为 nil 时按插入顺序,否则会在写入后按 less 排序 keys。
func NewOrderedMap[K comparable, V any](less func(a, b K) bool) *OrderMap[K, V] {
return &OrderMap[K, V]{
data: make(map[K]V),
keys: make([]K, 0),
less: less,
}
}
// Set 设置键值(存在则覆盖,不存在则添加)。若提供了 less则会在插入后排序 keys。
func (om *OrderMap[K, V]) Set(k K, v V) {
om.mu.Lock()
if _, exist := om.data[k]; !exist {
om.keys = append(om.keys, k)
}
om.data[k] = v
// 若定义了 less则保持 keys 有序
if om.less != nil {
sort.Slice(om.keys, func(i, j int) bool { return om.less(om.keys[i], om.keys[j]) })
}
om.mu.Unlock()
}
// Get 获取一个键的值,返回值和是否存在(并发安全)。
func (om *OrderMap[K, V]) Get(k K) (V, bool) {
om.mu.RLock()
v, ok := om.data[k]
om.mu.RUnlock()
return v, ok
}
// Delete 删除一个键(并发安全)。
func (om *OrderMap[K, V]) Delete(k K) {
om.mu.Lock()
if _, ok := om.data[k]; ok {
delete(om.data, k)
// 从 keys 切片中移除第一次出现的 k
for i, key := range om.keys {
if key == k {
om.keys = append(om.keys[:i], om.keys[i+1:]...)
break
}
}
}
om.mu.Unlock()
}
// Keys 返回当前 keys 的有序副本(并发安全)。
func (om *OrderMap[K, V]) Keys() []K {
om.mu.RLock()
keys := make([]K, len(om.keys))
copy(keys, om.keys)
om.mu.RUnlock()
return keys
}
// Values 返回当前 values 的有序副本(并发安全)。
func (om *OrderMap[K, V]) Values() []V {
om.mu.RLock()
values := make([]V, 0, len(om.keys))
for _, k := range om.keys {
values = append(values, om.data[k])
}
om.mu.RUnlock()
return values
}
// Len 返回 map 长度(并发安全)。
func (om *OrderMap[K, V]) Len() int {
om.mu.RLock()
n := len(om.data)
om.mu.RUnlock()
return n
}
// snapshot 复制一份 keys 列表和对应的数据(短时间持锁),返回 keys 及 map 的副本。
// 注意这是浅拷贝V 为引用类型则仍为相同引用)。
func (om *OrderMap[K, V]) snapshot() ([]K, map[K]V) {
om.mu.RLock()
keys := make([]K, len(om.keys))
copy(keys, om.keys)
dataCopy := make(map[K]V, len(keys))
for _, k := range keys {
dataCopy[k] = om.data[k]
}
om.mu.RUnlock()
return keys, dataCopy
}
// Iter 返回一个无缓冲的 channel用于在 for range 中遍历所有当前项。
// Deprecated: 使用 IterBuffered() 性能更好。
func (om *OrderMap[K, V]) Iter() <-chan Tuple[K, V] {
keys, dataCopy := om.snapshot()
ch := make(chan Tuple[K, V])
go func() {
for _, k := range keys {
// 可能在原 map 中此 key 已被删除,但 snapshot 中仍存在
if v, ok := dataCopy[k]; ok {
ch <- Tuple[K, V]{Key: k, Val: v}
}
}
close(ch)
}()
return ch
}
// IterBuffered 返回一个带缓冲的 channel缓冲大小为当前元素数用于高效遍历。
func (om *OrderMap[K, V]) IterBuffered() <-chan Tuple[K, V] {
keys, dataCopy := om.snapshot()
ch := make(chan Tuple[K, V], len(keys))
go func() {
for _, k := range keys {
if v, ok := dataCopy[k]; ok {
ch <- Tuple[K, V]{Key: k, Val: v}
}
}
close(ch)
}()
return ch
}
// Range 在非阻塞快照上按有序 keys 进行遍历。f 返回 false 可提前停止遍历。
// Range 本身不会在回调期间持锁,因此回调可以做耗时操作。
func (om *OrderMap[K, V]) Range(f func(K, V) bool) {
keys, dataCopy := om.snapshot()
for _, k := range keys {
v, ok := dataCopy[k]
if !ok {
continue
}
if !f(k, v) {
break
}
}
}
// Items 返回一份 map 的浅拷贝(并发安全)。注意:返回 map 的 key 顺序不可保证map 本身无序)
// 若你需要有序的键值序列,使用 IterBuffered/Range/Keys。
func (om *OrderMap[K, V]) Items() map[K]V {
_, dataCopy := om.snapshot()
// snapshot 已是 map 的拷贝,直接返回
return dataCopy
}
// Clear 清空所有数据(并发安全)。
func (om *OrderMap[K, V]) Clear() {
om.mu.Lock()
om.data = make(map[K]V)
om.keys = make([]K, 0)
om.mu.Unlock()
}
// ----------------------------------------------------------------------------
// JSON 支持(保持有序)
// ----------------------------------------------------------------------------
// jsonPair 用于 Marshal/Unmarshal 时保留顺序的中间结构:数组形式 [{key:..., value:...}, ...]
type jsonPair[K comparable, V any] struct {
Key K `json:"key"`
Value V `json:"value"`
}
// MarshalJSON 将 OrderMap 以数组形式序列化,保持 keys 的顺序:
// [
//
// {"key": <k1>, "value": <v1>},
// {"key": <k2>, "value": <v2>},
// ...
//
// ]
func (om *OrderMap[K, V]) MarshalJSON() ([]byte, error) {
keys, dataCopy := om.snapshot()
pairs := make([]jsonPair[K, V], 0, len(keys))
for _, k := range keys {
if v, ok := dataCopy[k]; ok {
pairs = append(pairs, jsonPair[K, V]{Key: k, Value: v})
}
}
return json.Marshal(pairs)
}
// UnmarshalJSON 从上述数组格式反序列化,按数组顺序恢复 keys 与数据。
// 注意:若 Key 类型不是 JSON 可直接解码的类型,反序列化会失败。
func (om *OrderMap[K, V]) UnmarshalJSON(b []byte) error {
var pairs []jsonPair[K, V]
if err := json.Unmarshal(b, &pairs); err != nil {
return err
}
om.mu.Lock()
defer om.mu.Unlock()
om.data = make(map[K]V, len(pairs))
om.keys = make([]K, 0, len(pairs))
for _, p := range pairs {
om.data[p.Key] = p.Value
om.keys = append(om.keys, p.Key)
}
// 若定义了 less则保持 keys 排序
if om.less != nil {
sort.Slice(om.keys, func(i, j int) bool { return om.less(om.keys[i], om.keys[j]) })
}
return nil
}