Documentation
¶
Overview ¶
Package smap is a zero dependencies library that provides a generic, concurrent safe map with go1.24 iterators including optionally ordered keys iteration.
This was originally developed for fortio.org/tsync which uses and battle tests it including for race conditions (there aren't any!).
Example ¶
Example demonstrates basic usage of the concurrent safe Map including creating, setting, getting, and iterating with All(). The whole point though would be do to these operations concurrently from multiple goroutines.
// Create a new concurrent safe map
m := New[string, int]()
// Add some entries
m.Set("apple", 5)
m.Set("banana", 3)
m.Set("cherry", 8)
// Get a specific value
count, exists := m.Get("banana")
if exists {
fmt.Printf("Bananas: %d\n", count)
}
// Check the total count
fmt.Printf("Total items: %d\n", m.Len())
// Check if a key exists
fmt.Printf("Has apple: %t\n", m.Has("apple"))
// Iterate over all entries using range
// Note: We collect and sort for deterministic output in this example
var toDelete []string
// Using m.All you can't mutate the map during iteration (use AllSorted or collect changes first, or
// use KeysSnapshot or KeysValuesSnapshot first then mutate)
for fruit, count := range m.All() {
if count < 8 {
toDelete = append(toDelete, fruit)
}
}
m.Delete(toDelete...) // Delete multiple, after iteration
fmt.Printf("After removing items with count < 8, total items: %d\n", m.Len())
fmt.Printf("Map is now just %v\n", m)
Output: Bananas: 3 Total items: 3 Has apple: true After removing items with count < 8, total items: 1 Map is now just map[cherry:8]
Index ¶
- func NaturalSort[Q cmp.Ordered, V any](s *Map[Q, V]) iter.Seq2[Q, V]
- type KV
- type Map
- func (s *Map[K, V]) All() iter.Seq2[K, V]
- func (s *Map[K, V]) AllSorted(less func(a, b K) bool) iter.Seq2[K, V]
- func (s *Map[K, V]) Clear() (newVersion uint64)
- func (s *Map[K, V]) Clone() *Map[K, V]
- func (s *Map[K, V]) Copy(src *Map[K, V]) (newVersion uint64)
- func (s *Map[K, V]) Delete(key ...K) (newVersion uint64)
- func (s *Map[K, V]) Get(key K) (V, bool)
- func (s *Map[K, V]) GoString() (debug string)
- func (s *Map[K, V]) Has(key K) bool
- func (s *Map[K, V]) Keys() iter.Seq[K]
- func (s *Map[K, V]) KeysSnapshot() []K
- func (s *Map[K, V]) KeysSorted(less func(a, b K) bool) iter.Seq[K]
- func (s *Map[K, V]) KeysValuesSnapshot() []KV[K, V]
- func (s *Map[K, V]) Len() int
- func (s *Map[K, V]) Set(key K, value V) (newVersion uint64)
- func (s *Map[K, V]) SetBatch(kvs []KV[K, V]) (newVersion uint64)
- func (s *Map[K, V]) String() (debug string)
- func (s *Map[K, V]) Transaction(fn func(m map[K]V)) (newVersion uint64)
- func (s *Map[K, V]) Values() iter.Seq[V]
- func (s *Map[K, V]) Version() (current uint64)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NaturalSort ¶
NaturalSort returns an iterator that visits key-value pairs in the natural order of Q (using <). This requires Q (K from the Map[Q, V]) to be an ordered type.
Example ¶
ExampleNaturalSort demonstrates sorting with naturally ordered types.
// Create a map with ordered keys (strings, ints, etc.)
scores := New[string, int]()
scores.Set("Charlie", 85)
scores.Set("Alice", 92)
scores.Set("Bob", 78)
// Iterate in natural (alphabetical) order
fmt.Println("Scores (alphabetically):")
for name, score := range NaturalSort(scores) {
fmt.Printf(" %s: %d\n", name, score)
}
Output: Scores (alphabetically): Alice: 92 Bob: 78 Charlie: 85
Types ¶
type KV ¶
type KV[K comparable, V any] struct { Key K Value V }
KV is a key-value pair used for Map.SetBatch and returned by Map.KeysValuesSnapshot (optional).
type Map ¶
type Map[K comparable, V any] struct { // contains filtered or unexported fields }
Map is a concurrent safe map.
func FromMap ¶ added in v1.0.0
func FromMap[K comparable, V any](m map[K]V) *Map[K, V]
FromMap creates a new Map from the provided standard map by cloning its contents. Passing a nil map results in a Map with a nil underlying map which will not work.
func Transfer ¶ added in v1.0.0
func Transfer[K comparable, V any](m map[K]V) *Map[K, V]
Transfer moves the input map into a new concurrent safe Map. You must not use the input map after calling Transfer or it would defeat the concurrent safety. In most case use FromMap instead to clone the input map and not take ownership of it. Passing a nil map results in a Map with a nil underlying map which will not work.
func (*Map[K, V]) All ¶
All returns an iterator over key-value pairs from the map. This allows ranging over the sync Map like a regular map using Go 1.24+ iterators. The iteration takes a read lock for the duration of going over the entries. If you wish to modify the map during iteration, you should postpone to after the loop or use Map.AllSorted or NaturalSort which are doing 2 phases and the 2nd phase is without holding a lock. If using this one and wanting to mutate the map, accumulate entries in a slice and call Delete(toDeleteSlice...) or Map.SetBatch for instance.
func (*Map[K, V]) AllSorted ¶
AllSorted returns an iterator over key-value pairs where keys are visited in the order defined by less. Unlike Map.All only the keys snapshot occurs under a read lock, then sorting and value lookups happen without holding it. Because of that, by the time a key is revisited later, it may have been deleted; such entries are skipped.
Example ¶
ExampleMap_AllSorted demonstrates using AllSorted with a custom struct to iterate over entries in a specific order.
// Define a custom struct with multiple fields
type Task struct {
Name string
Priority int
}
// Create a map with Task keys
m := New[Task, string]()
// Add some tasks
m.Set(Task{Name: "Fix bug", Priority: 1}, "In Progress")
m.Set(Task{Name: "Write docs", Priority: 3}, "Not Started")
m.Set(Task{Name: "Review PR", Priority: 2}, "Completed")
m.Set(Task{Name: "Deploy", Priority: 1}, "Pending")
// Iterate in priority order (lowest priority number first)
// If priorities are equal, sort by name
fmt.Println("Tasks by priority:")
for task, status := range m.AllSorted(func(a, b Task) bool {
if a.Priority != b.Priority {
return a.Priority < b.Priority
}
return a.Name < b.Name
}) {
// Here it's ok to mutate during iteration:
if task.Priority == 1 {
m.Set(task, "Changed") // Update high priority tasks
}
newStatus, _ := m.Get(task)
fmt.Printf(" [P%d] %s: %s (current: %s)\n", task.Priority, task.Name, status, newStatus)
}
Output: Tasks by priority: [P1] Deploy: Pending (current: Changed) [P1] Fix bug: In Progress (current: Changed) [P2] Review PR: Completed (current: Completed) [P3] Write docs: Not Started (current: Not Started)
func (*Map[K, V]) Copy ¶ added in v1.0.0
Copy copies all key/value pairs from src adding them to the current map. When a key in src is already present in this map, the value will be overwritten by the value associated with the key in src. Read lock is acquired on src and write lock on this map during the operation. The new version of the map is returned (previous + 1). Because of this calling Copy in both directions between 2 maps would lead to a deadlock.
func (*Map[K, V]) Delete ¶
Delete removes the given keys from the map and returns the new version of the map.
func (*Map[K, V]) GoString ¶ added in v1.0.0
GoString() returns a string representation of the map for debugging purposes (%#v).
func (*Map[K, V]) Keys ¶
Keys returns an iterator over keys from the map. This allows ranging over just the keys using Go 1.24+ iterators. Like Map.All this iterator takes a read lock for the duration of going over the entries.
func (*Map[K, V]) KeysSnapshot ¶ added in v1.0.0
func (s *Map[K, V]) KeysSnapshot() []K
KeysSnapshot returns a snapshot slice of the current keys in the map. The snapshot is taken under a read lock.
func (*Map[K, V]) KeysSorted ¶
KeysSorted returns an iterator over keys sorted using the provided comparison function. Unlike Map.Keys, the map snapshot occurs under a read lock, but then sorting happens without holding the lock.
func (*Map[K, V]) KeysValuesSnapshot ¶ added in v1.0.0
KeysValuesSnapshot returns a snapshot slice of the current key-value pairs in the map. The snapshot is taken under a read lock.
func (*Map[K, V]) Set ¶
Set sets the value for the given key and returns the new version of the map.
func (*Map[K, V]) SetBatch ¶ added in v1.0.0
SetBatch sets multiple key-value pairs in a single lock/unlock batch (and returns the new version of the map).
func (*Map[K, V]) String ¶ added in v1.0.0
String() returns a string representation of the map for debugging purposes (%s/%v).
func (*Map[K, V]) Transaction ¶ added in v1.1.0
Transaction executes the provided function fn within a write lock, with access to the internal map.