Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Switching from 4 spaces to 2 spaces

  • Loading branch information...
commit 356675fc129586a9b450206d24d8b032c5bf4def 1 parent 12f25d1
@rkapsi authored
Showing with 4,902 additions and 4,902 deletions.
  1. +7 −7 src/main/java/org/ardverk/collection/AbstractKeyAnalyzer.java
  2. +997 −997 src/main/java/org/ardverk/collection/AbstractPatriciaTrie.java
  3. +171 −171 src/main/java/org/ardverk/collection/AbstractTrie.java
  4. +192 −192 src/main/java/org/ardverk/collection/ByteArrayKeyAnalyzer.java
  5. +49 −49 src/main/java/org/ardverk/collection/ByteKeyAnalyzer.java
  6. +114 −114 src/main/java/org/ardverk/collection/CharArrayKeyAnalyzer.java
  7. +72 −72 src/main/java/org/ardverk/collection/CharacterKeyAnalyzer.java
  8. +40 −40 src/main/java/org/ardverk/collection/Cursor.java
  9. +30 −30 src/main/java/org/ardverk/collection/DefaultKeyAnalyzer.java
  10. +45 −45 src/main/java/org/ardverk/collection/IntegerKeyAnalyzer.java
  11. +21 −21 src/main/java/org/ardverk/collection/Key.java
  12. +39 −39 src/main/java/org/ardverk/collection/KeyAnalyzer.java
  13. +49 −49 src/main/java/org/ardverk/collection/LongKeyAnalyzer.java
  14. +1,079 −1,079 src/main/java/org/ardverk/collection/PatriciaTrie.java
  15. +49 −49 src/main/java/org/ardverk/collection/ShortKeyAnalyzer.java
  16. +95 −95 src/main/java/org/ardverk/collection/StringKeyAnalyzer.java
  17. +110 −110 src/main/java/org/ardverk/collection/Trie.java
  18. +637 −637 src/main/java/org/ardverk/collection/Tries.java
  19. +92 −92 src/test/java/org/ardverk/collection/ByteArrayKeyAnalyzerTest.java
  20. +969 −969 src/test/java/org/ardverk/collection/PatriciaTrieTest.java
  21. +45 −45 src/test/java/org/ardverk/collection/SerializationTest.java
View
14 src/main/java/org/ardverk/collection/AbstractKeyAnalyzer.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,10 +20,10 @@
* An abstract implementation of {@link KeyAnalyzer}.
*/
public abstract class AbstractKeyAnalyzer<K> implements KeyAnalyzer<K> {
-
- @SuppressWarnings("unchecked")
- @Override
- public int compare(K o1, K o2) {
- return ((Comparable<K>)o1).compareTo(o2);
- }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(K o1, K o2) {
+ return ((Comparable<K>)o1).compareTo(o2);
+ }
}
View
1,994 src/main/java/org/ardverk/collection/AbstractPatriciaTrie.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -32,1107 +32,1107 @@
* is related to the {@link Map} interface.
*/
abstract class AbstractPatriciaTrie<K, V> extends AbstractTrie<K, V> {
-
- private static final long serialVersionUID = -2303909182832019043L;
+
+ private static final long serialVersionUID = -2303909182832019043L;
- /**
- * The root node of the {@link Trie}.
- */
- final TrieEntry<K, V> root = new TrieEntry<K, V>(null, null, -1);
-
- /**
- * Each of these fields are initialized to contain an instance of the
- * appropriate view the first time this view is requested. The views are
- * stateless, so there's no reason to create more than one of each.
- */
- private transient volatile Set<K> keySet;
- private transient volatile Collection<V> values;
- private transient volatile Set<Map.Entry<K,V>> entrySet;
+ /**
+ * The root node of the {@link Trie}.
+ */
+ final TrieEntry<K, V> root = new TrieEntry<K, V>(null, null, -1);
+
+ /**
+ * Each of these fields are initialized to contain an instance of the
+ * appropriate view the first time this view is requested. The views are
+ * stateless, so there's no reason to create more than one of each.
+ */
+ private transient volatile Set<K> keySet;
+ private transient volatile Collection<V> values;
+ private transient volatile Set<Map.Entry<K,V>> entrySet;
+
+ /**
+ * The current size of the {@link Trie}
+ */
+ private int size = 0;
+
+ /**
+ * The number of times this {@link Trie} has been modified.
+ * It's used to detect concurrent modifications and fail-fast
+ * the {@link Iterator}s.
+ */
+ transient int modCount = 0;
+
+ public AbstractPatriciaTrie() {
+ super();
+ }
+
+ public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer) {
+ super(keyAnalyzer);
+ }
+
+ public AbstractPatriciaTrie(Map<? extends K, ? extends V> m) {
+ super();
+ putAll(m);
+ }
+
+ public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer,
+ Map<? extends K, ? extends V> m) {
+ super(keyAnalyzer);
+ putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ root.key = null;
+ root.bitIndex = -1;
+ root.value = null;
- /**
- * The current size of the {@link Trie}
- */
- private int size = 0;
-
- /**
- * The number of times this {@link Trie} has been modified.
- * It's used to detect concurrent modifications and fail-fast
- * the {@link Iterator}s.
- */
- transient int modCount = 0;
+ root.parent = null;
+ root.left = root;
+ root.right = null;
+ root.predecessor = root;
- public AbstractPatriciaTrie() {
- super();
+ size = 0;
+ incrementModCount();
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ /**
+ * A helper method to increment the {@link Trie} size
+ * and the modification counter.
+ */
+ void incrementSize() {
+ size++;
+ incrementModCount();
+ }
+
+ /**
+ * A helper method to decrement the {@link Trie} size
+ * and increment the modification counter.
+ */
+ void decrementSize() {
+ size--;
+ incrementModCount();
+ }
+
+ /**
+ * A helper method to increment the modification counter.
+ */
+ private void incrementModCount() {
+ ++modCount;
+ }
+
+ @Override
+ public V put(K key, V value) {
+ if (key == null) {
+ throw new NullPointerException("Key cannot be null");
}
- public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer) {
- super(keyAnalyzer);
+ int lengthInBits = lengthInBits(key);
+
+ // The only place to store a key with a length
+ // of zero bits is the root node
+ if (lengthInBits == 0) {
+ if (root.isEmpty()) {
+ incrementSize();
+ } else {
+ incrementModCount();
+ }
+ return root.setKeyValue(key, value);
}
- public AbstractPatriciaTrie(Map<? extends K, ? extends V> m) {
- super();
- putAll(m);
+ TrieEntry<K, V> found = getNearestEntryForKey(key);
+ if (compareKeys(key, found.key)) {
+ if (found.isEmpty()) { // <- must be the root
+ incrementSize();
+ } else {
+ incrementModCount();
+ }
+ return found.setKeyValue(key, value);
}
- public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer,
- Map<? extends K, ? extends V> m) {
- super(keyAnalyzer);
- putAll(m);
+ int bitIndex = bitIndex(key, found.key);
+ if (!Tries.isOutOfBoundsIndex(bitIndex)) {
+ if (Tries.isValidBitIndex(bitIndex)) { // in 99.999...9% the case
+ /* NEW KEY+VALUE TUPLE */
+ TrieEntry<K, V> t = new TrieEntry<K, V>(key, value, bitIndex);
+ addEntry(t);
+ incrementSize();
+ return null;
+ } else if (Tries.isNullBitKey(bitIndex)) {
+ // A bits of the Key are zero. The only place to
+ // store such a Key is the root Node!
+
+ /* NULL BIT KEY */
+ if (root.isEmpty()) {
+ incrementSize();
+ } else {
+ incrementModCount();
+ }
+ return root.setKeyValue(key, value);
+
+ } else if (Tries.isEqualBitKey(bitIndex)) {
+ // This is a very special and rare case.
+
+ /* REPLACE OLD KEY+VALUE */
+ if (found != root) {
+ incrementModCount();
+ return found.setKeyValue(key, value);
+ }
+ }
}
- @Override
- public void clear() {
- root.key = null;
- root.bitIndex = -1;
- root.value = null;
+ throw new IndexOutOfBoundsException("Failed to put: "
+ + key + " -> " + value + ", " + bitIndex);
+ }
+
+ /**
+ * Adds the given {@link TrieEntry} to the {@link Trie}
+ */
+ TrieEntry<K, V> addEntry(TrieEntry<K, V> entry) {
+ TrieEntry<K, V> current = root.left;
+ TrieEntry<K, V> path = root;
+ while(true) {
+ if (current.bitIndex >= entry.bitIndex
+ || current.bitIndex <= path.bitIndex) {
+ entry.predecessor = entry;
+
+ if (!isBitSet(entry.key, entry.bitIndex)) {
+ entry.left = entry;
+ entry.right = current;
+ } else {
+ entry.left = current;
+ entry.right = entry;
+ }
+
+ entry.parent = path;
+ if (current.bitIndex >= entry.bitIndex) {
+ current.parent = entry;
+ }
+
+ // if we inserted an uplink, set the predecessor on it
+ if (current.bitIndex <= path.bitIndex) {
+ current.predecessor = entry;
+ }
+
+ if (path == root || !isBitSet(entry.key, path.bitIndex)) {
+ path.left = entry;
+ } else {
+ path.right = entry;
+ }
- root.parent = null;
- root.left = root;
- root.right = null;
- root.predecessor = root;
+ return entry;
+ }
- size = 0;
- incrementModCount();
+ path = current;
+
+ if (!isBitSet(entry.key, current.bitIndex)) {
+ current = current.left;
+ } else {
+ current = current.right;
+ }
+ }
+ }
+
+ @Override
+ public V get(Object k) {
+ TrieEntry<K, V> entry = getEntry(k);
+ return entry != null ? entry.getValue() : null;
+ }
+
+ /**
+ * Returns the entry associated with the specified key in the
+ * AbstractPatriciaTrie. Returns null if the map contains no mapping
+ * for this key.
+ *
+ * This may throw ClassCastException if the object is not of type K.
+ */
+ TrieEntry<K,V> getEntry(Object k) {
+ K key = Tries.<K>cast(k);
+ if (key == null) {
+ return null;
}
- @Override
- public int size() {
- return size;
+ TrieEntry<K,V> entry = getNearestEntryForKey(key);
+ return !entry.isEmpty() && compareKeys(key, entry.key) ? entry : null;
+ }
+
+ @Override
+ public Map.Entry<K, V> select(K key) {
+ Reference<Map.Entry<K, V>> reference
+ = new Reference<Map.Entry<K,V>>();
+ if (!selectR(root.left, -1, key, reference)) {
+ return reference.get();
}
-
- /**
- * A helper method to increment the {@link Trie} size
- * and the modification counter.
- */
- void incrementSize() {
- size++;
- incrementModCount();
+ return null;
+ }
+
+ @Override
+ public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor) {
+ Reference<Map.Entry<K, V>> reference
+ = new Reference<Map.Entry<K,V>>();
+ selectR(root.left, -1, key, cursor, reference);
+ return reference.get();
+ }
+
+ /**
+ * This is equivalent to the other {@link #selectR(TrieEntry, int,
+ * Object, int, Cursor, Reference)} method but without its overhead
+ * because we're selecting only one best matching Entry from the
+ * {@link Trie}.
+ */
+ private boolean selectR(TrieEntry<K, V> h, int bitIndex,
+ final K key, final Reference<Map.Entry<K, V>> reference) {
+
+ if (h.bitIndex <= bitIndex) {
+ // If we hit the root Node and it is empty
+ // we have to look for an alternative best
+ // matching node.
+ if (!h.isEmpty()) {
+ reference.set(h);
+ return false;
+ }
+ return true;
+ }
+
+ if (!isBitSet(key, h.bitIndex)) {
+ if (selectR(h.left, h.bitIndex, key, reference)) {
+ return selectR(h.right, h.bitIndex, key, reference);
+ }
+ } else {
+ if (selectR(h.right, h.bitIndex, key, reference)) {
+ return selectR(h.left, h.bitIndex, key, reference);
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ */
+ private boolean selectR(TrieEntry<K,V> h, int bitIndex,
+ final K key, final Cursor<? super K, ? super V> cursor,
+ final Reference<Map.Entry<K, V>> reference) {
+
+ if (h.bitIndex <= bitIndex) {
+ if (!h.isEmpty()) {
+ Decision decision = cursor.select(h);
+ switch(decision) {
+ case REMOVE:
+ throw new UnsupportedOperationException(
+ "Cannot remove during select");
+ case EXIT:
+ reference.set(h);
+ return false; // exit
+ case REMOVE_AND_EXIT:
+ TrieEntry<K, V> entry = new TrieEntry<K, V>(
+ h.getKey(), h.getValue(), -1);
+ reference.set(entry);
+ removeEntry(h);
+ return false;
+ case CONTINUE:
+ // fall through.
+ }
+ }
+ return true; // continue
+ }
+
+ if (!isBitSet(key, h.bitIndex)) {
+ if (selectR(h.left, h.bitIndex, key, cursor, reference)) {
+ return selectR(h.right, h.bitIndex, key, cursor, reference);
+ }
+ } else {
+ if (selectR(h.right, h.bitIndex, key, cursor, reference)) {
+ return selectR(h.left, h.bitIndex, key, cursor, reference);
+ }
}
- /**
- * A helper method to decrement the {@link Trie} size
- * and increment the modification counter.
- */
- void decrementSize() {
- size--;
- incrementModCount();
+ return false;
+ }
+
+ @Override
+ public Map.Entry<K, V> traverse(Cursor<? super K, ? super V> cursor) {
+ TrieEntry<K, V> entry = nextEntry(null);
+ while (entry != null) {
+ TrieEntry<K, V> current = entry;
+
+ Decision decision = cursor.select(current);
+ entry = nextEntry(current);
+
+ switch(decision) {
+ case EXIT:
+ return current;
+ case REMOVE:
+ removeEntry(current);
+ break; // out of switch, stay in while loop
+ case REMOVE_AND_EXIT:
+ Map.Entry<K, V> value = new TrieEntry<K, V>(
+ current.getKey(), current.getValue(), -1);
+ removeEntry(current);
+ return value;
+ case CONTINUE: // do nothing.
+ }
}
- /**
- * A helper method to increment the modification counter.
- */
- private void incrementModCount() {
- ++modCount;
+ return null;
+ }
+
+ @Override
+ public boolean containsKey(Object k) {
+ if (k == null) {
+ return false;
}
- @Override
- public V put(K key, V value) {
- if (key == null) {
- throw new NullPointerException("Key cannot be null");
- }
-
- int lengthInBits = lengthInBits(key);
-
- // The only place to store a key with a length
- // of zero bits is the root node
- if (lengthInBits == 0) {
- if (root.isEmpty()) {
- incrementSize();
- } else {
- incrementModCount();
- }
- return root.setKeyValue(key, value);
- }
-
- TrieEntry<K, V> found = getNearestEntryForKey(key);
- if (compareKeys(key, found.key)) {
- if (found.isEmpty()) { // <- must be the root
- incrementSize();
- } else {
- incrementModCount();
- }
- return found.setKeyValue(key, value);
+ K key = Tries.<K>cast(k);
+ TrieEntry<K, V> entry = getNearestEntryForKey(key);
+ return !entry.isEmpty() && compareKeys(key, entry.key);
+ }
+
+ @Override
+ public Set<Map.Entry<K,V>> entrySet() {
+ if (entrySet == null) {
+ entrySet = new EntrySet();
+ }
+ return entrySet;
+ }
+
+ @Override
+ public Set<K> keySet() {
+ if (keySet == null) {
+ keySet = new KeySet();
+ }
+ return keySet;
+ }
+
+ @Override
+ public Collection<V> values() {
+ if (values == null) {
+ values = new Values();
+ }
+ return values;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws ClassCastException if provided key is of an incompatible type
+ */
+ @Override
+ public V remove(Object k) {
+ if (k == null) {
+ return null;
+ }
+
+ K key = Tries.<K>cast(k);
+ TrieEntry<K, V> current = root.left;
+ TrieEntry<K, V> path = root;
+ while (true) {
+ if (current.bitIndex <= path.bitIndex) {
+ if (!current.isEmpty() && compareKeys(key, current.key)) {
+ return removeEntry(current);
+ } else {
+ return null;
+ }
+ }
+
+ path = current;
+
+ if (!isBitSet(key, current.bitIndex)) {
+ current = current.left;
+ } else {
+ current = current.right;
+ }
+ }
+ }
+
+ /**
+ * Returns the nearest entry for a given key. This is useful
+ * for finding knowing if a given key exists (and finding the value
+ * for it), or for inserting the key.
+ *
+ * The actual get implementation. This is very similar to
+ * selectR but with the exception that it might return the
+ * root Entry even if it's empty.
+ */
+ TrieEntry<K, V> getNearestEntryForKey(K key) {
+ TrieEntry<K, V> current = root.left;
+ TrieEntry<K, V> path = root;
+ while(true) {
+ if (current.bitIndex <= path.bitIndex) {
+ return current;
+ }
+
+ path = current;
+ if (!isBitSet(key, current.bitIndex)) {
+ current = current.left;
+ } else {
+ current = current.right;
+ }
+ }
+ }
+
+ /**
+ * Removes a single entry from the {@link Trie}.
+ *
+ * If we found a Key (Entry h) then figure out if it's
+ * an internal (hard to remove) or external Entry (easy
+ * to remove)
+ */
+ V removeEntry(TrieEntry<K, V> h) {
+ if (h != root) {
+ if (h.isInternalNode()) {
+ removeInternalEntry(h);
+ } else {
+ removeExternalEntry(h);
+ }
+ }
+
+ decrementSize();
+ return h.setKeyValue(null, null);
+ }
+
+ /**
+ * Removes an external entry from the {@link Trie}.
+ *
+ * If it's an external Entry then just remove it.
+ * This is very easy and straight forward.
+ */
+ private void removeExternalEntry(TrieEntry<K, V> h) {
+ if (h == root) {
+ throw new IllegalArgumentException("Cannot delete root Entry!");
+ } else if (!h.isExternalNode()) {
+ throw new IllegalArgumentException(h + " is not an external Entry!");
+ }
+
+ TrieEntry<K, V> parent = h.parent;
+ TrieEntry<K, V> child = (h.left == h) ? h.right : h.left;
+
+ if (parent.left == h) {
+ parent.left = child;
+ } else {
+ parent.right = child;
+ }
+
+ // either the parent is changing, or the predecessor is changing.
+ if (child.bitIndex > parent.bitIndex) {
+ child.parent = parent;
+ } else {
+ child.predecessor = parent;
+ }
+
+ }
+
+ /**
+ * Removes an internal entry from the {@link Trie}.
+ *
+ * If it's an internal Entry then "good luck" with understanding
+ * this code. The Idea is essentially that Entry p takes Entry h's
+ * place in the trie which requires some re-wiring.
+ */
+ private void removeInternalEntry(TrieEntry<K, V> h) {
+ if (h == root) {
+ throw new IllegalArgumentException("Cannot delete root Entry!");
+ } else if (!h.isInternalNode()) {
+ throw new IllegalArgumentException(h + " is not an internal Entry!");
+ }
+
+ TrieEntry<K, V> p = h.predecessor;
+
+ // Set P's bitIndex
+ p.bitIndex = h.bitIndex;
+
+ // Fix P's parent, predecessor and child Nodes
+ {
+ TrieEntry<K, V> parent = p.parent;
+ TrieEntry<K, V> child = (p.left == h) ? p.right : p.left;
+
+ // if it was looping to itself previously,
+ // it will now be pointed from it's parent
+ // (if we aren't removing it's parent --
+ // in that case, it remains looping to itself).
+ // otherwise, it will continue to have the same
+ // predecessor.
+ if (p.predecessor == p && p.parent != h) {
+ p.predecessor = p.parent;
+ }
+
+ if (parent.left == p) {
+ parent.left = child;
+ } else {
+ parent.right = child;
+ }
+
+ if (child.bitIndex > parent.bitIndex) {
+ child.parent = parent;
+ }
+ };
+
+ // Fix H's parent and child Nodes
+ {
+ // If H is a parent of its left and right child
+ // then change them to P
+ if (h.left.parent == h) {
+ h.left.parent = p;
+ }
+
+ if (h.right.parent == h) {
+ h.right.parent = p;
+ }
+
+ // Change H's parent
+ if (h.parent.left == h) {
+ h.parent.left = p;
+ } else {
+ h.parent.right = p;
+ }
+ };
+
+ // Copy the remaining fields from H to P
+ //p.bitIndex = h.bitIndex;
+ p.parent = h.parent;
+ p.left = h.left;
+ p.right = h.right;
+
+ // Make sure that if h was pointing to any uplinks,
+ // p now points to them.
+ if (isValidUplink(p.left, p)) {
+ p.left.predecessor = p;
+ }
+
+ if (isValidUplink(p.right, p)) {
+ p.right.predecessor = p;
+ }
+ }
+
+ /**
+ * Returns the entry lexicographically after the given entry.
+ * If the given entry is null, returns the first node.
+ */
+ TrieEntry<K, V> nextEntry(TrieEntry<K, V> node) {
+ if (node == null) {
+ return firstEntry();
+ } else {
+ return nextEntryImpl(node.predecessor, node, null);
+ }
+ }
+
+ /**
+ * Scans for the next node, starting at the specified point, and using 'previous'
+ * as a hint that the last node we returned was 'previous' (so we know not to return
+ * it again). If 'tree' is non-null, this will limit the search to the given tree.
+ *
+ * The basic premise is that each iteration can follow the following steps:
+ *
+ * 1) Scan all the way to the left.
+ * a) If we already started from this node last time, proceed to Step 2.
+ * b) If a valid uplink is found, use it.
+ * c) If the result is an empty node (root not set), break the scan.
+ * d) If we already returned the left node, break the scan.
+ *
+ * 2) Check the right.
+ * a) If we already returned the right node, proceed to Step 3.
+ * b) If it is a valid uplink, use it.
+ * c) Do Step 1 from the right node.
+ *
+ * 3) Back up through the parents until we encounter find a parent
+ * that we're not the right child of.
+ *
+ * 4) If there's no right child of that parent, the iteration is finished.
+ * Otherwise continue to Step 5.
+ *
+ * 5) Check to see if the right child is a valid uplink.
+ * a) If we already returned that child, proceed to Step 6.
+ * Otherwise, use it.
+ *
+ * 6) If the right child of the parent is the parent itself, we've
+ * already found & returned the end of the Trie, so exit.
+ *
+ * 7) Do Step 1 on the parent's right child.
+ */
+ TrieEntry<K, V> nextEntryImpl(TrieEntry<K, V> start,
+ TrieEntry<K, V> previous, TrieEntry<K, V> tree) {
+
+ TrieEntry<K, V> current = start;
+
+ // Only look at the left if this was a recursive or
+ // the first check, otherwise we know we've already looked
+ // at the left.
+ if (previous == null || start != previous.predecessor) {
+ while (!current.left.isEmpty()) {
+ // stop traversing if we've already
+ // returned the left of this node.
+ if (previous == current.left) {
+ break;
}
- int bitIndex = bitIndex(key, found.key);
- if (!Tries.isOutOfBoundsIndex(bitIndex)) {
- if (Tries.isValidBitIndex(bitIndex)) { // in 99.999...9% the case
- /* NEW KEY+VALUE TUPLE */
- TrieEntry<K, V> t = new TrieEntry<K, V>(key, value, bitIndex);
- addEntry(t);
- incrementSize();
- return null;
- } else if (Tries.isNullBitKey(bitIndex)) {
- // A bits of the Key are zero. The only place to
- // store such a Key is the root Node!
-
- /* NULL BIT KEY */
- if (root.isEmpty()) {
- incrementSize();
- } else {
- incrementModCount();
- }
- return root.setKeyValue(key, value);
-
- } else if (Tries.isEqualBitKey(bitIndex)) {
- // This is a very special and rare case.
-
- /* REPLACE OLD KEY+VALUE */
- if (found != root) {
- incrementModCount();
- return found.setKeyValue(key, value);
- }
- }
+ if (isValidUplink(current.left, current)) {
+ return current.left;
}
- throw new IndexOutOfBoundsException("Failed to put: "
- + key + " -> " + value + ", " + bitIndex);
+ current = current.left;
+ }
}
- /**
- * Adds the given {@link TrieEntry} to the {@link Trie}
- */
- TrieEntry<K, V> addEntry(TrieEntry<K, V> entry) {
- TrieEntry<K, V> current = root.left;
- TrieEntry<K, V> path = root;
- while(true) {
- if (current.bitIndex >= entry.bitIndex
- || current.bitIndex <= path.bitIndex) {
- entry.predecessor = entry;
-
- if (!isBitSet(entry.key, entry.bitIndex)) {
- entry.left = entry;
- entry.right = current;
- } else {
- entry.left = current;
- entry.right = entry;
- }
-
- entry.parent = path;
- if (current.bitIndex >= entry.bitIndex) {
- current.parent = entry;
- }
-
- // if we inserted an uplink, set the predecessor on it
- if (current.bitIndex <= path.bitIndex) {
- current.predecessor = entry;
- }
-
- if (path == root || !isBitSet(entry.key, path.bitIndex)) {
- path.left = entry;
- } else {
- path.right = entry;
- }
-
- return entry;
- }
-
- path = current;
-
- if (!isBitSet(entry.key, current.bitIndex)) {
- current = current.left;
- } else {
- current = current.right;
- }
- }
+ // If there's no data at all, exit.
+ if (current.isEmpty()) {
+ return null;
}
- @Override
- public V get(Object k) {
- TrieEntry<K, V> entry = getEntry(k);
- return entry != null ? entry.getValue() : null;
+ // If we've already returned the left,
+ // and the immediate right is null,
+ // there's only one entry in the Trie
+ // which is stored at the root.
+ //
+ // / ("") <-- root
+ // \_/ \
+ // null <-- 'current'
+ //
+ if (current.right == null) {
+ return null;
}
-
- /**
- * Returns the entry associated with the specified key in the
- * AbstractPatriciaTrie. Returns null if the map contains no mapping
- * for this key.
- *
- * This may throw ClassCastException if the object is not of type K.
- */
- TrieEntry<K,V> getEntry(Object k) {
- K key = Tries.<K>cast(k);
- if (key == null) {
- return null;
- }
-
- TrieEntry<K,V> entry = getNearestEntryForKey(key);
- return !entry.isEmpty() && compareKeys(key, entry.key) ? entry : null;
+
+ // If nothing valid on the left, try the right.
+ if (previous != current.right) {
+ // See if it immediately is valid.
+ if (isValidUplink(current.right, current)) {
+ return current.right;
+ }
+
+ // Must search on the right's side if it wasn't initially valid.
+ return nextEntryImpl(current.right, previous, tree);
}
- @Override
- public Map.Entry<K, V> select(K key) {
- Reference<Map.Entry<K, V>> reference
- = new Reference<Map.Entry<K,V>>();
- if (!selectR(root.left, -1, key, reference)) {
- return reference.get();
- }
+ // Neither left nor right are valid, find the first parent
+ // whose child did not come from the right & traverse it.
+ while (current == current.parent.right) {
+ // If we're going to traverse to above the subtree, stop.
+ if (current == tree) {
return null;
+ }
+
+ current = current.parent;
+ }
+
+ // If we're on the top of the subtree, we can't go any higher.
+ if (current == tree) {
+ return null;
}
- @Override
- public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor) {
- Reference<Map.Entry<K, V>> reference
- = new Reference<Map.Entry<K,V>>();
- selectR(root.left, -1, key, cursor, reference);
- return reference.get();
+ // If there's no right, the parent must be root, so we're done.
+ if (current.parent.right == null) {
+ return null;
}
-
+
+ // If the parent's right points to itself, we've found one.
+ if (previous != current.parent.right
+ && isValidUplink(current.parent.right, current.parent)) {
+ return current.parent.right;
+ }
+
+ // If the parent's right is itself, there can't be any more nodes.
+ if (current.parent.right == current.parent) {
+ return null;
+ }
+
+ // We need to traverse down the parent's right's path.
+ return nextEntryImpl(current.parent.right, previous, tree);
+ }
+
+ /**
+ * Returns the first entry the {@link Trie} is storing.
+ *
+ * This is implemented by going always to the left until
+ * we encounter a valid uplink. That uplink is the first key.
+ */
+ TrieEntry<K, V> firstEntry() {
+ // if Trie is empty, no first node.
+ if (isEmpty()) {
+ return null;
+ }
+
+ return followLeft(root);
+ }
+
+ /**
+ * Goes left through the tree until it finds a valid node.
+ */
+ TrieEntry<K, V> followLeft(TrieEntry<K, V> node) {
+ while(true) {
+ TrieEntry<K, V> child = node.left;
+ // if we hit root and it didn't have a node, go right instead.
+ if (child.isEmpty()) {
+ child = node.right;
+ }
+
+ if (child.bitIndex <= node.bitIndex) {
+ return child;
+ }
+
+ node = child;
+ }
+ }
+
+ /**
+ * Returns true if 'next' is a valid uplink coming from 'from'.
+ */
+ static boolean isValidUplink(TrieEntry<?, ?> next, TrieEntry<?, ?> from) {
+ return next != null && next.bitIndex <= from.bitIndex && !next.isEmpty();
+ }
+
+ /**
+ * A {@link Reference} allows us to return something through a Method's
+ * argument list. An alternative would be to an Array with a length of
+ * one (1) but that leads to compiler warnings. Computationally and memory
+ * wise there's no difference (except for the need to load the
+ * {@link Reference} Class but that happens only once).
+ */
+ private static class Reference<E> {
+
+ private E item;
+
+ public void set(E item) {
+ this.item = item;
+ }
+
+ public E get() {
+ return item;
+ }
+ }
+
+ /**
+ * A {@link Trie} is a set of {@link TrieEntry} nodes
+ */
+ static class TrieEntry<K,V> extends BasicEntry<K, V> {
+
+ private static final long serialVersionUID = 4596023148184140013L;
+
+ /** The index this entry is comparing. */
+ protected int bitIndex;
+
+ /** The parent of this entry. */
+ protected TrieEntry<K,V> parent;
+
+ /** The left child of this entry. */
+ protected TrieEntry<K,V> left;
+
+ /** The right child of this entry. */
+ protected TrieEntry<K,V> right;
+
+ /** The entry who uplinks to this entry. */
+ protected TrieEntry<K,V> predecessor;
+
+ public TrieEntry(K key, V value, int bitIndex) {
+ super(key, value);
+
+ this.bitIndex = bitIndex;
+
+ this.parent = null;
+ this.left = this;
+ this.right = null;
+ this.predecessor = this;
+ }
+
/**
- * This is equivalent to the other {@link #selectR(TrieEntry, int,
- * Object, int, Cursor, Reference)} method but without its overhead
- * because we're selecting only one best matching Entry from the
- * {@link Trie}.
+ * Whether or not the entry is storing a key.
+ * Only the root can potentially be empty, all other
+ * nodes must have a key.
*/
- private boolean selectR(TrieEntry<K, V> h, int bitIndex,
- final K key, final Reference<Map.Entry<K, V>> reference) {
-
- if (h.bitIndex <= bitIndex) {
- // If we hit the root Node and it is empty
- // we have to look for an alternative best
- // matching node.
- if (!h.isEmpty()) {
- reference.set(h);
- return false;
- }
- return true;
- }
-
- if (!isBitSet(key, h.bitIndex)) {
- if (selectR(h.left, h.bitIndex, key, reference)) {
- return selectR(h.right, h.bitIndex, key, reference);
- }
- } else {
- if (selectR(h.right, h.bitIndex, key, reference)) {
- return selectR(h.left, h.bitIndex, key, reference);
- }
- }
- return false;
+ public boolean isEmpty() {
+ return key == null;
}
- /**
- *
+ /**
+ * Neither the left nor right child is a loopback
*/
- private boolean selectR(TrieEntry<K,V> h, int bitIndex,
- final K key, final Cursor<? super K, ? super V> cursor,
- final Reference<Map.Entry<K, V>> reference) {
-
- if (h.bitIndex <= bitIndex) {
- if (!h.isEmpty()) {
- Decision decision = cursor.select(h);
- switch(decision) {
- case REMOVE:
- throw new UnsupportedOperationException(
- "Cannot remove during select");
- case EXIT:
- reference.set(h);
- return false; // exit
- case REMOVE_AND_EXIT:
- TrieEntry<K, V> entry = new TrieEntry<K, V>(
- h.getKey(), h.getValue(), -1);
- reference.set(entry);
- removeEntry(h);
- return false;
- case CONTINUE:
- // fall through.
- }
- }
- return true; // continue
- }
-
- if (!isBitSet(key, h.bitIndex)) {
- if (selectR(h.left, h.bitIndex, key, cursor, reference)) {
- return selectR(h.right, h.bitIndex, key, cursor, reference);
- }
+ public boolean isInternalNode() {
+ return left != this && right != this;
+ }
+
+ /**
+ * Either the left or right child is a loopback
+ */
+ public boolean isExternalNode() {
+ return !isInternalNode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+
+ if (bitIndex == -1) {
+ buffer.append("RootEntry(");
+ } else {
+ buffer.append("Entry(");
+ }
+
+ buffer.append("key=").append(getKey()).append(" [").append(bitIndex).append("], ");
+ buffer.append("value=").append(getValue()).append(", ");
+ //buffer.append("bitIndex=").append(bitIndex).append(", ");
+
+ if (parent != null) {
+ if (parent.bitIndex == -1) {
+ buffer.append("parent=").append("ROOT");
+ } else {
+ buffer.append("parent=").append(parent.getKey()).append(" [").append(parent.bitIndex).append("]");
+ }
+ } else {
+ buffer.append("parent=").append("null");
+ }
+ buffer.append(", ");
+
+ if (left != null) {
+ if (left.bitIndex == -1) {
+ buffer.append("left=").append("ROOT");
+ } else {
+ buffer.append("left=").append(left.getKey()).append(" [").append(left.bitIndex).append("]");
+ }
+ } else {
+ buffer.append("left=").append("null");
+ }
+ buffer.append(", ");
+
+ if (right != null) {
+ if (right.bitIndex == -1) {
+ buffer.append("right=").append("ROOT");
} else {
- if (selectR(h.right, h.bitIndex, key, cursor, reference)) {
- return selectR(h.left, h.bitIndex, key, cursor, reference);
- }
+ buffer.append("right=").append(right.getKey()).append(" [").append(right.bitIndex).append("]");
+ }
+ } else {
+ buffer.append("right=").append("null");
+ }
+ buffer.append(", ");
+
+ if (predecessor != null) {
+ if(predecessor.bitIndex == -1) {
+ buffer.append("predecessor=").append("ROOT");
+ } else {
+ buffer.append("predecessor=").append(predecessor.getKey()).append(" [").append(predecessor.bitIndex).append("]");
}
-
- return false;
+ }
+
+ buffer.append(")");
+ return buffer.toString();
}
+ }
+
+
+ /**
+ * This is a entry set view of the {@link Trie} as returned
+ * by {@link Map#entrySet()}
+ */
+ private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
@Override
- public Map.Entry<K, V> traverse(Cursor<? super K, ? super V> cursor) {
- TrieEntry<K, V> entry = nextEntry(null);
- while (entry != null) {
- TrieEntry<K, V> current = entry;
-
- Decision decision = cursor.select(current);
- entry = nextEntry(current);
-
- switch(decision) {
- case EXIT:
- return current;
- case REMOVE:
- removeEntry(current);
- break; // out of switch, stay in while loop
- case REMOVE_AND_EXIT:
- Map.Entry<K, V> value = new TrieEntry<K, V>(
- current.getKey(), current.getValue(), -1);
- removeEntry(current);
- return value;
- case CONTINUE: // do nothing.
- }
- }
-
- return null;
+ public Iterator<Map.Entry<K,V>> iterator() {
+ return new EntryIterator();
}
@Override
- public boolean containsKey(Object k) {
- if (k == null) {
- return false;
- }
-
- K key = Tries.<K>cast(k);
- TrieEntry<K, V> entry = getNearestEntryForKey(key);
- return !entry.isEmpty() && compareKeys(key, entry.key);
+ public boolean contains(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+
+ TrieEntry<K,V> candidate = getEntry(((Map.Entry<?, ?>)o).getKey());
+ return candidate != null && candidate.equals(o);
}
@Override
- public Set<Map.Entry<K,V>> entrySet() {
- if (entrySet == null) {
- entrySet = new EntrySet();
- }
- return entrySet;
+ public boolean remove(Object o) {
+ int size = size();
+ AbstractPatriciaTrie.this.remove(o);
+ return size != size();
}
@Override
- public Set<K> keySet() {
- if (keySet == null) {
- keySet = new KeySet();
- }
- return keySet;
+ public int size() {
+ return AbstractPatriciaTrie.this.size();
}
@Override
- public Collection<V> values() {
- if (values == null) {
- values = new Values();
- }
- return values;
+ public void clear() {
+ AbstractPatriciaTrie.this.clear();
}
/**
- * {@inheritDoc}
- *
- * @throws ClassCastException if provided key is of an incompatible type
+ * An {@link Iterator} that returns {@link Entry} Objects
*/
+ private class EntryIterator extends TrieIterator<Map.Entry<K,V>> {
+ @Override
+ public Map.Entry<K,V> next() {
+ return nextEntry();
+ }
+ }
+ }
+
+ /**
+ * This is a key set view of the {@link Trie} as returned
+ * by {@link Map#keySet()}
+ */
+ private class KeySet extends AbstractSet<K> {
+
@Override
- public V remove(Object k) {
- if (k == null) {
- return null;
- }
-
- K key = Tries.<K>cast(k);
- TrieEntry<K, V> current = root.left;
- TrieEntry<K, V> path = root;
- while (true) {
- if (current.bitIndex <= path.bitIndex) {
- if (!current.isEmpty() && compareKeys(key, current.key)) {
- return removeEntry(current);
- } else {
- return null;
- }
- }
-
- path = current;
-
- if (!isBitSet(key, current.bitIndex)) {
- current = current.left;
- } else {
- current = current.right;
- }
- }
+ public Iterator<K> iterator() {
+ return new KeyIterator();
}
- /**
- * Returns the nearest entry for a given key. This is useful
- * for finding knowing if a given key exists (and finding the value
- * for it), or for inserting the key.
- *
- * The actual get implementation. This is very similar to
- * selectR but with the exception that it might return the
- * root Entry even if it's empty.
- */
- TrieEntry<K, V> getNearestEntryForKey(K key) {
- TrieEntry<K, V> current = root.left;
- TrieEntry<K, V> path = root;
- while(true) {
- if (current.bitIndex <= path.bitIndex) {
- return current;
- }
-
- path = current;
- if (!isBitSet(key, current.bitIndex)) {
- current = current.left;
- } else {
- current = current.right;
- }
- }
+ @Override
+ public int size() {
+ return AbstractPatriciaTrie.this.size();
}
- /**
- * Removes a single entry from the {@link Trie}.
- *
- * If we found a Key (Entry h) then figure out if it's
- * an internal (hard to remove) or external Entry (easy
- * to remove)
- */
- V removeEntry(TrieEntry<K, V> h) {
- if (h != root) {
- if (h.isInternalNode()) {
- removeInternalEntry(h);
- } else {
- removeExternalEntry(h);
- }
- }
-
- decrementSize();
- return h.setKeyValue(null, null);
+ @Override
+ public boolean contains(Object o) {
+ return containsKey(o);
}
- /**
- * Removes an external entry from the {@link Trie}.
- *
- * If it's an external Entry then just remove it.
- * This is very easy and straight forward.
- */
- private void removeExternalEntry(TrieEntry<K, V> h) {
- if (h == root) {
- throw new IllegalArgumentException("Cannot delete root Entry!");
- } else if (!h.isExternalNode()) {
- throw new IllegalArgumentException(h + " is not an external Entry!");
- }
-
- TrieEntry<K, V> parent = h.parent;
- TrieEntry<K, V> child = (h.left == h) ? h.right : h.left;
-
- if (parent.left == h) {
- parent.left = child;
- } else {
- parent.right = child;
- }
-
- // either the parent is changing, or the predecessor is changing.
- if (child.bitIndex > parent.bitIndex) {
- child.parent = parent;
- } else {
- child.predecessor = parent;
- }
-
+ @Override
+ public boolean remove(Object o) {
+ int size = size();
+ AbstractPatriciaTrie.this.remove(o);
+ return size != size();
}
- /**
- * Removes an internal entry from the {@link Trie}.
- *
- * If it's an internal Entry then "good luck" with understanding
- * this code. The Idea is essentially that Entry p takes Entry h's
- * place in the trie which requires some re-wiring.
- */
- private void removeInternalEntry(TrieEntry<K, V> h) {
- if (h == root) {
- throw new IllegalArgumentException("Cannot delete root Entry!");
- } else if (!h.isInternalNode()) {
- throw new IllegalArgumentException(h + " is not an internal Entry!");
- }
-
- TrieEntry<K, V> p = h.predecessor;
-
- // Set P's bitIndex
- p.bitIndex = h.bitIndex;
-
- // Fix P's parent, predecessor and child Nodes
- {
- TrieEntry<K, V> parent = p.parent;
- TrieEntry<K, V> child = (p.left == h) ? p.right : p.left;
-
- // if it was looping to itself previously,
- // it will now be pointed from it's parent
- // (if we aren't removing it's parent --
- // in that case, it remains looping to itself).
- // otherwise, it will continue to have the same
- // predecessor.
- if (p.predecessor == p && p.parent != h) {
- p.predecessor = p.parent;
- }
-
- if (parent.left == p) {
- parent.left = child;
- } else {
- parent.right = child;
- }
-
- if (child.bitIndex > parent.bitIndex) {
- child.parent = parent;
- }
- };
-
- // Fix H's parent and child Nodes
- {
- // If H is a parent of its left and right child
- // then change them to P
- if (h.left.parent == h) {
- h.left.parent = p;
- }
-
- if (h.right.parent == h) {
- h.right.parent = p;
- }
-
- // Change H's parent
- if (h.parent.left == h) {
- h.parent.left = p;
- } else {
- h.parent.right = p;
- }
- };
-
- // Copy the remaining fields from H to P
- //p.bitIndex = h.bitIndex;
- p.parent = h.parent;
- p.left = h.left;
- p.right = h.right;
-
- // Make sure that if h was pointing to any uplinks,
- // p now points to them.
- if (isValidUplink(p.left, p)) {
- p.left.predecessor = p;
- }
-
- if (isValidUplink(p.right, p)) {
- p.right.predecessor = p;
- }
+ @Override
+ public void clear() {
+ AbstractPatriciaTrie.this.clear();
}
/**
- * Returns the entry lexicographically after the given entry.
- * If the given entry is null, returns the first node.
+ * An {@link Iterator} that returns Key Objects
*/
- TrieEntry<K, V> nextEntry(TrieEntry<K, V> node) {
- if (node == null) {
- return firstEntry();
- } else {
- return nextEntryImpl(node.predecessor, node, null);
- }
+ private class KeyIterator extends TrieIterator<K> {
+ @Override
+ public K next() {
+ return nextEntry().getKey();
+ }
}
+ }
+
+ /**
+ * This is a value view of the {@link Trie} as returned
+ * by {@link Map#values()}
+ */
+ private class Values extends AbstractCollection<V> {
- /**
- * Scans for the next node, starting at the specified point, and using 'previous'
- * as a hint that the last node we returned was 'previous' (so we know not to return
- * it again). If 'tree' is non-null, this will limit the search to the given tree.
- *
- * The basic premise is that each iteration can follow the following steps:
- *
- * 1) Scan all the way to the left.
- * a) If we already started from this node last time, proceed to Step 2.
- * b) If a valid uplink is found, use it.
- * c) If the result is an empty node (root not set), break the scan.
- * d) If we already returned the left node, break the scan.
- *
- * 2) Check the right.
- * a) If we already returned the right node, proceed to Step 3.
- * b) If it is a valid uplink, use it.
- * c) Do Step 1 from the right node.
- *
- * 3) Back up through the parents until we encounter find a parent
- * that we're not the right child of.
- *
- * 4) If there's no right child of that parent, the iteration is finished.
- * Otherwise continue to Step 5.
- *
- * 5) Check to see if the right child is a valid uplink.
- * a) If we already returned that child, proceed to Step 6.
- * Otherwise, use it.
- *
- * 6) If the right child of the parent is the parent itself, we've
- * already found & returned the end of the Trie, so exit.
- *
- * 7) Do Step 1 on the parent's right child.
- */
- TrieEntry<K, V> nextEntryImpl(TrieEntry<K, V> start,
- TrieEntry<K, V> previous, TrieEntry<K, V> tree) {
-
- TrieEntry<K, V> current = start;
-
- // Only look at the left if this was a recursive or
- // the first check, otherwise we know we've already looked
- // at the left.
- if (previous == null || start != previous.predecessor) {
- while (!current.left.isEmpty()) {
- // stop traversing if we've already
- // returned the left of this node.
- if (previous == current.left) {
- break;
- }
-
- if (isValidUplink(current.left, current)) {
- return current.left;
- }
-
- current = current.left;
- }
- }
-
- // If there's no data at all, exit.
- if (current.isEmpty()) {
- return null;
- }
-
- // If we've already returned the left,
- // and the immediate right is null,
- // there's only one entry in the Trie
- // which is stored at the root.
- //
- // / ("") <-- root
- // \_/ \
- // null <-- 'current'
- //
- if (current.right == null) {
- return null;
- }
-
- // If nothing valid on the left, try the right.
- if (previous != current.right) {
- // See if it immediately is valid.
- if (isValidUplink(current.right, current)) {
- return current.right;
- }
-
- // Must search on the right's side if it wasn't initially valid.
- return nextEntryImpl(current.right, previous, tree);
- }
-
- // Neither left nor right are valid, find the first parent
- // whose child did not come from the right & traverse it.
- while (current == current.parent.right) {
- // If we're going to traverse to above the subtree, stop.
- if (current == tree) {
- return null;
- }
-
- current = current.parent;
- }
-
- // If we're on the top of the subtree, we can't go any higher.
- if (current == tree) {
- return null;
- }
-
- // If there's no right, the parent must be root, so we're done.
- if (current.parent.right == null) {
- return null;
- }
-
- // If the parent's right points to itself, we've found one.
- if (previous != current.parent.right
- && isValidUplink(current.parent.right, current.parent)) {
- return current.parent.right;
- }
-
- // If the parent's right is itself, there can't be any more nodes.
- if (current.parent.right == current.parent) {
- return null;
- }
-
- // We need to traverse down the parent's right's path.
- return nextEntryImpl(current.parent.right, previous, tree);
+ @Override
+ public Iterator<V> iterator() {
+ return new ValueIterator();
}
- /**
- * Returns the first entry the {@link Trie} is storing.
- *
- * This is implemented by going always to the left until
- * we encounter a valid uplink. That uplink is the first key.
- */
- TrieEntry<K, V> firstEntry() {
- // if Trie is empty, no first node.
- if (isEmpty()) {
- return null;
- }
-
- return followLeft(root);
+ @Override
+ public int size() {
+ return AbstractPatriciaTrie.this.size();
}
- /**
- * Goes left through the tree until it finds a valid node.
- */
- TrieEntry<K, V> followLeft(TrieEntry<K, V> node) {
- while(true) {
- TrieEntry<K, V> child = node.left;
- // if we hit root and it didn't have a node, go right instead.
- if (child.isEmpty()) {
- child = node.right;
- }
-
- if (child.bitIndex <= node.bitIndex) {
- return child;
- }
-
- node = child;
- }
+ @Override
+ public boolean contains(Object o) {
+ return containsValue(o);
}
- /**
- * Returns true if 'next' is a valid uplink coming from 'from'.
- */
- static boolean isValidUplink(TrieEntry<?, ?> next, TrieEntry<?, ?> from) {
- return next != null && next.bitIndex <= from.bitIndex && !next.isEmpty();
+ @Override
+ public void clear() {
+ AbstractPatriciaTrie.this.clear();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ for (Iterator<V> it = iterator(); it.hasNext(); ) {
+ V value = it.next();
+ if (Tries.areEqual(value, o)) {
+ it.remove();
+ return true;
+ }
+ }
+ return false;
}
/**
- * A {@link Reference} allows us to return something through a Method's
- * argument list. An alternative would be to an Array with a length of
- * one (1) but that leads to compiler warnings. Computationally and memory
- * wise there's no difference (except for the need to load the
- * {@link Reference} Class but that happens only once).
+ * An {@link Iterator} that returns Value Objects
*/
- private static class Reference<E> {
-
- private E item;
-
- public void set(E item) {
- this.item = item;
- }
-
- public E get() {
- return item;
- }
+ private class ValueIterator extends TrieIterator<V> {
+ @Override
+ public V next() {
+ return nextEntry().getValue();
+ }
}
+ }
+
+ /**
+ * An iterator for the entries.
+ */
+ abstract class TrieIterator<E> implements Iterator<E> {
/**
- * A {@link Trie} is a set of {@link TrieEntry} nodes
+ * For fast-fail
*/
- static class TrieEntry<K,V> extends BasicEntry<K, V> {
-
- private static final long serialVersionUID = 4596023148184140013L;
-
- /** The index this entry is comparing. */
- protected int bitIndex;
-
- /** The parent of this entry. */
- protected TrieEntry<K,V> parent;
-
- /** The left child of this entry. */
- protected TrieEntry<K,V> left;
-
- /** The right child of this entry. */
- protected TrieEntry<K,V> right;
-
- /** The entry who uplinks to this entry. */
- protected TrieEntry<K,V> predecessor;
-
- public TrieEntry(K key, V value, int bitIndex) {
- super(key, value);
-
- this.bitIndex = bitIndex;
-
- this.parent = null;
- this.left = this;
- this.right = null;
- this.predecessor = this;
- }
-
- /**
- * Whether or not the entry is storing a key.
- * Only the root can potentially be empty, all other
- * nodes must have a key.
- */
- public boolean isEmpty() {
- return key == null;
- }
-
- /**
- * Neither the left nor right child is a loopback
- */
- public boolean isInternalNode() {
- return left != this && right != this;
- }
-
- /**
- * Either the left or right child is a loopback
- */
- public boolean isExternalNode() {
- return !isInternalNode();
- }
-
- @Override
- public String toString() {
- StringBuilder buffer = new StringBuilder();
-
- if (bitIndex == -1) {
- buffer.append("RootEntry(");
- } else {
- buffer.append("Entry(");
- }
-
- buffer.append("key=").append(getKey()).append(" [").append(bitIndex).append("], ");
- buffer.append("value=").append(getValue()).append(", ");
- //buffer.append("bitIndex=").append(bitIndex).append(", ");
-
- if (parent != null) {
- if (parent.bitIndex == -1) {
- buffer.append("parent=").append("ROOT");
- } else {
- buffer.append("parent=").append(parent.getKey()).append(" [").append(parent.bitIndex).append("]");
- }
- } else {
- buffer.append("parent=").append("null");
- }
- buffer.append(", ");
-
- if (left != null) {
- if (left.bitIndex == -1) {
- buffer.append("left=").append("ROOT");
- } else {
- buffer.append("left=").append(left.getKey()).append(" [").append(left.bitIndex).append("]");
- }
- } else {
- buffer.append("left=").append("null");
- }
- buffer.append(", ");
-
- if (right != null) {
- if (right.bitIndex == -1) {
- buffer.append("right=").append("ROOT");
- } else {
- buffer.append("right=").append(right.getKey()).append(" [").append(right.bitIndex).append("]");
- }
- } else {
- buffer.append("right=").append("null");
- }
- buffer.append(", ");
-
- if (predecessor != null) {
- if(predecessor.bitIndex == -1) {
- buffer.append("predecessor=").append("ROOT");
- } else {
- buffer.append("predecessor=").append(predecessor.getKey()).append(" [").append(predecessor.bitIndex).append("]");
- }
- }
-
- buffer.append(")");
- return buffer.toString();
- }
- }
+ protected int expectedModCount = AbstractPatriciaTrie.this.modCount;
+
+ protected TrieEntry<K, V> next; // the next node to return
+ protected TrieEntry<K, V> current; // the current entry we're on
-
/**
- * This is a entry set view of the {@link Trie} as returned
- * by {@link Map#entrySet()}
+ * Starts iteration from the root
*/
- private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
-
- @Override
- public Iterator<Map.Entry<K,V>> iterator() {
- return new EntryIterator();
- }
-
- @Override
- public boolean contains(Object o) {
- if (!(o instanceof Map.Entry)) {
- return false;
- }
-
- TrieEntry<K,V> candidate = getEntry(((Map.Entry<?, ?>)o).getKey());
- return candidate != null && candidate.equals(o);
- }
-
- @Override
- public boolean remove(Object o) {
- int size = size();
- AbstractPatriciaTrie.this.remove(o);
- return size != size();
- }
-
- @Override
- public int size() {
- return AbstractPatriciaTrie.this.size();
- }
-
- @Override
- public void clear() {
- AbstractPatriciaTrie.this.clear();
- }
-
- /**
- * An {@link Iterator} that returns {@link Entry} Objects
- */
- private class EntryIterator extends TrieIterator<Map.Entry<K,V>> {
- @Override
- public Map.Entry<K,V> next() {
- return nextEntry();
- }
- }
+ protected TrieIterator() {
+ next = AbstractPatriciaTrie.this.nextEntry(null);
}
/**
- * This is a key set view of the {@link Trie} as returned
- * by {@link Map#keySet()}
+ * Starts iteration at the given entry
*/
- private class KeySet extends AbstractSet<K> {
-
- @Override
- public Iterator<K> iterator() {
- return new KeyIterator();
- }
-
- @Override
- public int size() {
- return AbstractPatriciaTrie.this.size();
- }
-
- @Override
- public boolean contains(Object o) {
- return containsKey(o);
- }
-
- @Override
- public boolean remove(Object o) {
- int size = size();
- AbstractPatriciaTrie.this.remove(o);
- return size != size();
- }
-
- @Override
- public void clear() {
- AbstractPatriciaTrie.this.clear();
- }
-
- /**
- * An {@link Iterator} that returns Key Objects
- */
- private class KeyIterator extends TrieIterator<K> {
- @Override
- public K next() {
- return nextEntry().getKey();
- }
- }
+ protected TrieIterator(TrieEntry<K, V> firstEntry) {
+ next = firstEntry;
}
/**
- * This is a value view of the {@link Trie} as returned
- * by {@link Map#values()}
+ * Returns the next {@link TrieEntry}
*/
- private class Values extends AbstractCollection<V> {
-
- @Override
- public Iterator<V> iterator() {
- return new ValueIterator();
- }
-
- @Override
- public int size() {
- return AbstractPatriciaTrie.this.size();
- }
-
- @Override
- public boolean contains(Object o) {
- return containsValue(o);
- }
-
- @Override
- public void clear() {
- AbstractPatriciaTrie.this.clear();
- }
-
- @Override
- public boolean remove(Object o) {
- for (Iterator<V> it = iterator(); it.hasNext(); ) {
- V value = it.next();
- if (Tries.areEqual(value, o)) {
- it.remove();
- return true;
- }
- }
- return false;
- }
-
- /**
- * An {@link Iterator} that returns Value Objects
- */
- private class ValueIterator extends TrieIterator<V> {
- @Override
- public V next() {
- return nextEntry().getValue();
- }
- }
+ protected TrieEntry<K,V> nextEntry() {
+ if (expectedModCount != AbstractPatriciaTrie.this.modCount) {
+ throw new ConcurrentModificationException();
+ }
+
+ TrieEntry<K,V> e = next;
+ if (e == null) {
+ throw new NoSuchElementException();
+ }
+
+ next = findNext(e);
+ current = e;
+ return e;
}
- /**
- * An iterator for the entries.
+ /**
+ * @see PatriciaTrie#nextEntry(TrieEntry)
*/
- abstract class TrieIterator<E> implements Iterator<E> {
-
- /**
- * For fast-fail
- */
- protected int expectedModCount = AbstractPatriciaTrie.this.modCount;
-
- protected TrieEntry<K, V> next; // the next node to return
- protected TrieEntry<K, V> current; // the current entry we're on
-
- /**
- * Starts iteration from the root
- */
- protected TrieIterator() {
- next = AbstractPatriciaTrie.this.nextEntry(null);
- }
-
- /**
- * Starts iteration at the given entry
- */
- protected TrieIterator(TrieEntry<K, V> firstEntry) {
- next = firstEntry;
- }
-
- /**
- * Returns the next {@link TrieEntry}
- */
- protected TrieEntry<K,V> nextEntry() {
- if (expectedModCount != AbstractPatriciaTrie.this.modCount) {
- throw new ConcurrentModificationException();
- }
-
- TrieEntry<K,V> e = next;
- if (e == null) {
- throw new NoSuchElementException();
- }
-
- next = findNext(e);
- current = e;
- return e;
- }
-
- /**
- * @see PatriciaTrie#nextEntry(TrieEntry)
- */
- protected TrieEntry<K, V> findNext(TrieEntry<K, V> prior) {
- return AbstractPatriciaTrie.this.nextEntry(prior);
- }
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public void remove() {
- if (current == null) {
- throw new IllegalStateException();
- }
-
- if (expectedModCount != AbstractPatriciaTrie.this.modCount) {
- throw new ConcurrentModificationException();
- }
-
- TrieEntry<K, V> node = current;
- current = null;
- AbstractPatriciaTrie.this.removeEntry(node);
-
- expectedModCount = AbstractPatriciaTrie.this.modCount;
- }
+ protected TrieEntry<K, V> findNext(TrieEntry<K, V> prior) {
+ return AbstractPatriciaTrie.this.nextEntry(prior);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public void remove() {
+ if (current == null) {
+ throw new IllegalStateException();
+ }
+
+ if (expectedModCount != AbstractPatriciaTrie.this.modCount) {
+ throw new ConcurrentModificationException();
+ }
+
+ TrieEntry<K, V> node = current;
+ current = null;
+ AbstractPatriciaTrie.this.removeEntry(node);
+
+ expectedModCount = AbstractPatriciaTrie.this.modCount;
}
+ }
}
View
342 src/main/java/org/ardverk/collection/AbstractTrie.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -25,200 +25,200 @@
* utility methods for actual {@link Trie} implementations.
*/
abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
- implements Serializable, Trie<K, V> {
+ implements Serializable, Trie<K, V> {
+
+ private static final long serialVersionUID = -6358111100045408883L;
+
+ /**
+ * The {@link KeyAnalyzer} that's being used to build the
+ * PATRICIA {@link Trie}
+ */
+ protected final KeyAnalyzer<? super K> keyAnalyzer;
+
+ public AbstractTrie() {
+ this(DefaultKeyAnalyzer.singleton());
+ }
+
+ /**
+ * Constructs a new {@link Trie} using the given {@link KeyAnalyzer}
+ */
+ public AbstractTrie(KeyAnalyzer<? super K> keyAnalyzer) {
+ this.keyAnalyzer = Tries.notNull(keyAnalyzer, "keyAnalyzer");
+ }
+
+ /**
+ * Returns the {@link KeyAnalyzer} that constructed the {@link Trie}.
+ */
+ public KeyAnalyzer<? super K> getKeyAnalyzer() {
+ return keyAnalyzer;
+ }
+
+ @Override
+ public K selectKey(K key) {
+ Map.Entry<K, V> entry = select(key);
+ return entry != null ? entry.getKey() : null;
+ }
+
+ @Override
+ public V selectValue(K key) {
+ Map.Entry<K, V> entry = select(key);
+ return entry != null ? entry.getValue() : null;
+ }
- private static final long serialVersionUID = -6358111100045408883L;
-
- /**
- * The {@link KeyAnalyzer} that's being used to build the
- * PATRICIA {@link Trie}
- */
- protected final KeyAnalyzer<? super K> keyAnalyzer;
-
- public AbstractTrie() {
- this(DefaultKeyAnalyzer.singleton());
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("Trie[").append(size()).append("]={\n");
+ for (Map.Entry<K, V> entry : entrySet()) {
+ buffer.append(" ").append(entry).append("\n");
}
-
- /**
- * Constructs a new {@link Trie} using the given {@link KeyAnalyzer}
- */
- public AbstractTrie(KeyAnalyzer<? super K> keyAnalyzer) {
- this.keyAnalyzer = Tries.notNull(keyAnalyzer, "keyAnalyzer");
+ buffer.append("}\n");
+ return buffer.toString();
+ }
+
+ /**
+ * Returns the length of the given key in bits
+ *
+ * @see KeyAnalyzer#lengthInBits(Object)
+ */
+ final int lengthInBits(K key) {
+ if (key == null) {
+ return 0;
}
- /**
- * Returns the {@link KeyAnalyzer} that constructed the {@link Trie}.
- */
- public KeyAnalyzer<? super K> getKeyAnalyzer() {
- return keyAnalyzer;
+ return keyAnalyzer.lengthInBits(key);
+ }
+
+ /**
+ * Returns whether or not the given bit on the
+ * key is set or false if the key is null.
+ *
+ * @see KeyAnalyzer#isBitSet(Object, int)
+ */
+ final boolean isBitSet(K key, int bitIndex) {
+ if (key == null) { // root's might be null!
+ return false;
+ }
+ return keyAnalyzer.isBitSet(key, bitIndex);
+ }
+
+ /**
+ * Utility method for calling {@link KeyAnalyzer#bitIndex(Object, Object)}
+ */
+ final int bitIndex(K key, K otherKey) {
+ if (key != null && otherKey != null) {
+ return keyAnalyzer.bitIndex(key, otherKey);
+ } else if (key != null && otherKey == null) {
+ return bitIndex(key);
+ } else if (key == null && otherKey != null) {
+ return bitIndex(otherKey);
}
- @Override
- public K selectKey(K key) {
- Map.Entry<K, V> entry = select(key);
- return entry != null ? entry.getKey() : null;
+ return KeyAnalyzer.NULL_BIT_KEY;
+ }
+
+ private int bitIndex(K key) {
+ int lengthInBits = lengthInBits(key);
+ for (int i = 0; i < lengthInBits; i++) {
+ if (isBitSet(key, i)) {
+ return i;
+ }
}
- @Override
- public V selectValue(K key) {
- Map.Entry<K, V> entry = select(key);
- return entry != null ? entry.getValue() : null;
+ return KeyAnalyzer.NULL_BIT_KEY;
+ }
+
+ /**
+ * An utility method for calling {@link KeyAnalyzer#compare(Object, Object)}
+ */
+ final boolean compareKeys(K key, K other) {
+ if (key == null) {
+ return (other == null);
+ } else if (other == null) {
+ return (key == null);
}
-
- @Override
- public String toString() {
- StringBuilder buffer = new StringBuilder();
- buffer.append("Trie[").append(size()).append("]={\n");
- for (Map.Entry<K, V> entry : entrySet()) {
- buffer.append(" ").append(entry).append("\n");
- }
- buffer.append("}\n");
- return buffer.toString();
+
+ return keyAnalyzer.compare(key, other) == 0;
+ }
+
+ /**
+ * A basic implementation of {@link Entry}
+ */
+ abstract static class BasicEntry<K, V> implements Map.Entry<K, V>, Serializable {
+
+ private static final long serialVersionUID = -944364551314110330L;
+
+ protected K key;
+
+ protected V value;
+
+ private transient int hashCode = 0;
+
+ public BasicEntry(K key) {
+ this.key = key;
}
- /**
- * Returns the length of the given key in bits
- *
- * @see KeyAnalyzer#lengthInBits(Object)
- */
- final int lengthInBits(K key) {
- if (key == null) {
- return 0;
- }
-
- return keyAnalyzer.lengthInBits(key);
+ public BasicEntry(K key, V value) {
+ this.key = key;
+ this.value = value;
}
/**
- * Returns whether or not the given bit on the
- * key is set or false if the key is null.
- *
- * @see KeyAnalyzer#isBitSet(Object, int)
+ * Replaces the current key and value with the provided
+ * key &amp; value
*/
- final boolean isBitSet(K key, int bitIndex) {
- if (key == null) { // root's might be null!
- return false;
- }
- return keyAnalyzer.isBitSet(key, bitIndex);
+ public V setKeyValue(K key, V value) {
+ this.key = key;
+ this.hashCode = 0;
+ return setValue(value);
}
- /**
- * Utility method for calling {@link KeyAnalyzer#bitIndex(Object, Object)}
- */
- final int bitIndex(K key, K otherKey) {
- if (key != null && otherKey != null) {
- return keyAnalyzer.bitIndex(key, otherKey);
- } else if (key != null && otherKey == null) {
- return bitIndex(key);
- } else if (key == null && otherKey != null) {
- return bitIndex(otherKey);
- }
-
- return KeyAnalyzer.NULL_BIT_KEY;
+ @Override
+ public K getKey() {
+ return key;
}
- private int bitIndex(K key) {
- int lengthInBits = lengthInBits(key);
- for (int i = 0; i < lengthInBits; i++) {
- if (isBitSet(key, i)) {
- return i;
- }
- }
-
- return KeyAnalyzer.NULL_BIT_KEY;
+ @Override
+ public V getValue() {
+ return value;
}
- /**
- * An utility method for calling {@link KeyAnalyzer#compare(Object, Object)}
- */
- final boolean compareKeys(K key, K other) {
- if (key == null) {
- return (other == null);
- } else if (other == null) {
- return (key == null);
- }
-
- return keyAnalyzer.compare(key, other) == 0;
+ @Override
+ public V setValue(V value) {
+ V previous = this.value;
+ this.value = value;
+ return previous;
}