HaxMap

repository·main·Indexed 21 days ago

https://github.com/alphadose/haxmap

A high-performance, concurrent hashmap for Go (1.18+) utilizing the xxHash algorithm and Harris lock-free lists. It features generics for type safety, atomic operations like CompareAndSwap, GetOrSet, and GetOrCompute, and supports Go 1.23 iterators via Iterator() and Keys().

Tokens
2.8K
Snippets
17
Records
18
Agent score
75%

What's inside haxmap

  1. Pre-allocate HaxMap size for better performance

    main

    To prevent frequent grow operations and improve performance, you can specify an initial size when calling haxmap.New[K, V](size). This is useful if you know the approximate number of elements the map will hold.

    package main
    
    import (
    	"github.com/alphadose/haxmap"
    )
    
    func main() {
    	const initialSize = 1 << 10
    
    	// pre-allocating the size of the map will prevent all grow operations
    	// until that limit is hit thereby improving performance
    	m := haxmap.New[int, string](initialSize)
    
    	m.Set(1, "1")
    	val, ok := m.Get(1)
    	if ok {
    		println(val)
    	}
    }
  2. Basic usage of HaxMap

    main

    HaxMap is a concurrent hashmap that uses generics for type safety. You can initialize a map with specific key and value types, perform CRUD operations, and iterate over entries.

    Key methods:

    • New[K, V](size): Initializes a new map. size is optional.
    • Set(key, value): Sets a value for a key (overwrites if exists).
    • Get(key): Returns the value and a boolean indicating if the key was found.
    • Del(keys...): Deletes one or more keys. Deleting a non-existent key is safe.
    • Len(): Returns the number of elements in the map.
    • ForEach(func(K, V) bool): Iterates over all pairs. The callback must return true to continue or false to break.
    package main
    
    import (
    	"fmt"
    	"github.com/alphadose/haxmap"
    )
    
    func main() {
    	// initialize map with key type `int` and value type `string`
    	mep := haxmap.New[int, string]()
    
    	// set a value (overwrites existing value if present)
    	mep.Set(1, "one")
    
    	// get the value and print it
    	val, ok := mep.Get(1)
    	if ok {
    		println(val)
    	}
    
    	mep.Set(2, "two")
    	mep.Set(3, "three")
    	mep.Set(4, "four")
    
    	// ForEach loop to iterate over all key-value pairs and execute the given lambda
    	mep.ForEach(func(key int, value string) bool {
    		fmt.Printf("Key -> %d | Value -> %s\n", key, value)
    		return true // return `true` to continue iteration and `false` to break iteration
    	})
    
    	mep.Del(1) // delete a value
    	mep.Del(0) // delete is safe even if a key doesn't exists
    
    	// bulk deletion is supported too in the same API call
    	mep.Del(2, 3, 4)
    
    	if mep.Len() == 0 {
    		println("cleanup complete")
    	}
    }
  3. Override the default hashing algorithm

    main

    By default, HaxMap uses the xxHash algorithm. You can provide a custom hasher using SetHasher. The custom function must match the signature func(keyType) uintptr.

    package main
    
    import (
    	"github.com/alphadose/haxmap"
    )
    
    // your custom hash function
    // the hash function signature must adhere to `func(keyType) uintptr`
    func customStringHasher(s string) uintptr {
    	return uintptr(len(s))
    }
    
    func main() {
    	m := haxmap.New[string, string]() // initialize a string-string map
    	m.SetHasher(customStringHasher) // this overrides the default xxHash algorithm
    
    	m.Set("one", "1")
    	val, ok := m.Get("one")
    	if ok {
    		println(val)
    	}
    }
  4. Iterate over the map with ForEach()

    main

    The ForEach(lambda func(K, V) bool) method iterates over all key-value pairs.

    • The lambda function receives the key and value.
    • To continue iteration, the lambda must return true.
    • To break (stop) iteration, the lambda must return false.
    m.ForEach(func(k string, v int) bool {
        fmt.Printf("%s: %d\n", k, v)
        return true // continue
    })
  5. Retrieve values with Get()

    main

    The Get(key K) method retrieves the value associated with the provided key. It returns the value and a boolean ok which is true if the key was found and not deleted, and false otherwise.

    val, ok := m.Get("myKey")
    if ok {
        fmt.Println("Found:", val)
    }
  6. Insert or update values with Set()

    main

    The Set(key K, value V) method inserts a new key-value pair or updates the value if the key already exists.

    Note: If a resizing operation is happening concurrently, the item might only appear in the map after the resize operation completes.

    m.Set("key1", 100)
    m.Set("key1", 200) // Updates existing key
  7. Use GetOrCompute for lazy value initialization

    main

    The GetOrCompute(key K, valueFn func() V) method is similar to GetOrSet, but the value is generated by a constructor function. The valueFn is called only once if the key is absent.

    • Returns (actual V, loaded bool) where loaded is true if the value was loaded from the map, and false if it was newly computed and stored.
    val, loaded := m.GetOrCompute("config", func() string {
        return "computed_value"
    })
  8. Delete keys with Del()

    main

    The Del(keys ...K) method removes one or more keys from the map.

    Performance Tip: Bulk deletion (passing multiple keys at once) is more efficient than calling Del multiple times for individual keys.

    // Delete a single key
    m.Del("key1")
    
    // Bulk delete (more efficient)
    m.Del("key1", "key2", "key3")
  9. Iterate over map entries with Iterator()

    main

    The Iterator() method returns an iter.Seq2[K, V] which allows you to iterate over all key-value pairs in the map using standard Go 1.23 for...range loops. Each iteration yields the key and the current value of the entry.

    for key, value := range m.Iterator() {
        fmt.Printf("Key: %v, Value: %v\n", key, value)
    }
  10. Iterate over map keys with Keys()

    main

    The Keys() method returns an iter.Seq[K] which allows you to iterate over all keys present in the map using standard Go 1.23 for...range loops. This is useful when you only need the keys and not the associated values.

    for key := range m.Keys() {
        fmt.Printf("Key: %v\n", key)
    }