# \[ANN\] StaticDictTrees.jl: Fast dictionary trees with zero-allocation views

**URL:** https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187
**Category:** Package Announcements
**Created:** [May 19, 2026, 9:29am UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187 "2026-05-19T09:29:17Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 19, 2026, 9:29am UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/1 "2026-05-19T09:29:18Z")

</div>

Hi everyone! 👋

I’d like to announce the [StaticDictTrees.jl](https://github.com/gcalderone/StaticDictTrees.jl) package. It provides a high-performance dictionary with `Tuple`s as keys, and with the added functionality of providing views into any data subset identified by an incomplete key.

If you’ve ever used a `Dict{Tuple, T}` and wished you could instantly isolate all entries sharing a specific prefix without an expensive O(N) filter, this package is for you.

An example is worth a million words:

```julia
using StaticDictTrees

# Create a static dict tree
metrics = SDTree((:prod, :web, :cpu) => 42.5,
                 (:prod, :web, :ram) => 78.1,
                 (:prod, :db, :cpu) => 12.0,
                 (:stage, :web, :cpu) => 8.4)

# Standard full-key lookup
metrics[:prod, :web, :cpu] # 42.5

# Take a zero-allocation, type-stable view of a sub-branch
prod_web = view(metrics, (:prod, :web))

# The view behaves exactly like a relative AbstractDict ...
prod_web[:ram] # 78.1

# ... and any modification in a view is reflected in the original tree
prod_web[:ram] = 99.
metrics[:prod, :web, :ram] # 99.

```

Comments are welcome!

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [May 19, 2026, 11:35am UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/2 "2026-05-19T11:35:41Z")

</div>

This post was temporarily hidden by the community for possibly being off-topic, unfocused, inappropriate, or spammy.

---

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 19, 2026, 3:26pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/3 "2026-05-19T15:26:40Z")

</div>

I understand you don’t like it, no probem!! 😉

> [@foobar\_lv2](#):
>
> Refuse to support deletion.

Deletion is feasible, it works, its availability do not harm the performance of other operations (lookup, insert and updates), and most importantly it is **clearly** mentioned that it is by far the slowest operation.  
In short: it can be useful to delete just one entry rather than recreating a huge structure from scratch.  
So why refusing?

> [@foobar\_lv2](#):
>
> Don’t delete the items from the arrays which necessitates the terrible “shift index by one”; instead `Base._unsetindex!` the slot.

I was not aware of `Base._unsetindex!`, thanks for te suggestion!  
But I want to use the `Array` exposed interface, rather than relying on internal machinery.

> [@foobar\_lv2](#):
>
> On deletion, check whether there are too many currently empty array slots, and if so, reassign all of them at once.

This would be in contrast with the need to always have all the values stored in a contiguous vector.

> [@foobar\_lv2](#):
>
> Generally, I find publication / open-sourcing of this kind of AI-sloppy package of questionable value.  
> …  
> But then: Why publish and advertise it here?

Well, there possibly is a misunderstanding here: this is not a 5-minute chat with Gemini being copy/pasted and published, it’s something it took me a lot of time to conceive and implement despite the help from AI, and I share it in the hope it is useful to someone else.

Also, I don’t think the code is sloppy at all (but I am surely biased…).

But I understand your concerns, and I am curious to know whether there is a clear guideline on registering / advertising packages whose code has been developed with AI help (besides pointing it out).

---

<div class="post-metadata">

### Author: ![JarJar](https://avatars.discourse-cdn.com/v4/letter/j/e19b73/32.png) [@JarJar](https://discourse.julialang.org/u/JarJar)
#### Post date: [May 19, 2026, 5:43pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/4 "2026-05-19T17:43:06Z")

</div>

So this alleviates the need to always have all the values stored in a contiguous vector?

---

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 19, 2026, 9:23pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/5 "2026-05-19T21:23:11Z")

</div>

Not sure I follow…  
The **purpose** is to have all values stored in a contiguous vector, and this is what the package does, i.e.:

```julia
using StaticDictTrees

# Create a static dict tree
metrics = SDTree((:prod, :web, :cpu) => 42.5,
                 (:prod, :web, :ram) => 78.1,
                 (:prod, :db, :cpu) => 12.0,
                 (:stage, :web, :cpu) => 8.4)
values(metrics)

```

will print

```julia-auto
4-element Vector{Float64}:
 42.5
 78.1
 12.0
  8.4

```

regardless them being in separate branch.

Let me know if this answer your question

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [May 19, 2026, 10:32pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/6 "2026-05-19T22:32:56Z")

</div>

> But I want to use the Array exposed interface, rather than relying on internal machinery.

You shouldn’t be afraid of using the internal machinery here. Everybody uses something like Base.unset\_index! for this job. Without that, it would not be possible to write things like `Dict`. If you must avoid unexported APIs (e.g. due to policy), then you can [ccall](https://github.com/JuliaLang/julia/blob/062a90bc8c2e393cddc52de58d1b373645ea88ee/src/array.c#L322) – that was exported in 1.0 and therefore will remain valid.

> [@gcalderone](#):
>
> Well, there possibly is a misunderstanding here: this is not a 5-minute chat with Gemini being copy/pasted and published, it’s something it took me a lot of time to conceive and implement despite the help from AI, and I share it in the hope it is useful to someone else.

I apologize, I misunderstood the nature of your package. In that case, keep on sharing and having fun! And sorry for me being a spoilsport ☹  
(…but I think the sharing should be for educational purposes, not for “use this as dependency”)

> [@gcalderone](#):
>
> Also, I don’t think the code is sloppy at all (but I am surely biased…).

It’s not the code, it’s the underlying algorithm / datastructure that looks sloppy.

> [@gcalderone](#):
>
> This would be in contrast with the need to always have all the values stored in a contiguous vector.

You can also do deletion by moving: If the key/value at index `idx` needs to go, then do

```julia-auto
oldkey = sdtree.keys[idx]
oldlen = length(sdtree.keys)
movedkey = sdtree.keys[oldlen]
sdtree.keys[idx] = movedkey
sdtree.values[idx] = sdtree.values[oldlen]
pop!(sdtree.keys)
pop!(sdtree.values)

sdtree.lookup[movedkey] = idx
delete!(sdtree.lookup, oldkey)

#do the same for branch_lookup

```

---

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 20, 2026, 10:17am UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/7 "2026-05-20T10:17:38Z")

</div>

> [@foobar\_lv2](#):
>
> You shouldn’t be afraid of using the internal machinery here.

If possible I would avoid it, exactly because I prefer to be on the safe side and write something I entirely understand.

> [@foobar\_lv2](#):
>
> ou can also do deletion by moving: If the key/value at index `idx` needs to go, then do

But this would destroy the order on the internal `values` vector and force me to modify `values(::SDTree)` to return an iterator rather than a vector.

This was one of my requirements from the beginning and I don’t know if it is worth to trade this to have better performance on `delete!` and `prune!`…

Anyway, thanks for raising the point, I would definitely think about it.

---

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 21, 2026, 10:14am UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/8 "2026-05-21T10:14:14Z")

</div>

Quick update:

- I modified the delete and prune algorithms to follow @foobar_lv2 suggestion (“deletion by moving”). This destroys the order on the internal values vector but the perfomance improvements are huge!;
- The `SDTree` and `SDBranch` inherit from `AbstractDict` hence it is fine for `values()` to return an iterator;
- I also implemented the `values_view()` method to return a view on the internal vector with correct order (namely the order elements were inserted in the tree).

New version is 0.1.1.

---

<div class="post-metadata">

### Author: ![JarJar](https://avatars.discourse-cdn.com/v4/letter/j/e19b73/32.png) [@JarJar](https://discourse.julialang.org/u/JarJar)
#### Post date: [May 21, 2026, 1:28pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/9 "2026-05-21T13:28:17Z")

</div>

destroys the order on the internal values vector but the perfomance improvements are huge. Does this preserve contiguity?

---

<div class="post-metadata">

### Author: ![gcalderone](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gcalderone/32/1539_2.png) [@gcalderone](https://discourse.julialang.org/u/gcalderone)
#### Post date: [May 21, 2026, 1:37pm UTC](https://discourse.julialang.org/t/ann-staticdicttrees-jl-fast-dictionary-trees-with-zero-allocation-views/137187/10 "2026-05-21T13:37:27Z")

</div>

Yes, the internal `values` field is still dense, i.e. contiguous, but adjacent cells are not guaranteed to be in the same order as insertion.

The only ways to get them in insertion order is by:

- iterating over `values()`;
- accessing via `values_view()`.
