9
9
* array that combines the characteristics of a hash table and an array mapped
10
10
* trie. It is a refined version of the more general notion of a hash tree.
11
11
*
12
+ * This implementation is 32-bit and steps in 5-bit intervals, maximum tree
13
+ * height of 7.
14
+ *
12
15
* http://en.wikipedia.org/wiki/Hash_array_mapped_trie
13
16
*
14
17
* @author Justin Wetherell <phishman3579@gmail.com>
15
18
*/
16
19
@ SuppressWarnings ("unchecked" )
17
- public class HashArrayMappedTrie <K extends Number , V > implements IMap <K ,V > {
20
+ public class HashArrayMappedTrie <K , V > implements IMap <K ,V > {
18
21
19
22
private static final byte MAX_BITS = 32 ;
20
23
private static final byte BITS = 5 ;
21
- private static final byte MAX_DEPTH = MAX_BITS /BITS ; // default 6
24
+ private static final byte MAX_DEPTH = MAX_BITS /BITS ; // 6
22
25
23
26
private Node root = null ;
24
27
private int size = 0 ;
25
28
29
+ /**
30
+ * Convert a big-endian binary string to a little-endian binary string
31
+ * and also pads with zeros to make it "BITS" length.
32
+ *
33
+ * e.g. BITS=5 value==6 big-endian=110 little-endian=011 (pad) return=01100
34
+ */
26
35
private static final String toBinaryString (int value ) {
27
- if (value <0 ) return "null" ;
28
-
29
36
StringBuilder builder = new StringBuilder (Integer .toBinaryString (value ));
30
37
builder = builder .reverse ();
31
38
while (builder .length ()<BITS ) {
@@ -34,11 +41,16 @@ private static final String toBinaryString(int value) {
34
41
return builder .toString ();
35
42
}
36
43
37
- private static final int getPosition (int level , int value ) {
38
- return (value >>> level *BITS ) & 0x01f ;
44
+ /**
45
+ * Get the "BITS" length integer starting at height*BITS position.
46
+ *
47
+ * e.g. BITS=5 height=1 value==266 big-endian=100001010 (shifts height*BITS off the right) return=1000 (8 in decimal)
48
+ */
49
+ private static final int getPosition (int height , int value ) {
50
+ return (value >>> height *BITS ) & 0x01f ;
39
51
}
40
52
41
- private V put (ArrayNode parent , Node node , byte level , int key , V value ) {
53
+ private V put (ArrayNode parent , Node node , byte height , int key , V value ) {
42
54
if (node instanceof KeyValueNode ) {
43
55
KeyValueNode <V > kvNode = (KeyValueNode <V >) node ;
44
56
if (key ==kvNode .key ) {
@@ -48,10 +60,10 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
48
60
}
49
61
// Key already exists but doesn't match current key
50
62
KeyValueNode <V > oldParent = kvNode ;
51
- int newParentPosition = getPosition (level -1 , key );
52
- int oldParentPosition = getPosition (level , oldParent .key );
53
- int childPosition = getPosition (level , key );
54
- ArrayNode newParent = new ArrayNode (parent , key , level );
63
+ int newParentPosition = getPosition (height -1 , key );
64
+ int oldParentPosition = getPosition (height , oldParent .key );
65
+ int childPosition = getPosition (height , key );
66
+ ArrayNode newParent = new ArrayNode (parent , key , height );
55
67
newParent .parent = parent ;
56
68
if (parent ==null ) {
57
69
// Only the root doesn't have a parent, so new root
@@ -69,18 +81,18 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
69
81
}
70
82
while (oldParentPosition == childPosition ) {
71
83
// Handle the case when the new children map to same position.
72
- level ++;
73
- if (level >MAX_DEPTH ) {
84
+ height ++;
85
+ if (height >MAX_DEPTH ) {
74
86
// We have found two keys which match exactly.
75
87
System .err .println ("Yikes! Found two keys which match exactly." );
76
88
return null ;
77
89
}
78
- newParentPosition = getPosition (level -1 , key );
79
- ArrayNode newParent2 = new ArrayNode (newParent , key , level );
90
+ newParentPosition = getPosition (height -1 , key );
91
+ ArrayNode newParent2 = new ArrayNode (newParent , key , height );
80
92
newParent .addChild (newParentPosition , newParent2 );
81
93
82
- oldParentPosition = getPosition (level , oldParent .key );
83
- childPosition = getPosition (level , key );
94
+ oldParentPosition = getPosition (height , oldParent .key );
95
+ childPosition = getPosition (height , key );
84
96
if (oldParentPosition != childPosition ) {
85
97
newParent2 .addChild (oldParentPosition , oldParent );
86
98
oldParent .parent = newParent2 ;
@@ -92,14 +104,14 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
92
104
return value ;
93
105
} else if (node instanceof ArrayNode ) {
94
106
ArrayNode arrayRoot = (ArrayNode ) node ;
95
- int position = getPosition (arrayRoot .level , key );
107
+ int position = getPosition (arrayRoot .height , key );
96
108
Node child = arrayRoot .getChild (position );
97
109
if (child ==null ) {
98
110
// Found an empty slot in parent
99
111
arrayRoot .addChild (position , new KeyValueNode <V >(arrayRoot , key , value ));
100
112
return value ;
101
113
}
102
- return put (arrayRoot , child , (byte )(level +1 ), key , value );
114
+ return put (arrayRoot , child , (byte )(height +1 ), key , value );
103
115
}
104
116
return null ;
105
117
}
@@ -109,39 +121,35 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
109
121
*/
110
122
@ Override
111
123
public V put (K key , V value ) {
112
- //System.out.println("put="+key);
113
- //System.out.println(this.toString());
114
- int intKey = key .intValue ();
124
+ int intKey = key .hashCode ();
115
125
V toReturn = null ;
116
126
if (root ==null ) {
117
127
root = new KeyValueNode <V >(null , intKey , value );
118
128
toReturn = value ;
119
129
} else {
120
130
toReturn = put (null , root , (byte )0 , intKey , value );
121
131
}
122
- //System.out.println(this.toString());
123
132
if (toReturn !=null ) size ++;
124
133
return toReturn ;
125
134
}
126
135
127
- private Node find (Node node , K key ) {
136
+ private Node find (Node node , int key ) {
128
137
if (node instanceof KeyValueNode ) {
129
138
KeyValueNode <V > kvNode = (KeyValueNode <V >) node ;
130
- if (key . equals ( kvNode .key ) )
139
+ if (kvNode .key == key )
131
140
return kvNode ;
132
141
return null ;
133
142
} else if (node instanceof ArrayNode ) {
134
143
ArrayNode arrayNode = (ArrayNode )node ;
135
- int intKey = key .intValue ();
136
- int position = getPosition (arrayNode .level , intKey );
144
+ int position = getPosition (arrayNode .height , key );
137
145
Node possibleNode = arrayNode .getChild (position );
138
146
if (possibleNode ==null ) return null ;
139
147
return find (possibleNode ,key );
140
148
}
141
149
return null ;
142
150
}
143
151
144
- private Node find (K key ) {
152
+ private Node find (int key ) {
145
153
if (root ==null ) return null ;
146
154
return find (root , key );
147
155
}
@@ -151,23 +159,30 @@ private Node find(K key) {
151
159
*/
152
160
@ Override
153
161
public V get (K key ) {
154
- Node node = find (key );
162
+ Node node = find (key . hashCode () );
155
163
if (node ==null ) return null ;
156
164
if (node instanceof KeyValueNode ) return ((KeyValueNode <V >)node ).value ;
157
165
return null ;
158
166
}
159
167
168
+ /**
169
+ * {@inheritDoc}
170
+ */
171
+ @ Override
172
+ public boolean contains (K key ) {
173
+ Node node = find (key .hashCode ());
174
+ return (node !=null );
175
+ }
176
+
160
177
/**
161
178
* {@inheritDoc}
162
179
*/
163
180
@ Override
164
181
public V remove (K key ) {
165
- Node node = this . find (key );
182
+ Node node = find (key . hashCode () );
166
183
if (node ==null ) return null ;
167
184
if (node instanceof ArrayNode ) return null ;
168
185
169
- //System.out.println("remove="+key);
170
- //System.out.println(this.toString());
171
186
KeyValueNode <V > kvNode = (KeyValueNode <V >)node ;
172
187
V value = kvNode .value ;
173
188
if (node .parent ==null ) {
@@ -176,7 +191,7 @@ public V remove(K key) {
176
191
} else {
177
192
ArrayNode parent = node .parent ;
178
193
// Remove child from parent
179
- int position = getPosition (parent .level , node .key );
194
+ int position = getPosition (parent .height , node .key );
180
195
parent .removeChild (position );
181
196
// Go back up the tree, pruning any array nodes which no longer have children.
182
197
int numOfChildren = parent .getNumberOfChildren ();
@@ -188,25 +203,15 @@ public V remove(K key) {
188
203
root = null ;
189
204
break ;
190
205
}
191
- position = getPosition (parent .level , node .key );
206
+ position = getPosition (parent .height , node .key );
192
207
parent .removeChild (position );
193
208
numOfChildren = parent .getNumberOfChildren ();
194
209
}
195
210
}
196
- //System.out.println(this.toString());
197
211
size --;
198
212
return value ;
199
213
}
200
214
201
- /**
202
- * {@inheritDoc}
203
- */
204
- @ Override
205
- public boolean contains (K key ) {
206
- Node node = find (key );
207
- return (node !=null );
208
- }
209
-
210
215
/**
211
216
* {@inheritDoc}
212
217
*/
@@ -215,18 +220,18 @@ public int size() {
215
220
return size ;
216
221
}
217
222
218
- private static <K extends Number , V > boolean validate (ArrayNode parent , KeyValueNode <V > child ) {
219
- if (parent ==null || parent .level ==0 ) return true ;
223
+ private static <K , V > boolean validate (ArrayNode parent , KeyValueNode <V > child ) {
224
+ if (parent ==null || parent .height ==0 ) return true ;
220
225
221
- int parentPosition = getPosition (parent .level -1 ,parent .key );
222
- int childPosition = getPosition (parent .level -1 ,child .key );
226
+ int parentPosition = getPosition (parent .height -1 ,parent .key );
227
+ int childPosition = getPosition (parent .height -1 ,child .key );
223
228
return (childPosition ==parentPosition );
224
229
}
225
230
226
- private static <K extends Number , V > boolean validate (ArrayNode parent , ArrayNode node ) {
231
+ private static <K , V > boolean validate (ArrayNode parent , ArrayNode node ) {
227
232
if (parent !=null ) {
228
233
if (parent .key != (node .parent .key )) return false ;
229
- if (parent .level +1 != node .level ) return false ;
234
+ if (parent .height +1 != node .height ) return false ;
230
235
}
231
236
232
237
int children = 0 ;
@@ -278,33 +283,59 @@ protected static class Node {
278
283
279
284
private static final class ArrayNode extends Node {
280
285
281
- private byte level = 0 ;
286
+ private byte height = 0 ;
282
287
private int bitmap = 0 ;
283
288
private Node [] children = new Node [2 ];
284
289
285
- private ArrayNode (ArrayNode parent , int key , byte level ) {
290
+ private ArrayNode (ArrayNode parent , int key , byte height ) {
286
291
this .parent = parent ;
287
292
this .key = key ;
288
- this .level = level ;
293
+ this .height = height ;
289
294
}
290
295
296
+ /**
297
+ * Set the bit at position (zero based)
298
+ *
299
+ * e.g. bitmap=0000 position==3 result=1000
300
+ */
291
301
private void set (int position ) {
292
302
bitmap |= (1 << position );
293
303
}
294
304
305
+ /**
306
+ * Unset the bit at position (zero based)
307
+ *
308
+ * e.g. bitmap==110 position=1 result=100
309
+ */
295
310
private void unset (int position ) {
296
311
bitmap &= ~(1 << position );
297
312
}
298
313
314
+ /**
315
+ * Is the bit at position set (zero based)
316
+ *
317
+ * e.g. bitmap=01110 position=3 result=00110
318
+ */
299
319
private boolean isSet (int position ) {
300
320
return ((bitmap &(1 << position )) >>> position )==1 ;
301
321
}
302
322
323
+ /**
324
+ * Count the number of 1s in the bitmap between 0 and position (zero based)
325
+ *
326
+ * Because each 1 in the bitmap means a child exists, you can use the 1s count
327
+ * to calculate the index of the child at the given position.
328
+ *
329
+ * e.g. bitmap=01110010 position=5 result=3
330
+ */
303
331
private int getIndex (int position ){
304
332
position = (1 << position )-1 ;
305
333
return Integer .bitCount (bitmap & position );
306
334
}
307
335
336
+ /**
337
+ * Calculates the height by doubling the size with a max size of MAX_BITS
338
+ */
308
339
private static int calcNewLength (int size ) {
309
340
if (size ==MAX_BITS ) return size ;
310
341
@@ -362,7 +393,7 @@ private int getNumberOfChildren() {
362
393
@ Override
363
394
public String toString () {
364
395
StringBuilder builder = new StringBuilder ();
365
- builder .append ("level =" ).append (level ).append (" key=" ).append (toBinaryString (key )).append ("\n " );
396
+ builder .append ("height =" ).append (height ).append (" key=" ).append (toBinaryString (key )).append ("\n " );
366
397
for (int i =0 ; i <MAX_BITS ; i ++) {
367
398
Node c = getChild (i );
368
399
if (c !=null ) builder .append (c .toString ()).append (", " );
@@ -401,32 +432,32 @@ public String toString() {
401
432
402
433
protected static class TreePrinter {
403
434
404
- public static <K extends Number , V > String getString (HashArrayMappedTrie <K ,V > tree ) {
435
+ public static <K , V > String getString (HashArrayMappedTrie <K ,V > tree ) {
405
436
if (tree .root == null ) return "Tree has no nodes." ;
406
- return getString (tree .root , -1 , "" , true );
437
+ return getString (null , tree .root , -1 , "" , true );
407
438
}
408
439
409
- private static <K , V > String getString (Node node , int level , String prefix , boolean isTail ) {
440
+ private static <K , V > String getString (Node parent , Node node , int height , String prefix , boolean isTail ) {
410
441
StringBuilder builder = new StringBuilder ();
411
442
412
443
if (node instanceof KeyValueNode ) {
413
444
KeyValueNode <V > kvNode = (KeyValueNode <V >) node ;
414
- int position = getPosition (level ,kvNode .key );
415
- builder .append (prefix + (isTail ? "└── " : "├── " ) + toBinaryString (position ) + "=" + toBinaryString (kvNode .key ) + "=" + kvNode .value + "\n " );
445
+ int position = getPosition (height ,kvNode .key );
446
+ builder .append (prefix + (isTail ? "└── " : "├── " ) + (( parent == null )? null : toBinaryString (position ) ) + "=" + toBinaryString (kvNode .key ) + "=" + kvNode .value + "\n " );
416
447
} else {
417
448
ArrayNode arrayNode = (ArrayNode ) node ;
418
- int position = getPosition (level ,arrayNode .key );
419
- builder .append (prefix + (isTail ? "└── " : "├── " ) + toBinaryString (position ) + " level =" + arrayNode . level + " bitmap=" + toBinaryString (arrayNode .bitmap ) + "\n " );
449
+ int position = ( arrayNode . parent == null )?- 1 : getPosition (height ,arrayNode .key );
450
+ builder .append (prefix + (isTail ? "└── " : "├── " ) + (( parent == null )? null : toBinaryString (position )) + " height =" + (( height < 0 )? null : height ) + " bitmap=" + toBinaryString (arrayNode .bitmap ) + "\n " );
420
451
List <Node > children = new LinkedList <Node >();
421
452
for (int i =0 ; i <MAX_BITS ; i ++) {
422
453
Node child = arrayNode .getChild (i );
423
454
if (child != null ) children .add (child );
424
455
}
425
456
for (int i = 0 ; i <(children .size ()-1 ); i ++) {
426
- builder .append (getString (children .get (i ), level +1 , prefix +(isTail ? " " : "│ " ), false ));
457
+ builder .append (getString (arrayNode , children .get (i ), height +1 , prefix +(isTail ? " " : "│ " ), false ));
427
458
}
428
459
if (children .size () >= 1 ) {
429
- builder .append (getString (children .get (children .size ()-1 ), level +1 , prefix +(isTail ? " " : "│ " ), true ));
460
+ builder .append (getString (arrayNode , children .get (children .size ()-1 ), height +1 , prefix +(isTail ? " " : "│ " ), true ));
430
461
}
431
462
}
432
463
return builder .toString ();
@@ -441,7 +472,7 @@ public Map<K,V> toMap() {
441
472
return (new JavaCompatibleHashMap <K ,V >(this ));
442
473
}
443
474
444
- private static class JavaCompatibleIteratorWrapper <K extends Number ,V > implements java .util .Iterator <java .util .Map .Entry <K , V >> {
475
+ private static class JavaCompatibleIteratorWrapper <K ,V > implements java .util .Iterator <java .util .Map .Entry <K , V >> {
445
476
446
477
private HashArrayMappedTrie <K ,V > map = null ;
447
478
private java .util .Iterator <java .util .Map .Entry <K , V >> iter = null ;
@@ -484,7 +515,7 @@ public void remove() {
484
515
}
485
516
}
486
517
487
- private static class JavaCompatibleMapEntry <K extends Number ,V > extends java .util .AbstractMap .SimpleEntry <K ,V > {
518
+ private static class JavaCompatibleMapEntry <K ,V > extends java .util .AbstractMap .SimpleEntry <K ,V > {
488
519
489
520
private static final long serialVersionUID = -2943023801121889519L ;
490
521
@@ -493,7 +524,7 @@ public JavaCompatibleMapEntry(K key, V value) {
493
524
}
494
525
}
495
526
496
- private static class JavaCompatibleHashMap <K extends Number , V > extends java .util .AbstractMap <K ,V > {
527
+ private static class JavaCompatibleHashMap <K , V > extends java .util .AbstractMap <K ,V > {
497
528
498
529
private HashArrayMappedTrie <K ,V > map = null ;
499
530
0 commit comments