Optimization using tries

To make it easy to visualize, let us try a smaller example.

Imagine storing a key-value mapping like this:

We could store them all in a single JavaScript object:

const data = {

to: 7,

tea: 3,

ted: 4,

ten: 12,

A: 15,

i: 11,

in: 5,

inn: 9

}

But how about we create a trie instead? It’s a tree structure that… uh just look it up on Wikipedia. It looks like this:

Image source: Wikipedia

Basically, you can get to the value you need by following the paths in this graph from the root.

If you want data.in , start at the root, follow the path labelled i and n respectively. You should arrive at a node that contains 5 .

Now, how about modifications?

Let’s say we want to change the value at the key tea from 3 to 14 .

We can construct a new trie, reusing existing nodes as much as possible.

Green colors means newly-created nodes.

The old tree is still there, unchanged, in case you keep a reference to it.

The nodes in gray will be garbage-collected if we lose a reference to the old tree.

As you can see, we only need to reconstruct 4 new nodes to update this tree. Other nodes could be reused as-is.

This is called structural sharing.

This is how Immutable.js implements its Immutable.Map . It creates a tree where each node can have up to 32 branches.

Exploring an Immutable.Map containing 100,000 entries. This diagram is drawn from an actual Immutable.Map.

When we update a single item, only a few nodes need to be recreated.

Immutable.js employs crazy advanced techniques to keep this tree structure compact and performant by creating multiple types of nodes to account for various characteristics of subtrees.