Further cleanup of trie package & interface, renamed AbstractTrie to AbstractBitwiseTrie, added TODOs.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1491621 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2013-06-10 22:05:38 +00:00
parent 467d516b9c
commit 81e47470cc
7 changed files with 118 additions and 174 deletions

View File

@ -27,93 +27,12 @@ import java.util.Map.Entry;
* @since 4.0
* @version $Id$
*/
// TODO: should extend IterableSortedMap
// TODO: move bitwise getPrefixedBy methods to AbstractBitwiseTrie
// TODO: consider a BitwiseTrie interface which extends Trie and supports the bitwise selection methods
// TODO: consider a better name for getPrefixedBy: maybe prefixMap(...)
public interface Trie<K, V> extends SortedMap<K, V> {
/**
* Returns the {@link Entry} whose key is closest in a bitwise XOR
* metric to the given key. This is NOT lexicographic closeness.
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the {@link Entry} whose key is closest in a bitwise XOR metric
* to the provided key
*/
public Map.Entry<K, V> select(K key);
/**
* Returns the key that is closest in a bitwise XOR metric to the
* provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the key that is closest in a bitwise XOR metric to the provided key
*/
public K selectKey(K key);
/**
* Returns the value whose key is closest in a bitwise XOR metric to
* the provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the value whose key is closest in a bitwise XOR metric
* to the provided key
*/
public V selectValue(K key);
/**
* Iterates through the {@link Trie}, starting with the entry whose bitwise
* value is closest in an XOR metric to the given key. After the closest
* entry is found, the {@link Trie} will call select on that entry and continue
* calling select for each entry (traversing in order of XOR closeness,
* NOT lexicographically) until the cursor returns {@link Cursor.Decision#EXIT}.
* <p>
* The cursor can return {@link Cursor.Decision#CONTINUE} to continue traversing.
* <p>
* {@link Cursor.Decision#REMOVE_AND_EXIT} is used to remove the current element
* and stop traversing.
* <p>
* Note: The {@link Cursor.Decision#REMOVE} operation is not supported.
*
* @param key the key to use in the search
* @param cursor the cursor used throughout the search
* @return the entry the cursor returned {@link Cursor.Decision#EXIT} on, or null
* if it continued till the end
*/
public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor);
/**
* Traverses the {@link Trie} in lexicographical order.
* {@link Cursor#select(java.util.Map.Entry)} will be called on each entry.

View File

@ -19,17 +19,18 @@ package org.apache.commons.collections4.trie;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.collections4.Trie;
/**
* This class provides some basic {@link Trie} functionality and
* utility methods for actual {@link Trie} implementations.
* utility methods for actual bitwise {@link Trie} implementations.
*
* @since 4.0
* @version $Id$
*/
abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
abstract class AbstractBitwiseTrie<K, V> extends AbstractMap<K, V>
implements Trie<K, V>, Serializable {
private static final long serialVersionUID = 5826987063535505652L;
@ -37,15 +38,14 @@ abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
// TODO Privatise fields?
/**
* The {@link KeyAnalyzer} that's being used to build the
* PATRICIA {@link Trie}.
* The {@link KeyAnalyzer} that's being used to build the PATRICIA {@link Trie}.
*/
protected final KeyAnalyzer<? super K> keyAnalyzer;
/**
* Constructs a new {@link Trie} using the given {@link KeyAnalyzer}.
*/
public AbstractTrie(final KeyAnalyzer<? super K> keyAnalyzer) {
public AbstractBitwiseTrie(final KeyAnalyzer<? super K> keyAnalyzer) {
if (keyAnalyzer == null) {
throw new NullPointerException("keyAnalyzer");
}
@ -60,6 +60,46 @@ abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
return keyAnalyzer;
}
/**
* Returns the {@link Entry} whose key is closest in a bitwise XOR
* metric to the given key. This is NOT lexicographic closeness.
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the {@link Entry} whose key is closest in a bitwise XOR metric
* to the provided key
*/
public abstract Map.Entry<K, V> select(K key);
/**
* Returns the key that is closest in a bitwise XOR metric to the
* provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the key that is closest in a bitwise XOR metric to the provided key
*/
public K selectKey(final K key) {
final Map.Entry<K, V> entry = select(key);
if (entry == null) {
@ -68,6 +108,26 @@ abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
return entry.getKey();
}
/**
* Returns the value whose key is closest in a bitwise XOR metric to
* the provided key. This is NOT lexicographic closeness!
*
* For example, given the keys:
*
* <ol>
* <li>D = 1000100
* <li>H = 1001000
* <li>L = 1001100
* </ol>
*
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
* return 'L', because the XOR distance between D &amp; L is smaller
* than the XOR distance between D &amp; H.
*
* @param key the key to use in the search
* @return the value whose key is closest in a bitwise XOR metric
* to the provided key
*/
public V selectValue(final K key) {
final Map.Entry<K, V> entry = select(key);
if (entry == null) {
@ -76,6 +136,27 @@ abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
return entry.getValue();
}
/**
* Iterates through the {@link Trie}, starting with the entry whose bitwise
* value is closest in an XOR metric to the given key. After the closest
* entry is found, the {@link Trie} will call select on that entry and continue
* calling select for each entry (traversing in order of XOR closeness,
* NOT lexicographically) until the cursor returns {@link Cursor.Decision#EXIT}.
* <p>
* The cursor can return {@link Cursor.Decision#CONTINUE} to continue traversing.
* <p>
* {@link Cursor.Decision#REMOVE_AND_EXIT} is used to remove the current element
* and stop traversing.
* <p>
* Note: The {@link Cursor.Decision#REMOVE} operation is not supported.
*
* @param key the key to use in the search
* @param cursor the cursor used throughout the search
* @return the entry the cursor returned {@link Cursor.Decision#EXIT} on, or null
* if it continued till the end
*/
public abstract Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor);
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();

View File

@ -21,8 +21,8 @@ import java.util.Comparator;
/**
* Defines the interface to analyze {@link org.apache.commons.collections4.Trie Trie} keys on a bit level.
* {@link KeyAnalyzer}'s methods return the length of the key in bits,
* whether or not a bit is set, and bits per element in the key.
* {@link KeyAnalyzer}'s methods return the length of the key in bits, whether or not a bit is set,
* and bits per element in the key.
* <p>
* Additionally, a method determines if a key is a prefix of another
* key and returns the bit index where one key is different from another
@ -39,44 +39,42 @@ public abstract class KeyAnalyzer<K> implements Comparator<K>, Serializable {
/**
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
* if key's bits are all 0
* if key's bits are all 0.
*/
public static final int NULL_BIT_KEY = -1;
/**
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
* if key and found key are equal. This is a very very specific case
* and shouldn't happen on a regular basis
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)} if key and found key are equal.
* This is a very very specific case and shouldn't happen on a regular basis.
*/
public static final int EQUAL_BIT_KEY = -2;
public static final int OUT_OF_BOUNDS_BIT_KEY = -3;
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#OUT_OF_BOUNDS_BIT_KEY}
* Returns true if bitIndex is a {@link KeyAnalyzer#OUT_OF_BOUNDS_BIT_KEY}.
*/
static boolean isOutOfBoundsIndex(final int bitIndex) {
return bitIndex == OUT_OF_BOUNDS_BIT_KEY;
}
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#EQUAL_BIT_KEY}
* Returns true if bitIndex is a {@link KeyAnalyzer#EQUAL_BIT_KEY}.
*/
static boolean isEqualBitKey(final int bitIndex) {
return bitIndex == EQUAL_BIT_KEY;
}
/**
* Returns true if bitIndex is a {@link KeyAnalyzer#NULL_BIT_KEY}
* Returns true if bitIndex is a {@link KeyAnalyzer#NULL_BIT_KEY}.
*/
static boolean isNullBitKey(final int bitIndex) {
return bitIndex == NULL_BIT_KEY;
}
/**
* Returns true if the given bitIndex is valid. Indices
* are considered valid if they're between 0 and
* {@link Integer#MAX_VALUE}
* Returns true if the given bitIndex is valid.
* Indices are considered valid if they're between 0 and {@link Integer#MAX_VALUE}
*/
static boolean isValidBitIndex(final int bitIndex) {
return bitIndex >= 0;

View File

@ -29,11 +29,10 @@ import java.util.SortedMap;
import org.apache.commons.collections4.Trie;
/**
* <h3>PATRICIA {@link Trie}</h3>
*
* <i>Practical Algorithm to Retrieve Information Coded in Alphanumeric</i>
*
* <p>A PATRICIA {@link Trie} is a compressed {@link Trie}. Instead of storing
* Implementation of a PATRICIA Trie (Practical Algorithm to Retrieve Information
* Coded in Alphanumeric).
* <p>
* A PATRICIA {@link Trie} is a compressed {@link Trie}. Instead of storing
* all data at the edges of the {@link Trie} (and having empty internal nodes),
* PATRICIA stores data in every node. This allows for very efficient traversal,
* insert, delete, predecessor, successor, prefix, range, and {@link #select(Object)}
@ -41,25 +40,25 @@ import org.apache.commons.collections4.Trie;
* is the number of bits in the largest item in the tree. In practice,
* operations actually take O(A(K)) time, where A(K) is the average number of
* bits of all items in the tree.
*
* <p>Most importantly, PATRICIA requires very few comparisons to keys while
* <p>
* Most importantly, PATRICIA requires very few comparisons to keys while
* doing any operation. While performing a lookup, each comparison (at most
* K of them, described above) will perform a single bit comparison against
* the given key, instead of comparing the entire key to another key.
*
* <p>The {@link Trie} can return operations in lexicographical order using the
* <p>
* The {@link Trie} can return operations in lexicographical order using the
* {@link #traverse(Cursor)}, 'prefix', 'submap', or 'iterator' methods. The
* {@link Trie} can also scan for items that are 'bitwise' (using an XOR
* metric) by the 'select' method. Bitwise closeness is determined by the
* {@link KeyAnalyzer} returning true or false for a bit being set or not in
* a given key.
*
* <p>This PATRICIA {@link Trie} supports both variable length & fixed length
* <p>
* This PATRICIA {@link Trie} supports both variable length & fixed length
* keys. Some methods, such as {@link #getPrefixedBy(Object)} are suited only
* to variable length keys, whereas {@link #getPrefixedByBits(Object, int)} is
* suited to fixed-size keys.
*
* <p>Any methods here that take an {@link Object} argument may throw a
* <p>
* Any methods here that take an {@link Object} argument may throw a
* {@link ClassCastException} if the method is expecting an instance of K
* and it isn't K.
*

View File

@ -34,7 +34,7 @@ import org.apache.commons.collections4.Trie.Cursor.Decision;
* @since 4.0
* @version $Id$
*/
abstract class PatriciaTrieBase<K, V> extends AbstractTrie<K, V> {
abstract class PatriciaTrieBase<K, V> extends AbstractBitwiseTrie<K, V> {
private static final long serialVersionUID = 5155253417231339498L;

View File

@ -66,22 +66,6 @@ public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
this.delegate = trie;
}
public synchronized Entry<K, V> select(final K key, final Cursor<? super K, ? super V> cursor) {
return delegate.select(key, cursor);
}
public synchronized Entry<K, V> select(final K key) {
return delegate.select(key);
}
public synchronized K selectKey(final K key) {
return delegate.selectKey(key);
}
public synchronized V selectValue(final K key) {
return delegate.selectValue(key);
}
public synchronized Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
return delegate.traverse(cursor);
}

View File

@ -66,38 +66,6 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
this.delegate = trie;
}
public Entry<K, V> select(final K key, final Cursor<? super K, ? super V> cursor) {
final Cursor<K, V> c = new Cursor<K, V>() {
public Decision select(final Map.Entry<? extends K, ? extends V> entry) {
final Decision decision = cursor.select(entry);
switch (decision) {
case REMOVE:
case REMOVE_AND_EXIT:
throw new UnsupportedOperationException();
default:
// other decisions are fine
break;
}
return decision;
}
};
return delegate.select(key, c);
}
public Entry<K, V> select(final K key) {
return delegate.select(key);
}
public K selectKey(final K key) {
return delegate.selectKey(key);
}
public V selectValue(final K key) {
return delegate.selectValue(key);
}
public Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
final Cursor<K, V> c = new Cursor<K, V>() {
public Decision select(final Map.Entry<? extends K, ? extends V> entry) {
@ -175,8 +143,7 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
}
public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
return Collections.unmodifiableSortedMap(
delegate.subMap(fromKey, toKey));
return Collections.unmodifiableSortedMap(delegate.subMap(fromKey, toKey));
}
public SortedMap<K, V> tailMap(final K fromKey) {
@ -184,23 +151,19 @@ public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable, Unmodif
}
public SortedMap<K, V> getPrefixedBy(final K key, final int offset, final int length) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key, offset, length));
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key, offset, length));
}
public SortedMap<K, V> getPrefixedBy(final K key, final int length) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key, length));
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key, length));
}
public SortedMap<K, V> getPrefixedBy(final K key) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedBy(key));
return Collections.unmodifiableSortedMap(delegate.getPrefixedBy(key));
}
public SortedMap<K, V> getPrefixedByBits(final K key, final int lengthInBits) {
return Collections.unmodifiableSortedMap(
delegate.getPrefixedByBits(key, lengthInBits));
return Collections.unmodifiableSortedMap(delegate.getPrefixedByBits(key, lengthInBits));
}
public SortedMap<K, V> getPrefixedByBits(final K key, final int offsetInBits, final int lengthInBits) {