package com.jwetherell.algorithms.data_structures; import com.jwetherell.algorithms.data_structures.Trie.Node; import com.jwetherell.algorithms.data_structures.interfaces.IMap; /** * A trie used to store key->values pairs, this is an implementation of an * associative array. * * This implementation is a composition using a Trie as the backing structure. * * http://en.wikipedia.org/wiki/Trie * http://en.wikipedia.org/wiki/Associative_array * * @author Justin Wetherell */ @SuppressWarnings("unchecked") public class TrieMap implements Trie.INodeCreator, IMap { private Trie trie = null; public TrieMap() { trie = new Trie(this); } /** * {@inheritDoc} */ @Override public V put(K key, V value) { V prev = null; Trie.Node node = trie.addSequence(key); if (node!=null && node instanceof TrieMapNode) { TrieMapNode trieMapNode = (TrieMapNode) node; if (trieMapNode.value!=null) prev = trieMapNode.value; trieMapNode.value = value; } return prev; } /** * {@inheritDoc} */ @Override public V get(K key) { Trie.Node node = trie.getNode(key); if (node!=null && node instanceof TrieMapNode) { TrieMapNode mapNode = (TrieMapNode) node; return mapNode.value; } return null; } /** * {@inheritDoc} */ @Override public boolean contains(K key) { return trie.contains(key); } /** * {@inheritDoc} */ @Override public V remove(K key) { Trie.Node node = trie.getNode(key); V value = null; if (node!=null) { if (node instanceof TrieMapNode) { TrieMapNode tmn = (TrieMapNode)node; value = tmn.value; tmn.value = null; } trie.remove(node); } return value; } /** * {@inheritDoc} */ @Override public void clear() { trie.clear(); } /** * {@inheritDoc} */ @Override public int size() { return trie.size(); } /** * {@inheritDoc} */ @Override public boolean validate() { java.util.Set keys = new java.util.HashSet(); Trie.Node node = trie.root; if (node!=null && !validate(node,"",keys)) return false; return (keys.size()==size()); } private boolean validate(Trie.Node node, String string, java.util.Set keys) { if (!(node instanceof TrieMapNode)) return false; TrieMapNode tmn = (TrieMapNode)node; StringBuilder builder = new StringBuilder(string); builder.append(tmn.character); String s = builder.toString(); if (tmn.isWord) { K k = (K)s; V v = tmn.value; if (k==null || v==null) return false; if (keys.contains(k)) return false; keys.add(k); } for (int i=0; i toMap() { return (new JavaCompatibleTrieMap(this)); } /** * {@inheritDoc} */ @Override public String toString() { return TrieMapPrinter.getString(this); } /** * {@inheritDoc} */ @Override public Trie.Node createNewNode(Trie.Node parent, Character character, boolean type) { return (new TrieMapNode(parent, character, type)); } protected static class TrieMapNode extends Trie.Node { protected V value = null; protected TrieMapNode(Trie.Node parent, Character character, boolean type) { super(parent, character, type); } protected TrieMapNode(Trie.Node parent, Character character, boolean type, V value) { super(parent, character, type); this.value = value; } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder builder = new StringBuilder(); if (value != null) builder.append("key=").append(character).append(" value=").append(value).append("\n"); for (int i = 0; i < getChildrenSize(); i++) { Trie.Node c = getChild(i); builder.append(c.toString()); } return builder.toString(); } } protected static class TrieMapPrinter { public static String getString(TrieMap map) { return getString(map.trie.root, "", null, true); } protected static String getString(Trie.Node node, String prefix, String previousString, boolean isTail) { StringBuilder builder = new StringBuilder(); String string = null; if (node.character != Node.SENTINAL) { String temp = String.valueOf(node.character); if (previousString != null) { string = previousString + temp; } else { string = temp; } } if (node instanceof TrieMapNode) { TrieMapNode hashNode = (TrieMapNode) node; builder.append(prefix + (isTail ? "└── " : "├── ") + ((node.isWord) ? ("(" + String.valueOf(node.character) + ") " + string + " = {" + hashNode.value + "}") : string) + "\n"); } if (node.getChildrenSize() > 0) { for (int i = 0; i < node.getChildrenSize() - 1; i++) { builder.append(getString(node.getChild(i), prefix + (isTail ? " " : "│ "), string, false)); } if (node.getChildrenSize() >= 1) { builder.append(getString(node.getChild(node.getChildrenSize() - 1), prefix + (isTail ? " " : "│ "), string, true)); } } return builder.toString(); } } private static class JavaCompatibleMapEntry extends java.util.AbstractMap.SimpleEntry { private static final long serialVersionUID = -4427602384853830561L; public JavaCompatibleMapEntry(K key, V value) { super(key, value); } } private static class JavaCompatibleIteratorWrapper implements java.util.Iterator> { private TrieMap map = null; private java.util.Iterator> iter = null; private java.util.Map.Entry lastEntry = null; public JavaCompatibleIteratorWrapper(TrieMap map, java.util.Iterator> iter) { this.map = map; this.iter = iter; } /** * {@inheritDoc} */ @Override public boolean hasNext() { if (iter==null) return false; return iter.hasNext(); } /** * {@inheritDoc} */ @Override public java.util.Map.Entry next() { if (iter==null) return null; lastEntry = iter.next(); return lastEntry; } /** * {@inheritDoc} */ @Override public void remove() { if (iter==null || lastEntry==null) return; map.remove(lastEntry.getKey()); iter.remove(); } } private static class JavaCompatibleTrieMap extends java.util.AbstractMap { private TrieMap map = null; protected JavaCompatibleTrieMap(TrieMap map) { this.map = map; } /** * {@inheritDoc} */ @Override public V put(K key, V value) { return map.put(key, value); } /** * {@inheritDoc} */ @Override public V remove(Object key) { return map.remove((K)key); } /** * {@inheritDoc} */ @Override public void clear() { map.clear(); } /** * {@inheritDoc} */ @Override public boolean containsKey(Object key) { return map.contains((K)key); } /** * {@inheritDoc} */ @Override public int size() { return map.size(); } /** * {@inheritDoc} */ @Override public java.util.Set> entrySet() { java.util.Set> set = new java.util.HashSet>() { private static final long serialVersionUID = 1L; /** * {@inheritDoc} */ @Override public java.util.Iterator> iterator() { return (new JavaCompatibleIteratorWrapper(map,super.iterator())); } }; if (map.trie!=null && map.trie.root!=null) { Trie.Node n = map.trie.root; levelOrder(n,"",set); } return set; } private void levelOrder(Trie.Node node, String string, java.util.Set> set) { StringBuilder builder = new StringBuilder(string); TrieMapNode tmn = null; if (node instanceof TrieMapNode) { tmn = (TrieMapNode)node; if (tmn.character!=Trie.Node.SENTINAL) builder.append(tmn.character); if (tmn.isWord) { K s = (K)builder.toString(); java.util.Map.Entry entry = new JavaCompatibleMapEntry(s, tmn.value); set.add(entry); } } String b = builder.toString(); for (int i=0; i