/go-defaultdict

Go implementation of Python's defaultdict, kind of

Primary LanguageGoBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Go Reference Go Report Card

go-defaultdict

Go implementation of Python's defaultdict, in a way that's both thread-safe and memory efficient.

Overview

Underneath it pairs a sync.Map with a sync.Pool, and removed all direct store/write accesses to the map. As a result, the only way to mutate the map is through Load/Get, (which either create a new value for you if this is the first access to the key, or return the value created by a previous Load/Get), then mutate the value returned directly (in a thread-safe way).

Here are 2 example usages:

  1. To implement a rowlock. See my rowlock package for detailed example.

  2. To implement a concurrent counter-by-key. See package example or below for details.

Example Code

Here's a step-by-step example to create a concurrent counter-by-key.

First, create a generator, which simply returns an *int64 so it can be used by atomic int64 functions:

generator := func() defaultdict.Pointer {
  return new(int64)
}

Then, create the map:

m := defaultdict.New(generator)

When you need to add the counter, get by key then use atomic.AddInt64:

atomic.AddInt64(m.Get(key).(*int64), 1)

When you need to get the counter value, just get by key then use atomic.LoadInt64:

fmt.Printf(
  "Key %v was added %d times\n",
  key,
  atomic.LoadInt64(
    m.Get(key).(*int64),
  ),
)

Or use Range:

m.Range(func(key defaultdict.Comparable, value defaultdict.Pointer) bool {
  fmt.Printf("Key %v was added %d times\n", key, atomic.LoadInt64(value.(*int64)))
  return true
})

License

BSD License.