Go implementation of Python's defaultdict, kind of
Find a file
2026-02-20 20:00:59 -08:00
doc.go Initial implementation 2020-12-31 19:43:34 -08:00
doc_test.go Modernize code with go fix 2026-02-20 20:00:59 -08:00
generator.go Generic support 2022-03-17 19:28:14 -07:00
generator_test.go Modernize code with go fix 2026-02-20 20:00:59 -08:00
go.mod Update for go 1.23 2024-08-15 08:35:30 -07:00
interface.go Update for go 1.23 2024-08-15 08:35:30 -07:00
interface_test.go Fix Pointer doc example 2022-03-17 21:24:38 -07:00
LICENSE Initial commit 2020-12-31 17:45:18 -08:00
README.md Add LICENSE section to README 2021-01-03 12:50:27 -08:00
sync.go Update for go 1.23 2024-08-15 08:35:30 -07:00
sync_test.go Update for go 1.23 2024-08-15 08:35:30 -07:00

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.