triemap
triemap copied to clipboard
Implement MutableTrieMap.computeIfAbsent()
We currently rely on default implementation from ConcurrentMap. That implementation defers to get() and then to putIfAbsent() -- which means that we end up traversing the tree twice, whereas given the key we know the insertion point.
We should be doing better than that, provided we can plug into our lookup machinery that is :)
The idea is that the get() operation is inevitably finding an existing entry or at least something close to the insertion point. Once we have established that point and the mapping does not exist, we should consult the mapping function. If it produces a non-null value, we should perform an insert operation based on the context.
Implementation-wise this is mostly about INode and its methods: it feels like an extension of recInsertIf().
Note this has some overlap with #148, hence we need to think about those methods when modifying INode. Both issues seem to have an underlying mechanic in mind, which we need to analyze in terms of meta-meaning and provide a reasonable set of INode methods so we do not end up copy&pasting a ton of code.
public V computeIfAbsent(String key, Function<? super String, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
TrieNode<V> node = root;
// Traverse the trie to find the node for the key
for (char c : key.toCharArray()) {
node = node.getChildren().get(c);
if (node == null) {
break;
}
}
V value = node != null ? node.getValue() : null;
if (value == null) {
// Compute the value using the mapping function
value = mappingFunction.apply(key);
if (value == null) {
return null;
}
// Add the key-value pair to the trie
node = root;
for (char c : key.toCharArray()) {
node = node.getChildren().computeIfAbsent(c, k -> new TrieNode<>());
}
node.setValue(value);
}
return value;
}