├── LICENSE.txt
├── README.md
├── pom.xml
└── src
├── main
└── java
│ ├── io
│ └── prelink
│ │ └── critbit
│ │ ├── AbstractCritBitTree.java
│ │ ├── CritBitTree.java
│ │ ├── MCritBitTree.java
│ │ └── sharedbytearray
│ │ ├── AbstractSBA.java
│ │ ├── EmptySBA.java
│ │ ├── JoinedSBA.java
│ │ ├── PrefixSBA.java
│ │ ├── SBAKeyAnalyzer.java
│ │ ├── SharedByteArray.java
│ │ ├── SuffixSBA.java
│ │ ├── ThickSBA.java
│ │ └── ThinSBA.java
│ └── org
│ └── ardverk
│ └── collection
│ ├── AbstractKeyAnalyzer.java
│ ├── ByteArrayKeyAnalyzer.java
│ ├── ByteKeyAnalyzer.java
│ ├── CharArrayKeyAnalyzer.java
│ ├── CharacterKeyAnalyzer.java
│ ├── Cursor.java
│ ├── DefaultKeyAnalyzer.java
│ ├── IntegerKeyAnalyzer.java
│ ├── Key.java
│ ├── KeyAnalyzer.java
│ ├── LongKeyAnalyzer.java
│ ├── ShortKeyAnalyzer.java
│ └── StringKeyAnalyzer.java
└── test
├── java
├── io
│ └── prelink
│ │ └── critbit
│ │ ├── CritBitTest.java
│ │ ├── IterationSpeedTest.java
│ │ ├── PatriciaTrieTest.java
│ │ └── sharedbytearray
│ │ └── SharedByteArrayTest.java
└── org
│ └── ardverk
│ └── collection
│ ├── ByteArrayKeyAnalyzerTest.java
│ ├── PatriciaTrieTest.java
│ └── SerializationTest.java
└── resources
└── org
└── ardverk
└── collection
└── hamlet.txt
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Jason Fager
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | An OO/Functional Crit-bit tree in Java, inspired by
2 | [djb](http://cr.yp.to/critbit.html), [Adam Langley](https://github.com/agl/critbit), and [Okasaki](http://www.eecs.usma.edu/webs/people/okasaki/pubs.html).
3 |
4 | A primary goal for the project is space-efficiency. It accomplishes this by
5 | defining the five different kinds of nodes in a critbit tree - no
6 | nodes have unused or unnecessary references, and leaf nodes are collapsed into
7 | their parents when possible.
8 |
9 | An immutable/functional implementation is provided for use where such traits
10 | are desirable. A mutable implementation with much higher insertion throughput
11 | and lower garbage collection tolls is also available.
12 |
13 | Special thanks to [rkapsi's](https://github.com/rkapsi)
14 | [patricia-trie](https://github.com/rkapsi/patricia-trie) project for a nice
15 | set of key analyzers and tests. If you're more concerned with Java Map
16 | interface compatibility and/or performance, you should probably use that
17 | project instead.
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
5 | 4.0.0
6 |
7 | io.prelink
8 | critbit
9 | 0.0.5-SNAPSHOT
10 | jar
11 |
12 |
13 | UTF-8
14 |
15 |
16 |
17 |
18 | commons-lang
19 | commons-lang
20 | 2.6
21 | test
22 |
23 |
24 | junit
25 | junit
26 | 4.10
27 | test
28 |
29 |
30 |
31 |
32 |
33 |
34 | org.apache.maven.plugins
35 | maven-compiler-plugin
36 | 2.3.2
37 |
38 | 1.6
39 | 1.6
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.codehaus.mojo
49 | cobertura-maven-plugin
50 | 2.5.1
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/main/java/io/prelink/critbit/AbstractCritBitTree.java:
--------------------------------------------------------------------------------
1 | package io.prelink.critbit;
2 |
3 | import java.io.Serializable;
4 | import java.util.Map;
5 |
6 | import org.ardverk.collection.Cursor;
7 | import org.ardverk.collection.Cursor.Decision;
8 | import org.ardverk.collection.KeyAnalyzer;
9 |
10 |
11 | /**
12 | * An OO/Functional Java crit-bit tree, inspired by
13 | * djb (http://cr.yp.to/critbit.html),
14 | * Adam Langley (https://github.com/agl/critbit),
15 | * and Okasaki (http://www.eecs.usma.edu/webs/people/okasaki/pubs.html)
16 | */
17 | abstract class AbstractCritBitTree implements Serializable {
18 |
19 | static final long serialVersionUID = 20110212L;
20 |
21 | static interface NodeFactory extends Serializable {
22 | Node mkShortBoth(int diffBit, K lk, V lv, K rk, V rv);
23 | Node mkShortRight(int diffBit, Node left, K k, V v);
24 | Node mkShortLeft(int diffBit, K k, V v, Node right);
25 | Node mkTall(int diffBit, Node left, Node right);
26 | Node mkLeaf(K key, V val);
27 | }
28 |
29 | static class Context implements Serializable {
30 | private static final long serialVersionUID = 20110212L;
31 | final KeyAnalyzer chk;
32 | final NodeFactory nf;
33 | Context(KeyAnalyzer chk, NodeFactory nf) {
34 | this.chk = chk;
35 | this.nf = nf;
36 | }
37 | }
38 |
39 | static interface Node extends Serializable {
40 | //Everybody implements these.
41 | Node insert(int diffBit, K key, V val, Context ctx);
42 | Node remove(K key, Context ctx, boolean force);
43 | boolean isInternal();
44 |
45 | //Should only be called for internal nodes.
46 | int bit();
47 | Direction next(K key, Context ctx);
48 | Node nextNode(K key, Context ctx);
49 | Node left(Context ctx);
50 | Node right(Context ctx);
51 | Node setLeft(int diffBit, K key, V val, Context ctx);
52 | Node setRight(int diffBit, K key, V val, Context ctx);
53 | boolean hasExternalLeft();
54 | boolean hasExternalRight();
55 |
56 | //Should only be called for external nodes.
57 | K getKey();
58 | V getValue();
59 | }
60 |
61 | static enum Direction {
62 | LEFT, RIGHT
63 | }
64 |
65 | static abstract class BaseNode implements Node {
66 | private static final long serialVersionUID = 20110212L;
67 | public Node insert(int diffBit, K key, V val, Context ctx) {
68 | throw new UnsupportedOperationException();
69 | }
70 | public int bit() {
71 | throw new UnsupportedOperationException();
72 | }
73 | public Direction next(K key, Context ctx) {
74 | throw new UnsupportedOperationException();
75 | }
76 | public Node nextNode(K key, Context ctx) {
77 | throw new UnsupportedOperationException();
78 | }
79 | public Node left(Context ctx) {
80 | throw new UnsupportedOperationException();
81 | }
82 | public Node right(Context ctx) {
83 | throw new UnsupportedOperationException();
84 | }
85 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
86 | throw new UnsupportedOperationException();
87 | }
88 | public Node setRight(int diffBit, K key, V val, Context ctx) {
89 | throw new UnsupportedOperationException();
90 | }
91 | public boolean hasExternalLeft() {
92 | throw new UnsupportedOperationException();
93 | }
94 | public boolean hasExternalRight() {
95 | throw new UnsupportedOperationException();
96 | }
97 | public K getKey() {
98 | throw new UnsupportedOperationException();
99 | }
100 | public V getValue() {
101 | throw new UnsupportedOperationException();
102 | }
103 | }
104 |
105 | static abstract class AbstractInternal extends BaseNode {
106 | private static final long serialVersionUID = 20110212L;
107 |
108 | private final int bit;
109 | AbstractInternal(int bit) {
110 | this.bit = bit;
111 | }
112 |
113 | public final int bit() { return bit; }
114 |
115 | public final Node insert(int diffBit, K k, V v, Context ctx) {
116 | if(diffBit >= 0 && diffBit < bit()) {
117 | return ctx.chk.isBitSet(k, diffBit) ? ctx.nf.mkShortRight(diffBit, this, k, v)
118 | : ctx.nf.mkShortLeft(diffBit, k, v, this);
119 | } else {
120 | return ctx.chk.isBitSet(k, bit()) ? setRight(diffBit, k, v, ctx)
121 | : setLeft(diffBit, k, v, ctx);
122 | }
123 | }
124 |
125 | public Node remove(K key, Context ctx, boolean force) {
126 | switch(next(key, ctx)) {
127 | case LEFT:
128 | return removeLeft(key, ctx, force);
129 | default:
130 | return removeRight(key, ctx, force);
131 | }
132 | }
133 |
134 | public final Direction next(K key, Context ctx) {
135 | return ctx.chk.isBitSet(key, bit()) ? Direction.RIGHT
136 | : Direction.LEFT;
137 | }
138 |
139 | public final Node nextNode(K key, Context ctx) {
140 | switch(next(key, ctx)) {
141 | case LEFT: return left(ctx);
142 | default: return right(ctx);
143 | }
144 | }
145 |
146 | public final boolean isInternal() { return true; }
147 |
148 | protected abstract Node removeLeft(K key, Context ctx, boolean force);
149 | protected abstract Node removeRight(K key, Context ctx, boolean force);
150 |
151 | protected final Node mkShortBothChild(int diffBit,
152 | K newKey, V newVal,
153 | K oldKey, V oldVal,
154 | Context ctx) {
155 | boolean newGoesRight = ctx.chk.isBitSet(newKey, diffBit);
156 | K rKey = newGoesRight ? newKey : oldKey;
157 | V rVal = newGoesRight ? newVal : oldVal;
158 | K lKey = newGoesRight ? oldKey : newKey;
159 | V lVal = newGoesRight ? oldVal : newVal;
160 | return ctx.nf.mkShortBoth(diffBit, lKey, lVal, rKey, rVal);
161 | }
162 | }
163 |
164 | static final class LeafNode
165 | extends BaseNode
166 | implements Map.Entry
167 | {
168 | private static final long serialVersionUID = 20110212L;
169 | private final K key;
170 | private final V value;
171 | public LeafNode(K key, V value) {
172 | this.key = key;
173 | this.value = value;
174 | }
175 | public K getKey() { return this.key; }
176 | public V getValue() { return this.value; }
177 | public V setValue(V arg0) {
178 | throw new UnsupportedOperationException();
179 | }
180 | public Node insert(int diffBit, K key, V val, Context ctx) {
181 | if(diffBit < 0) {
182 | return ctx.nf.mkLeaf(key, val);
183 | }
184 | return ctx.chk.isBitSet(key, diffBit) ? ctx.nf.mkShortBoth(diffBit, this.key, this.value, key, val)
185 | : ctx.nf.mkShortBoth(diffBit, key, val, this.key, this.value);
186 | }
187 | public boolean isInternal() { return false; }
188 | public Node remove(K key, Context ctx, boolean force) {
189 | return (force || ctx.chk.bitIndex(key, this.key) < 0) ? null
190 | : this;
191 | }
192 | }
193 |
194 | static final class ShortBothNode extends AbstractInternal {
195 | private static final long serialVersionUID = 20110212L;
196 | private final K leftKey;
197 | private final V leftVal;
198 | private final K rightKey;
199 | private final V rightVal;
200 | public ShortBothNode(int bit, K leftKey, V leftVal, K rightKey, V rightVal) {
201 | super(bit);
202 | this.leftKey = leftKey;
203 | this.leftVal = leftVal;
204 | this.rightKey = rightKey;
205 | this.rightVal = rightVal;
206 | }
207 | public Node left(Context ctx) { return ctx.nf.mkLeaf(leftKey, leftVal); }
208 | public Node right(Context ctx) { return ctx.nf.mkLeaf(rightKey, rightVal); }
209 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
210 | if(diffBit < 0) {
211 | return ctx.nf.mkShortBoth(bit(), key, val, rightKey, rightVal);
212 | }
213 | Node newLeft = mkShortBothChild(diffBit, key, val, leftKey, leftVal, ctx);
214 | return ctx.nf.mkShortRight(bit(), newLeft, rightKey, rightVal);
215 | }
216 | public Node setRight(int diffBit, K key, V val, Context ctx) {
217 | if(diffBit < 0) {
218 | return ctx.nf.mkShortBoth(bit(), leftKey, leftVal, key, val);
219 | }
220 | Node newRight = mkShortBothChild(diffBit, key, val, rightKey, rightVal, ctx);
221 | return ctx.nf.mkShortLeft(bit(), leftKey, leftVal, newRight);
222 | }
223 | protected Node removeLeft(K key, Context ctx, boolean force) {
224 | return (force || ctx.chk.bitIndex(key, this.leftKey) < 0) ? ctx.nf.mkLeaf(rightKey, rightVal)
225 | : this;
226 | }
227 | protected Node removeRight(K key, Context ctx, boolean force) {
228 | return (force || ctx.chk.bitIndex(key, this.rightKey) < 0) ? ctx.nf.mkLeaf(leftKey, leftVal)
229 | : this;
230 | }
231 | public boolean hasExternalLeft() { return true; }
232 | public boolean hasExternalRight() { return true; }
233 | }
234 |
235 | private final Context ctx;
236 |
237 | AbstractCritBitTree(Context context) {
238 | this.ctx = context;
239 | }
240 |
241 | abstract Node root();
242 |
243 | Context ctx() {
244 | return ctx;
245 | }
246 |
247 | static final class SearchResult {
248 | final Node parent;
249 | final Direction pDirection;
250 | final Node result;
251 | final Direction rDirection;
252 | public SearchResult(Node parent,
253 | Direction pDirection,
254 | Node result,
255 | Direction rDirection) {
256 | this.parent = parent;
257 | this.pDirection = pDirection;
258 | this.result = result;
259 | this.rDirection = rDirection;
260 | }
261 | K key(Context ctx) {
262 | switch(rDirection) {
263 | case LEFT:
264 | return result.left(ctx).getKey();
265 | default: //case RIGHT:
266 | return result.right(ctx).getKey();
267 | }
268 | }
269 | V value(Context ctx) {
270 | switch(rDirection) {
271 | case LEFT:
272 | return result.left(ctx).getValue();
273 | default: //case RIGHT:
274 | return result.right(ctx).getValue();
275 | }
276 | }
277 | }
278 |
279 | final SearchResult search(final Node start, final K key) {
280 | Node par = null;
281 | Direction parDirection = null;
282 | Node cur = start;
283 | for(;;) {
284 | switch(cur.next(key, ctx)) {
285 | case LEFT:
286 | if(cur.hasExternalLeft()) {
287 | return new SearchResult(par, parDirection, cur, Direction.LEFT);
288 | }
289 | par = cur;
290 | parDirection = Direction.LEFT;
291 | cur = cur.left(ctx);
292 | break;
293 | case RIGHT:
294 | if(cur.hasExternalRight()) {
295 | return new SearchResult(par, parDirection, cur, Direction.RIGHT);
296 | }
297 | par = cur;
298 | parDirection = Direction.RIGHT;
299 | cur = cur.right(ctx);
300 | break;
301 | }
302 | }
303 | }
304 |
305 | public final V get(Object k) {
306 | K key = AbstractCritBitTree.cast(k);
307 | if(root() == null) {
308 | return null;
309 | }
310 | if(!root().isInternal()) {
311 | return ctx().chk.bitIndex(key, root().getKey()) < 0 ? root().getValue() : null;
312 | }
313 | SearchResult sr = search(root(), key);
314 | final int diffBit = ctx().chk.bitIndex(key, sr.key(ctx()));
315 | return (diffBit < 0) ? sr.value(ctx()) : null;
316 | }
317 |
318 | public final Map.Entry min() {
319 | if(root() == null) {
320 | return null;
321 | }
322 | Node current = root();
323 | while(current.isInternal()) {
324 | current = current.left(ctx());
325 | }
326 | return AbstractCritBitTree.>cast(current);
327 | }
328 |
329 | public final Map.Entry max() {
330 | if(root() == null) {
331 | return null;
332 | }
333 | Node current = root();
334 | while(current.isInternal()) {
335 | current = current.right(ctx());
336 | }
337 | return AbstractCritBitTree.>cast(current);
338 | }
339 |
340 | public final void traverse(Cursor super K, ? super V> cursor) {
341 | if(root() == null) {
342 | return;
343 | }
344 | doTraverse(root(), cursor);
345 | }
346 |
347 | public final void traverseWithPrefix(K key,
348 | Cursor super K, ? super V> cursor) {
349 | if(root() == null) {
350 | return;
351 | }
352 | if(!root().isInternal()) {
353 | Map.Entry e = AbstractCritBitTree.>cast(root());
354 | cursor.select(e);
355 | return;
356 | }
357 |
358 | int keyLen = ctx.chk.lengthInBits(key);
359 | Node current = root();
360 | Node top = current;
361 | Direction topDirection = Direction.LEFT;
362 | while(current.isInternal()) {
363 | Direction nextDirection = current.next(key, ctx);
364 | if(current.bit() < keyLen) {
365 | top = current;
366 | topDirection = nextDirection;
367 | }
368 | switch(nextDirection) {
369 | case LEFT:
370 | current = current.left(ctx);
371 | break;
372 | case RIGHT:
373 | current = current.right(ctx);
374 | break;
375 | }
376 | }
377 | if(!ctx.chk.isPrefix(current.getKey(), key)) {
378 | return;
379 | }
380 |
381 | switch(topDirection) {
382 | case LEFT:
383 | doTraverse(top.left(ctx), cursor);
384 | return;
385 | default:
386 | doTraverse(top.right(ctx), cursor);
387 | return;
388 | }
389 | }
390 |
391 | protected abstract Decision doTraverse(Node top,
392 | Cursor super K, ? super V> cursor);
393 |
394 | public abstract int size();
395 | public final boolean isEmpty() { return size() == 0; }
396 |
397 | @SuppressWarnings("unchecked")
398 | static T cast(Object obj) {
399 | return (T)obj;
400 | }
401 |
402 | public final boolean containsKey(Object k) {
403 | if(root() == null) {
404 | return false;
405 | }
406 |
407 | K key = AbstractCritBitTree.cast(k);
408 | if(!root().isInternal()) {
409 | return ctx().chk.bitIndex(key, root().getKey()) < 0;
410 | }
411 |
412 | final SearchResult sr = search(root(), key);
413 | final int diffBit = ctx().chk.bitIndex(key, sr.key(ctx()));
414 | return diffBit < 0;
415 | }
416 |
417 | private static class ContainsValueCursor implements Cursor {
418 | private final V value;
419 | private boolean outcome = false;
420 | public ContainsValueCursor(V value) {
421 | this.value = value;
422 | }
423 | public Decision select(Map.Entry extends K, ? extends V> entry) {
424 | if(value.equals(entry.getValue())) {
425 | outcome = true;
426 | return Decision.EXIT;
427 | }
428 | return Decision.CONTINUE;
429 | }
430 | public boolean getOutcome() {
431 | return outcome;
432 | }
433 | }
434 |
435 | public final boolean containsValue(Object v) {
436 | V val = AbstractCritBitTree.cast(v);
437 | ContainsValueCursor cvc = new ContainsValueCursor(val);
438 | traverse(cvc);
439 | return cvc.getOutcome();
440 | }
441 | }
442 |
--------------------------------------------------------------------------------
/src/main/java/io/prelink/critbit/CritBitTree.java:
--------------------------------------------------------------------------------
1 | package io.prelink.critbit;
2 |
3 | import java.util.Map;
4 |
5 | import org.ardverk.collection.Cursor;
6 | import org.ardverk.collection.Cursor.Decision;
7 | import org.ardverk.collection.KeyAnalyzer;
8 |
9 | /**
10 | * An OO/Functional Java crit-bit tree, inspired by
11 | * djb (http://cr.yp.to/critbit.html),
12 | * Adam Langley (https://github.com/agl/critbit),
13 | * and Okasaki (http://www.eecs.usma.edu/webs/people/okasaki/pubs.html)
14 | */
15 | public final class CritBitTree extends AbstractCritBitTree {
16 |
17 | private static final long serialVersionUID = 20110212L;
18 |
19 | static final class ShortLeftNode extends AbstractInternal {
20 | private static final long serialVersionUID = 20110212L;
21 | private final K leftKey;
22 | private final V leftVal;
23 | private final Node right;
24 | public ShortLeftNode(int bit, K leftKey, V leftVal, Node right) {
25 | super(bit);
26 | this.leftKey = leftKey;
27 | this.leftVal = leftVal;
28 | this.right = right;
29 | }
30 | public Node left(Context ctx) { return ctx.nf.mkLeaf(leftKey, leftVal); }
31 | public Node right(Context ctx) { return right; }
32 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
33 | if(diffBit < 0) {
34 | return ctx.nf.mkShortLeft(bit(), key, val, right);
35 | }
36 | Node newLeft = mkShortBothChild(diffBit, key, val, leftKey, leftVal, ctx);
37 | return ctx.nf.mkTall(bit(), newLeft, right);
38 | }
39 | public Node setRight(int diffBit, K key, V val, Context ctx) {
40 | Node newRight = right.insert(diffBit, key, val, ctx);
41 | return ctx.nf.mkShortLeft(bit(), leftKey, leftVal, newRight);
42 | }
43 | protected Node removeLeft(K key, Context ctx, boolean force) {
44 | if(force || ctx.chk.bitIndex(key, this.leftKey) < 0) {
45 | return right;
46 | }
47 | return this;
48 | }
49 | protected Node removeRight(K key, Context ctx, boolean force) {
50 | Node newRight = right.remove(key, ctx, force);
51 | if(right == newRight) {
52 | return this;
53 | }
54 | return newRight.isInternal() ? ctx.nf.mkShortLeft(bit(), leftKey, leftVal, newRight)
55 | : ctx.nf.mkShortBoth(bit(), leftKey, leftVal, newRight.getKey(), newRight.getValue());
56 | }
57 | public boolean hasExternalLeft() { return true; }
58 | public boolean hasExternalRight() { return false; }
59 | }
60 | static final class ShortRightNode extends AbstractInternal {
61 | private static final long serialVersionUID = 20110212L;
62 | private final Node left;
63 | private final K rightKey;
64 | private final V rightVal;
65 | public ShortRightNode(int bit, Node left, K rightKey, V rightVal) {
66 | super(bit);
67 | this.left = left;
68 | this.rightKey = rightKey;
69 | this.rightVal = rightVal;
70 | }
71 | public Node left(Context ctx) { return left; }
72 | public Node right(Context ctx) { return ctx.nf.mkLeaf(rightKey, rightVal); }
73 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
74 | Node newLeft = left.insert(diffBit, key, val, ctx);
75 | return ctx.nf.mkShortRight(bit(), newLeft, rightKey, rightVal);
76 | }
77 | public Node setRight(int diffBit, K key, V val, Context ctx) {
78 | if(diffBit < 0) {
79 | return ctx.nf.mkShortRight(bit(), left, key, val);
80 | }
81 | Node newRight = mkShortBothChild(diffBit, key, val, rightKey, rightVal, ctx);
82 | return ctx.nf.mkTall(bit(), left, newRight);
83 | }
84 | protected Node removeLeft(K key, Context ctx, boolean force) {
85 | Node newLeft = left.remove(key, ctx, force);
86 | if(left == newLeft) {
87 | return this;
88 | }
89 | return newLeft.isInternal() ? ctx.nf.mkShortRight(bit(), newLeft, rightKey, rightVal)
90 | : ctx.nf.mkShortBoth(bit(), newLeft.getKey(), newLeft.getValue(), rightKey, rightVal);
91 | }
92 | protected Node removeRight(K key, Context ctx, boolean force) {
93 | return (force || ctx.chk.bitIndex(key, this.rightKey) < 0) ? left
94 | : this;
95 | }
96 | public boolean hasExternalLeft() { return false; }
97 | public boolean hasExternalRight() { return true; }
98 | }
99 | static final class TallNode extends AbstractInternal {
100 | private static final long serialVersionUID = 20110212L;
101 | private final Node left;
102 | private final Node right;
103 | public TallNode(int bit, Node left, Node right) {
104 | super(bit);
105 | this.left = left;
106 | this.right = right;
107 | }
108 | public Node left(Context ctx) { return left; }
109 | public Node right(Context ctx) { return right; }
110 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
111 | Node newLeft = left.insert(diffBit, key, val, ctx);
112 | return ctx.nf.mkTall(bit(), newLeft, right);
113 | }
114 | public Node setRight(int diffBit, K key, V val, Context ctx) {
115 | Node newRight = right.insert(diffBit, key, val, ctx);
116 | return ctx.nf.mkTall(bit(), left, newRight);
117 | }
118 | protected Node removeLeft(K key, Context ctx, boolean force) {
119 | Node newLeft = left.remove(key, ctx, force);
120 | if(left == newLeft) {
121 | return this;
122 | }
123 | return newLeft.isInternal() ? ctx.nf.mkTall(bit(), newLeft, right)
124 | : ctx.nf.mkShortLeft(bit(), newLeft.getKey(), newLeft.getValue(), right);
125 | }
126 | protected Node removeRight(K key, Context ctx, boolean force) {
127 | Node newRight = right.remove(key, ctx, force);
128 | if(right == newRight) {
129 | return this;
130 | }
131 | return newRight.isInternal() ? ctx.nf.mkTall(bit(), left, newRight)
132 | : ctx.nf.mkShortRight(bit(), left, newRight.getKey(), newRight.getValue());
133 | }
134 | public boolean hasExternalLeft() { return false; }
135 | public boolean hasExternalRight() { return false; }
136 | }
137 |
138 | static final class ImmutableNodeFactory implements NodeFactory {
139 | private static final long serialVersionUID = 20110212L;
140 | public Node mkShortBoth(int diffBit, K lk, V lv, K rk, V rv) {
141 | return new ShortBothNode(diffBit, lk, lv, rk, rv);
142 | }
143 | public Node mkShortRight(int diffBit, Node left, K k, V v) {
144 | return new ShortRightNode(diffBit, left, k, v);
145 | }
146 | public Node mkShortLeft(int diffBit, K k, V v, Node right) {
147 | return new ShortLeftNode(diffBit, k, v, right);
148 | }
149 | public Node mkTall(int diffBit, Node left, Node right) {
150 | return new TallNode(diffBit, left, right);
151 | }
152 | public Node mkLeaf(K key, V val) {
153 | return new LeafNode(key, val);
154 | }
155 | }
156 |
157 | private final Node root;
158 | private final int size;
159 |
160 | public CritBitTree(KeyAnalyzer analyzer) {
161 | this(null, 0,
162 | new Context(analyzer, new ImmutableNodeFactory()));
163 | }
164 |
165 | private CritBitTree(Node root, int size, Context context) {
166 | super(context);
167 | this.root = root;
168 | this.size = size;
169 | }
170 |
171 | Node root() { return root; }
172 | public int size() { return size; }
173 |
174 | public CritBitTree put(K key, V val) {
175 | if(root() == null) {
176 | return new CritBitTree(ctx().nf.mkLeaf(key, val), 1, ctx());
177 | }
178 | K compKey;
179 | if(root().isInternal()) {
180 | SearchResult sr = search(root(), key);
181 | compKey = sr.key(ctx());
182 | } else {
183 | compKey = root().getKey();
184 | }
185 |
186 | int diffBit = ctx().chk.bitIndex(key, compKey);
187 | return new CritBitTree(root().insert(diffBit, key, val, ctx()),
188 | (diffBit < 0) ? size : size + 1,
189 | ctx());
190 | }
191 |
192 | public CritBitTree remove(K key) {
193 | if(root == null) {
194 | return this;
195 | }
196 | Node removed = root.remove(key, ctx(), false);
197 | return (removed == root) ? this
198 | : new CritBitTree(removed, size - 1, ctx());
199 | }
200 |
201 | protected final Decision doTraverse(Node top,
202 | Cursor super K, ? super V> cursor) {
203 | if(top.isInternal()) {
204 | Decision d = doTraverse(top.left(ctx()), cursor);
205 | switch(d) {
206 | case REMOVE_AND_EXIT: //fall through
207 | case EXIT:
208 | return d;
209 | case REMOVE: //fall through
210 | case CONTINUE:
211 | default:
212 | return doTraverse(top.right(ctx()), cursor);
213 | }
214 | } else {
215 | Map.Entry e = AbstractCritBitTree.>cast(top);
216 | return cursor.select(e);
217 | }
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/main/java/io/prelink/critbit/MCritBitTree.java:
--------------------------------------------------------------------------------
1 | package io.prelink.critbit;
2 |
3 | import java.util.AbstractSet;
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.Iterator;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.NoSuchElementException;
10 | import java.util.Set;
11 |
12 | import org.ardverk.collection.Cursor;
13 | import org.ardverk.collection.Cursor.Decision;
14 | import org.ardverk.collection.KeyAnalyzer;
15 |
16 | /**
17 | * Like immutable.CritBitTree, except w/ nodes that are mutable where it makes
18 | * sense, hypothesis being we can improve performance and cut down on garbage
19 | * collection a bit.
20 | */
21 | public final class MCritBitTree extends AbstractCritBitTree implements Map {
22 |
23 | private static final long serialVersionUID = 20110212L;
24 |
25 | static final class MShortLeftNode extends AbstractInternal {
26 | private static final long serialVersionUID = 20110212L;
27 | private final K leftKey;
28 | private V leftVal;
29 | private Node right;
30 | public MShortLeftNode(int bit, K leftKey, V leftVal, Node right) {
31 | super(bit);
32 | this.leftKey = leftKey;
33 | this.leftVal = leftVal;
34 | this.right = right;
35 | }
36 | public Node left(Context ctx) { return ctx.nf.mkLeaf(leftKey, leftVal); }
37 | public Node right(Context ctx) { return right; }
38 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
39 | if(diffBit < 0) {
40 | this.leftVal = val;
41 | return this;
42 | }
43 | Node newLeft = mkShortBothChild(diffBit, key, val, leftKey, leftVal, ctx);
44 | return ctx.nf.mkTall(bit(), newLeft, right);
45 | }
46 | public Node setRight(int diffBit, K key, V val, Context ctx) {
47 | this.right = right.insert(diffBit, key, val, ctx);
48 | return this;
49 | }
50 | public boolean hasExternalLeft() { return true; }
51 | public boolean hasExternalRight() { return false; }
52 | protected Node removeLeft(K key, Context ctx, boolean force) {
53 | if(force || ctx.chk.bitIndex(key, this.leftKey) < 0) {
54 | return right;
55 | }
56 | return this;
57 | }
58 | protected Node removeRight(K key, Context ctx, boolean force) {
59 | Node newRight = right.remove(key, ctx, force);
60 | if(newRight.isInternal()) {
61 | this.right = newRight;
62 | return this;
63 | } else {
64 | return ctx.nf.mkShortBoth(bit(), leftKey, leftVal, newRight.getKey(), newRight.getValue());
65 | }
66 | }
67 | }
68 |
69 | static final class MShortRightNode extends AbstractInternal {
70 | private static final long serialVersionUID = 20110212L;
71 | private Node left;
72 | private final K rightKey;
73 | private V rightVal;
74 | public MShortRightNode(int bit, Node left, K rightKey, V rightVal) {
75 | super(bit);
76 | this.left = left;
77 | this.rightKey = rightKey;
78 | this.rightVal = rightVal;
79 | }
80 | public Node left(Context ctx) { return left; }
81 | public Node right(Context ctx) { return ctx.nf.mkLeaf(rightKey, rightVal); }
82 | public Node setLeft(int diffBit, K key, V val, Context ctx) {
83 | this.left = left.insert(diffBit, key, val, ctx);
84 | return this;
85 | }
86 | public Node setRight(int diffBit, K key, V val, Context ctx) {
87 | if(diffBit < 0) {
88 | this.rightVal = val;
89 | return this;
90 | }
91 | Node newRight = mkShortBothChild(diffBit, key, val, rightKey, rightVal, ctx);
92 | return ctx.nf.mkTall(bit(), left, newRight);
93 | }
94 | protected Node removeLeft(K key, Context ctx, boolean force) {
95 | Node