- Memory efficient multivaluemap
- int getPackedInt(byte[] array, int index) {
- int i = index*3;
- return ((array[i] & 0xFF)<<16) + ((array[i+1] & 0xFF) <<8) + (array[i+2] & 0xFF);
- }
- int storePackedInt(byte[] array, int index, int value) {
- assert value >= 0 && value <= 0xFFFFFF;
- int i = index*3;
- array[i] = (byte)((value>>16) & 0xFF);
- array[i+1] = (byte)((value>>8) & 0xFF);
- array[i+2] = (byte)(value & 0xFF);
- }
- ListMultimap<String, Integer> multimap = Multimaps.newListMultimap(
- new HashMap<String, Collection<Integer>>(),
- new Supplier<List<Integer>>() {
- public List<Integer> get() {
- return new TIntListDecorator();
- }
- });
- /**
- * Implementation of a Trie structure.
- *
- * A Trie is a compact form of tree that takes advantage of common prefixes
- * to the keys.
- *
- * A normal HashSet will take the key and compute a hash from it, this hash will
- * be used to locate the value through various methods but usually some kind
- * of bucket system is used. The memory footprint resulting becomes something
- * like O(n).
- *
- * A Trie structure essentuially combines all common prefixes into a single key.
- * For example, holding the strings A, AB, ABC and ABCD will only take enough
- * space to record the presence of ABCD. The presence of the others will be
- * recorded as flags within the record of ABCD structure at zero cost.
- *
- * This structure is useful for holding similar strings such as product IDs or
- * credit card numbers.
- *
- */
- public class TrieMap<V> extends AbstractMap<String, V> implements Map<String, V> {
- /**
- * Map each character to a sub-trie.
- *
- * Could replace this with a 256 entry array of Tries but this will handle
- * multibyte character sets and I can discard empty maps.
- *
- * Maintained at null until needed (for better memory footprint).
- *
- */
- protected Map<Character, TrieMap<V>> children = null;
- /**
- * Here we store the map contents.
- */
- protected V leaf = null;
- /**
- * Set the leaf value to a new setting and return the old one.
- *
- * @param newValue
- * @return old value of leaf.
- */
- protected V setLeaf(V newValue) {
- V old = leaf;
- leaf = newValue;
- return old;
- }
- /**
- * I've always wanted to name a method something like this.
- */
- protected void makeChildren () {
- if ( children == null ) {
- // Use a TreeMap to ensure sorted iteration.
- children = new TreeMap<Character, TrieMap<V>>();
- }
- }
- /**
- * Finds the TrieMap that "should" contain the key.
- *
- * @param key
- *
- * The key to find.
- *
- * @param grow
- *
- * Set to true to grow the Trie to fit the key.
- *
- * @return
- *
- * The sub Trie that "should" contain the key or null if key was not found and
- * grow was false.
- */
- protected TrieMap<V> find(String key, boolean grow) {
- if (key.length() == 0) {
- // Found it!
- return this;
- } else {
- // Not at end of string.
- if (grow) {
- // Grow the tree.
- makeChildren();
- }
- if (children != null) {
- // Ask the kids.
- char ch = key.charAt(0);
- TrieMap<V> child = children.get(ch);
- if (child == null && grow) {
- // Make the child.
- child = new TrieMap<V>();
- // Store the child.
- children.put(ch, child);
- }
- if (child != null) {
- // Find it in the child.
- return child.find(tail(key), grow);
- }
- }
- }
- return null;
- }
- /**
- * Remove the head (first character) from the string.
- *
- * @param s
- *
- * The string.
- *
- * @return
- *
- * The same string without the first (head) character.
- *
- */
- // Suppress warnings over taking a subsequence
- private String tail(String s) {
- return s.substring(1, s.length());
- }
- /**
- *
- * Add a new value to the map.
- *
- * Time footprint = O(s.length).
- *
- * @param s
- *
- * The key defining the place to add.
- *
- * @param value
- *
- * The value to add there.
- *
- * @return
- *
- * The value that was there, or null if it wasn't.
- *
- */
- @Override
- public V put(String key, V value) {
- V old = null;
- // If empty string.
- if (key.length() == 0) {
- old = setLeaf(value);
- } else {
- // Find it.
- old = find(key, true).put("", value);
- }
- return old;
- }
- /**
- * Gets the value at the specified key position.
- *
- * @param o
- *
- * The key to the location.
- *
- * @return
- *
- * The value at that location, or null if there is no value at that location.
- */
- @Override
- public V get(Object o) {
- V got = null;
- if (o != null) {
- String key = (String) o;
- TrieMap<V> it = find(key, false);
- if (it != null) {
- got = it.leaf;
- }
- } else {
- throw new NullPointerException("Nulls not allowed.");
- }
- return got;
- }
- /**
- * Remove the value at the specified location.
- *
- * @param o
- *
- * The key to the location.
- *
- * @return
- *
- * The value that was removed, or null if there was no value at that location.
- */
- @Override
- public V remove(Object o) {
- V old = null;
- if (o != null) {
- String key = (String) o;
- if (key.length() == 0) {
- // Its me!
- old = leaf;
- leaf = null;
- } else {
- TrieMap<V> it = find(key, false);
- if (it != null) {
- old = it.remove("");
- }
- }
- } else {
- throw new NullPointerException("Nulls not allowed.");
- }
- return old;
- }
- /**
- * Count the number of values in the structure.
- *
- * @return
- *
- * The number of values in the structure.
- */
- @Override
- public int size() {
- // If I am a leaf then size increases by 1.
- int size = leaf != null ? 1 : 0;
- if (children != null) {
- // Add sizes of all my children.
- for (Character c : children.keySet()) {
- size += children.get(c).size();
- }
- }
- return size;
- }
- /**
- * Is the tree empty?
- *
- * @return
- *
- * true if the tree is empty.
- * false if there is still at least one value in the tree.
- */
- @Override
- public boolean isEmpty() {
- // I am empty if I am not a leaf and I have no children
- // (slightly quicker than the AbstaractCollection implementation).
- return leaf == null && (children == null || children.isEmpty());
- }
- /**
- * Returns all keys as a Set.
- *
- * @return
- *
- * A HashSet of all keys.
- *
- * Note: Although it returns Set<S> it is actually a Set<String> that has been
- * home-grown because the original keys are not stored in the structure
- * anywhere.
- */
- @Override
- public Set<String> keySet() {
- // Roll them a temporary list and give them a Set from it.
- return new HashSet<String>(keyList());
- }
- /**
- * List all my keys.
- *
- * @return
- *
- * An ArrayList of all keys in the tree.
- *
- * Note: Although it returns List<S> it is actually a List<String> that has been
- * home-grown because the original keys are not stored in the structure
- * anywhere.
- *
- */
- protected List<String> keyList() {
- List<String> contents = new ArrayList<String>();
- if (leaf != null) {
- // If I am a leaf, a null string is in the set.
- contents.add((String) "");
- }
- // Add all sub-tries.
- if (children != null) {
- for (Character c : children.keySet()) {
- TrieMap<V> child = children.get(c);
- List<String> childContents = child.keyList();
- for (String subString : childContents) {
- // All possible substrings can be prepended with this character.
- contents.add((String) (c + subString.toString()));
- }
- }
- }
- return contents;
- }
- /**
- * Does the map contain the specified key.
- *
- * @param key
- *
- * The key to look for.
- *
- * @return
- *
- * true if the key is in the Map.
- * false if not.
- */
- public boolean containsKey(String key) {
- TrieMap<V> it = find(key, false);
- if (it != null) {
- return it.leaf != null;
- }
- return false;
- }
- /**
- * Represent me as a list.
- *
- * @return
- *
- * A String representation of the tree.
- */
- @Override
- public String toString() {
- List<String> list = keyList();
- //Collections.sort((List<String>)list);
- StringBuilder sb = new StringBuilder();
- Separator comma = new Separator(",");
- sb.append("{");
- for (String s : list) {
- sb.append(comma.sep()).append(s).append("=").append(get(s));
- }
- sb.append("}");
- return sb.toString();
- }
- /**
- * Clear down completely.
- */
- @Override
- public void clear() {
- children = null;
- leaf = null;
- }
- /**
- * Return a list of key/value pairs.
- *
- * @return
- *
- * The entry set.
- */
- public Set<Map.Entry<String, V>> entrySet() {
- Set<Map.Entry<String, V>> entries = new HashSet<Map.Entry<String, V>>();
- List<String> keys = keyList();
- for (String key : keys) {
- entries.add(new Entry<String,V>(key, get(key)));
- }
- return entries;
- }
- /**
- * An entry.
- *
- * @param <S>
- *
- * The type of the key.
- *
- * @param <V>
- *
- * The type of the value.
- */
- private static class Entry<S, V> implements Map.Entry<S, V> {
- protected S key;
- protected V value;
- public Entry(S key, V value) {
- this.key = key;
- this.value = value;
- }
- public S getKey() {
- return key;
- }
- public V getValue() {
- return value;
- }
- public V setValue(V newValue) {
- V oldValue = value;
- value = newValue;
- return oldValue;
- }
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof TrieMap.Entry)) {
- return false;
- }
- Entry e = (Entry) o;
- return (key == null ? e.getKey() == null : key.equals(e.getKey()))
- && (value == null ? e.getValue() == null : value.equals(e.getValue()));
- }
- @Override
- public int hashCode() {
- int keyHash = (key == null ? 0 : key.hashCode());
- int valueHash = (value == null ? 0 : value.hashCode());
- return keyHash ^ valueHash;
- }
- @Override
- public String toString() {
- return key + "=" + value;
- }
- }
- }