Skip to content

Commit 03b66f9

Browse files
author
phishman3579
committed
Cleaned up the Hash Array Mapped Trie (HAMT) data structure.
git-svn-id: https://java-algorithms-implementation.googlecode.com/svn/trunk@431 032fbc0f-8cab-eb90-e552-f08422b9a96a
1 parent 45ec8ac commit 03b66f9

File tree

1 file changed

+97
-66
lines changed

1 file changed

+97
-66
lines changed

src/com/jwetherell/algorithms/data_structures/HashArrayMappedTrie.java

+97-66
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,30 @@
99
* array that combines the characteristics of a hash table and an array mapped
1010
* trie. It is a refined version of the more general notion of a hash tree.
1111
*
12+
* This implementation is 32-bit and steps in 5-bit intervals, maximum tree
13+
* height of 7.
14+
*
1215
* http://en.wikipedia.org/wiki/Hash_array_mapped_trie
1316
*
1417
* @author Justin Wetherell <phishman3579@gmail.com>
1518
*/
1619
@SuppressWarnings("unchecked")
17-
public class HashArrayMappedTrie<K extends Number, V> implements IMap<K,V> {
20+
public class HashArrayMappedTrie<K, V> implements IMap<K,V> {
1821

1922
private static final byte MAX_BITS = 32;
2023
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
2225

2326
private Node root = null;
2427
private int size = 0;
2528

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+
*/
2635
private static final String toBinaryString(int value) {
27-
if (value<0) return "null";
28-
2936
StringBuilder builder = new StringBuilder(Integer.toBinaryString(value));
3037
builder = builder.reverse();
3138
while (builder.length()<BITS) {
@@ -34,11 +41,16 @@ private static final String toBinaryString(int value) {
3441
return builder.toString();
3542
}
3643

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;
3951
}
4052

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) {
4254
if (node instanceof KeyValueNode) {
4355
KeyValueNode<V> kvNode = (KeyValueNode<V>) node;
4456
if (key==kvNode.key) {
@@ -48,10 +60,10 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
4860
}
4961
// Key already exists but doesn't match current key
5062
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);
5567
newParent.parent = parent;
5668
if (parent==null) {
5769
// 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) {
6981
}
7082
while (oldParentPosition == childPosition) {
7183
// 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) {
7486
// We have found two keys which match exactly.
7587
System.err.println("Yikes! Found two keys which match exactly.");
7688
return null;
7789
}
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);
8092
newParent.addChild(newParentPosition, newParent2);
8193

82-
oldParentPosition = getPosition(level, oldParent.key);
83-
childPosition = getPosition(level, key);
94+
oldParentPosition = getPosition(height, oldParent.key);
95+
childPosition = getPosition(height, key);
8496
if (oldParentPosition != childPosition) {
8597
newParent2.addChild(oldParentPosition, oldParent);
8698
oldParent.parent = newParent2;
@@ -92,14 +104,14 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
92104
return value;
93105
} else if (node instanceof ArrayNode) {
94106
ArrayNode arrayRoot = (ArrayNode) node;
95-
int position = getPosition(arrayRoot.level, key);
107+
int position = getPosition(arrayRoot.height, key);
96108
Node child = arrayRoot.getChild(position);
97109
if (child==null) {
98110
// Found an empty slot in parent
99111
arrayRoot.addChild(position, new KeyValueNode<V>(arrayRoot, key, value));
100112
return value;
101113
}
102-
return put(arrayRoot, child, (byte)(level+1), key, value);
114+
return put(arrayRoot, child, (byte)(height+1), key, value);
103115
}
104116
return null;
105117
}
@@ -109,39 +121,35 @@ private V put(ArrayNode parent, Node node, byte level, int key, V value) {
109121
*/
110122
@Override
111123
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();
115125
V toReturn = null;
116126
if (root==null) {
117127
root = new KeyValueNode<V>(null, intKey, value);
118128
toReturn = value;
119129
} else {
120130
toReturn = put(null, root, (byte)0, intKey, value);
121131
}
122-
//System.out.println(this.toString());
123132
if (toReturn!=null) size++;
124133
return toReturn;
125134
}
126135

127-
private Node find(Node node, K key) {
136+
private Node find(Node node, int key) {
128137
if (node instanceof KeyValueNode) {
129138
KeyValueNode<V> kvNode = (KeyValueNode<V>) node;
130-
if (key.equals(kvNode.key))
139+
if (kvNode.key==key)
131140
return kvNode;
132141
return null;
133142
} else if (node instanceof ArrayNode) {
134143
ArrayNode arrayNode = (ArrayNode)node;
135-
int intKey = key.intValue();
136-
int position = getPosition(arrayNode.level, intKey);
144+
int position = getPosition(arrayNode.height, key);
137145
Node possibleNode = arrayNode.getChild(position);
138146
if (possibleNode==null) return null;
139147
return find(possibleNode,key);
140148
}
141149
return null;
142150
}
143151

144-
private Node find(K key) {
152+
private Node find(int key) {
145153
if (root==null) return null;
146154
return find(root, key);
147155
}
@@ -151,23 +159,30 @@ private Node find(K key) {
151159
*/
152160
@Override
153161
public V get(K key) {
154-
Node node = find(key);
162+
Node node = find(key.hashCode());
155163
if (node==null) return null;
156164
if (node instanceof KeyValueNode) return ((KeyValueNode<V>)node).value;
157165
return null;
158166
}
159167

168+
/**
169+
* {@inheritDoc}
170+
*/
171+
@Override
172+
public boolean contains(K key) {
173+
Node node = find(key.hashCode());
174+
return (node!=null);
175+
}
176+
160177
/**
161178
* {@inheritDoc}
162179
*/
163180
@Override
164181
public V remove(K key) {
165-
Node node = this.find(key);
182+
Node node = find(key.hashCode());
166183
if (node==null) return null;
167184
if (node instanceof ArrayNode) return null;
168185

169-
//System.out.println("remove="+key);
170-
//System.out.println(this.toString());
171186
KeyValueNode<V> kvNode = (KeyValueNode<V>)node;
172187
V value = kvNode.value;
173188
if (node.parent==null) {
@@ -176,7 +191,7 @@ public V remove(K key) {
176191
} else {
177192
ArrayNode parent = node.parent;
178193
// Remove child from parent
179-
int position = getPosition(parent.level, node.key);
194+
int position = getPosition(parent.height, node.key);
180195
parent.removeChild(position);
181196
// Go back up the tree, pruning any array nodes which no longer have children.
182197
int numOfChildren = parent.getNumberOfChildren();
@@ -188,25 +203,15 @@ public V remove(K key) {
188203
root = null;
189204
break;
190205
}
191-
position = getPosition(parent.level, node.key);
206+
position = getPosition(parent.height, node.key);
192207
parent.removeChild(position);
193208
numOfChildren = parent.getNumberOfChildren();
194209
}
195210
}
196-
//System.out.println(this.toString());
197211
size--;
198212
return value;
199213
}
200214

201-
/**
202-
* {@inheritDoc}
203-
*/
204-
@Override
205-
public boolean contains(K key) {
206-
Node node = find(key);
207-
return (node!=null);
208-
}
209-
210215
/**
211216
* {@inheritDoc}
212217
*/
@@ -215,18 +220,18 @@ public int size() {
215220
return size;
216221
}
217222

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;
220225

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);
223228
return (childPosition==parentPosition);
224229
}
225230

226-
private static <K extends Number, V> boolean validate(ArrayNode parent, ArrayNode node) {
231+
private static <K, V> boolean validate(ArrayNode parent, ArrayNode node) {
227232
if (parent!=null) {
228233
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;
230235
}
231236

232237
int children = 0;
@@ -278,33 +283,59 @@ protected static class Node {
278283

279284
private static final class ArrayNode extends Node {
280285

281-
private byte level = 0;
286+
private byte height = 0;
282287
private int bitmap = 0;
283288
private Node[] children = new Node[2];
284289

285-
private ArrayNode(ArrayNode parent, int key, byte level) {
290+
private ArrayNode(ArrayNode parent, int key, byte height) {
286291
this.parent = parent;
287292
this.key = key;
288-
this.level = level;
293+
this.height = height;
289294
}
290295

296+
/**
297+
* Set the bit at position (zero based)
298+
*
299+
* e.g. bitmap=0000 position==3 result=1000
300+
*/
291301
private void set(int position) {
292302
bitmap |= (1 << position);
293303
}
294304

305+
/**
306+
* Unset the bit at position (zero based)
307+
*
308+
* e.g. bitmap==110 position=1 result=100
309+
*/
295310
private void unset(int position) {
296311
bitmap &= ~(1 << position);
297312
}
298313

314+
/**
315+
* Is the bit at position set (zero based)
316+
*
317+
* e.g. bitmap=01110 position=3 result=00110
318+
*/
299319
private boolean isSet(int position) {
300320
return ((bitmap &(1 << position)) >>> position)==1;
301321
}
302322

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+
*/
303331
private int getIndex(int position){
304332
position = (1 << position)-1;
305333
return Integer.bitCount(bitmap & position);
306334
}
307335

336+
/**
337+
* Calculates the height by doubling the size with a max size of MAX_BITS
338+
*/
308339
private static int calcNewLength(int size) {
309340
if (size==MAX_BITS) return size;
310341

@@ -362,7 +393,7 @@ private int getNumberOfChildren() {
362393
@Override
363394
public String toString() {
364395
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");
366397
for (int i=0; i<MAX_BITS; i++) {
367398
Node c = getChild(i);
368399
if (c!=null) builder.append(c.toString()).append(", ");
@@ -401,32 +432,32 @@ public String toString() {
401432

402433
protected static class TreePrinter {
403434

404-
public static <K extends Number, V> String getString(HashArrayMappedTrie<K,V> tree) {
435+
public static <K, V> String getString(HashArrayMappedTrie<K,V> tree) {
405436
if (tree.root == null) return "Tree has no nodes.";
406-
return getString(tree.root, -1, "", true);
437+
return getString(null, tree.root, -1, "", true);
407438
}
408439

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) {
410441
StringBuilder builder = new StringBuilder();
411442

412443
if (node instanceof KeyValueNode) {
413444
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");
416447
} else {
417448
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");
420451
List<Node> children = new LinkedList<Node>();
421452
for (int i=0; i<MAX_BITS; i++) {
422453
Node child = arrayNode.getChild(i);
423454
if (child != null) children.add(child);
424455
}
425456
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));
427458
}
428459
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));
430461
}
431462
}
432463
return builder.toString();
@@ -441,7 +472,7 @@ public Map<K,V> toMap() {
441472
return (new JavaCompatibleHashMap<K,V>(this));
442473
}
443474

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>> {
445476

446477
private HashArrayMappedTrie<K,V> map = null;
447478
private java.util.Iterator<java.util.Map.Entry<K, V>> iter = null;
@@ -484,7 +515,7 @@ public void remove() {
484515
}
485516
}
486517

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> {
488519

489520
private static final long serialVersionUID = -2943023801121889519L;
490521

@@ -493,7 +524,7 @@ public JavaCompatibleMapEntry(K key, V value) {
493524
}
494525
}
495526

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> {
497528

498529
private HashArrayMappedTrie<K,V> map = null;
499530

0 commit comments

Comments
 (0)