633 lines
15 KiB
Java
633 lines
15 KiB
Java
|
/*This is a java program to implement Hash Tree. In computer science, a hash tree (or hash trie) is a persistent data structure that can be used to implement sets and maps, intended to replace hash tables in purely functional programming. In its basic form, a hash tree stores the hashes of its keys, regarded as strings of bits, in a trie, with the actual keys and (optional) values stored at the trie’s “final” nodes.*/
|
|||
|
|
|||
|
import java.io.IOException;
|
|||
|
import java.io.ObjectInputStream;
|
|||
|
import java.io.ObjectOutputStream;
|
|||
|
import java.io.Serializable;
|
|||
|
import java.util.Arrays;
|
|||
|
import java.util.Collection;
|
|||
|
import java.util.HashMap;
|
|||
|
import java.util.Iterator;
|
|||
|
import java.util.Map;
|
|||
|
import java.util.Set;
|
|||
|
|
|||
|
interface HashTreeTraverser
|
|||
|
{
|
|||
|
public void addNode(Object node, HashTree subTree);
|
|||
|
|
|||
|
public void subtractNode();
|
|||
|
|
|||
|
public void processPath();
|
|||
|
}
|
|||
|
|
|||
|
class TreeSearcher implements HashTreeTraverser
|
|||
|
{
|
|||
|
Object target;
|
|||
|
HashTree result;
|
|||
|
|
|||
|
public TreeSearcher(Object t)
|
|||
|
{
|
|||
|
target = t;
|
|||
|
}
|
|||
|
|
|||
|
public HashTree getResult()
|
|||
|
{
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public void addNode(Object node, HashTree subTree)
|
|||
|
{
|
|||
|
result = subTree.getTree(target);
|
|||
|
if (result != null)
|
|||
|
{
|
|||
|
throw new RuntimeException("found"); // short circuit traversal
|
|||
|
// when found
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void subtractNode()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public void processPath()
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class ConvertToString implements HashTreeTraverser
|
|||
|
{
|
|||
|
StringBuffer string = new StringBuffer(getClass().getName() + "{");
|
|||
|
StringBuffer spaces = new StringBuffer();
|
|||
|
int depth = 0;
|
|||
|
|
|||
|
public void addNode(Object key, HashTree subTree)
|
|||
|
{
|
|||
|
depth++;
|
|||
|
string.append("\n" + getSpaces() + key + " {");
|
|||
|
}
|
|||
|
|
|||
|
public void subtractNode()
|
|||
|
{
|
|||
|
string.append("\n" + getSpaces() + "}");
|
|||
|
depth--;
|
|||
|
}
|
|||
|
|
|||
|
public void processPath()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public String toString()
|
|||
|
{
|
|||
|
string.append("\n}");
|
|||
|
return string.toString();
|
|||
|
}
|
|||
|
|
|||
|
private String getSpaces()
|
|||
|
{
|
|||
|
if (spaces.length() < depth * 2)
|
|||
|
{
|
|||
|
while (spaces.length() < depth * 2)
|
|||
|
{
|
|||
|
spaces.append(" ");
|
|||
|
}
|
|||
|
}
|
|||
|
else if (spaces.length() > depth * 2)
|
|||
|
{
|
|||
|
spaces.setLength(depth * 2);
|
|||
|
}
|
|||
|
return spaces.toString();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|||
|
public class HashTree implements Serializable, Map
|
|||
|
{
|
|||
|
private static final long serialVersionUID = 5526070397395889719L;
|
|||
|
|
|||
|
public HashTree()
|
|||
|
{
|
|||
|
data = new HashMap();
|
|||
|
}
|
|||
|
|
|||
|
public HashTree(Object key)
|
|||
|
{
|
|||
|
data = new HashMap();
|
|||
|
data.put(key, new HashTree());
|
|||
|
}
|
|||
|
|
|||
|
public void putAll(Map map)
|
|||
|
{
|
|||
|
if (map instanceof HashTree)
|
|||
|
{
|
|||
|
this.add((HashTree) map);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
throw new UnsupportedOperationException(
|
|||
|
"can only putAll other HashTree objects");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Set entrySet()
|
|||
|
{
|
|||
|
return data.entrySet();
|
|||
|
}
|
|||
|
|
|||
|
public boolean containsValue(Object value)
|
|||
|
{
|
|||
|
return data.containsValue(value);
|
|||
|
}
|
|||
|
|
|||
|
public Object put(Object key, Object value)
|
|||
|
{
|
|||
|
Object previous = data.get(key);
|
|||
|
add(key, value);
|
|||
|
return previous;
|
|||
|
}
|
|||
|
|
|||
|
public void clear()
|
|||
|
{
|
|||
|
data.clear();
|
|||
|
}
|
|||
|
|
|||
|
public Collection values()
|
|||
|
{
|
|||
|
return data.values();
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object key, HashTree subTree)
|
|||
|
{
|
|||
|
add(key);
|
|||
|
getTree(key).add(subTree);
|
|||
|
}
|
|||
|
|
|||
|
public void add(HashTree newTree)
|
|||
|
{
|
|||
|
Iterator iter = newTree.list().iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object item = iter.next();
|
|||
|
add(item);
|
|||
|
getTree(item).add(newTree.getTree(item));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public HashTree(Collection keys)
|
|||
|
{
|
|||
|
data = new HashMap();
|
|||
|
Iterator it = keys.iterator();
|
|||
|
while (it.hasNext())
|
|||
|
{
|
|||
|
data.put(it.next(), new HashTree());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public HashTree(Object[] keys)
|
|||
|
{
|
|||
|
data = new HashMap();
|
|||
|
for (int x = 0; x < keys.length; x++)
|
|||
|
{
|
|||
|
data.put(keys[x], new HashTree());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public boolean containsKey(Object o)
|
|||
|
{
|
|||
|
return data.containsKey(o);
|
|||
|
}
|
|||
|
|
|||
|
public boolean isEmpty()
|
|||
|
{
|
|||
|
return data.isEmpty();
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object key, Object value)
|
|||
|
{
|
|||
|
data.put(key, createNewTree(value));
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object key, HashTree t)
|
|||
|
{
|
|||
|
data.put(key, t);
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object key, Object[] values)
|
|||
|
{
|
|||
|
data.put(key, createNewTree(Arrays.asList(values)));
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object key, Collection values)
|
|||
|
{
|
|||
|
data.put(key, createNewTree(values));
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object[] treePath, Object[] values)
|
|||
|
{
|
|||
|
if (treePath != null && values != null)
|
|||
|
{
|
|||
|
set(Arrays.asList(treePath), Arrays.asList(values));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void set(Object[] treePath, Collection values)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
set(Arrays.asList(treePath), values);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void set(Collection treePath, Object[] values)
|
|||
|
{
|
|||
|
HashTree tree = addTreePath(treePath);
|
|||
|
tree.set(Arrays.asList(values));
|
|||
|
}
|
|||
|
|
|||
|
public void set(Collection values)
|
|||
|
{
|
|||
|
clear();
|
|||
|
this.add(values);
|
|||
|
}
|
|||
|
|
|||
|
public void set(Collection treePath, Collection values)
|
|||
|
{
|
|||
|
HashTree tree = addTreePath(treePath);
|
|||
|
tree.set(values);
|
|||
|
}
|
|||
|
|
|||
|
public HashTree add(Object key)
|
|||
|
{
|
|||
|
if (!data.containsKey(key))
|
|||
|
{
|
|||
|
HashTree newTree = createNewTree();
|
|||
|
data.put(key, newTree);
|
|||
|
return newTree;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return getTree(key);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object[] keys)
|
|||
|
{
|
|||
|
for (int x = 0; x < keys.length; x++)
|
|||
|
{
|
|||
|
add(keys[x]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void add(Collection keys)
|
|||
|
{
|
|||
|
Iterator it = keys.iterator();
|
|||
|
while (it.hasNext())
|
|||
|
{
|
|||
|
add(it.next());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public HashTree add(Object key, Object value)
|
|||
|
{
|
|||
|
add(key);
|
|||
|
return getTree(key).add(value);
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object key, Object[] values)
|
|||
|
{
|
|||
|
add(key);
|
|||
|
getTree(key).add(values);
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object key, Collection values)
|
|||
|
{
|
|||
|
add(key);
|
|||
|
getTree(key).add(values);
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object[] treePath, Object[] values)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
add(Arrays.asList(treePath), Arrays.asList(values));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void add(Object[] treePath, Collection values)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
add(Arrays.asList(treePath), values);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public HashTree add(Object[] treePath, Object value)
|
|||
|
{
|
|||
|
return add(Arrays.asList(treePath), value);
|
|||
|
}
|
|||
|
|
|||
|
public void add(Collection treePath, Object[] values)
|
|||
|
{
|
|||
|
HashTree tree = addTreePath(treePath);
|
|||
|
tree.add(Arrays.asList(values));
|
|||
|
}
|
|||
|
|
|||
|
public HashTree add(Collection treePath, Object value)
|
|||
|
{
|
|||
|
HashTree tree = addTreePath(treePath);
|
|||
|
return tree.add(value);
|
|||
|
}
|
|||
|
|
|||
|
public void add(Collection treePath, Collection values)
|
|||
|
{
|
|||
|
HashTree tree = addTreePath(treePath);
|
|||
|
tree.add(values);
|
|||
|
}
|
|||
|
|
|||
|
protected HashTree addTreePath(Collection treePath)
|
|||
|
{
|
|||
|
HashTree tree = this;
|
|||
|
Iterator iter = treePath.iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object temp = iter.next();
|
|||
|
tree.add(temp);
|
|||
|
tree = tree.getTree(temp);
|
|||
|
}
|
|||
|
return tree;
|
|||
|
}
|
|||
|
|
|||
|
public HashTree getTree(Object key)
|
|||
|
{
|
|||
|
return (HashTree) data.get(key);
|
|||
|
}
|
|||
|
|
|||
|
public Object get(Object key)
|
|||
|
{
|
|||
|
return getTree(key);
|
|||
|
}
|
|||
|
|
|||
|
public HashTree getTree(Object[] treePath)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
return getTree(Arrays.asList(treePath));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return this;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Object clone()
|
|||
|
{
|
|||
|
HashTree newTree = new HashTree();
|
|||
|
cloneTree(newTree);
|
|||
|
return newTree;
|
|||
|
}
|
|||
|
|
|||
|
protected void cloneTree(HashTree newTree)
|
|||
|
{
|
|||
|
Iterator iter = list().iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object key = iter.next();
|
|||
|
newTree.set(key, (HashTree) getTree(key).clone());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected HashTree createNewTree()
|
|||
|
{
|
|||
|
return new HashTree();
|
|||
|
}
|
|||
|
|
|||
|
protected HashTree createNewTree(Object key)
|
|||
|
{
|
|||
|
return new HashTree(key);
|
|||
|
}
|
|||
|
|
|||
|
protected HashTree createNewTree(Collection values)
|
|||
|
{
|
|||
|
return new HashTree(values);
|
|||
|
}
|
|||
|
|
|||
|
public HashTree getTree(Collection treePath)
|
|||
|
{
|
|||
|
return getTreePath(treePath);
|
|||
|
}
|
|||
|
|
|||
|
public Collection list()
|
|||
|
{
|
|||
|
return data.keySet();
|
|||
|
}
|
|||
|
|
|||
|
public Collection list(Object key)
|
|||
|
{
|
|||
|
HashTree temp = (HashTree) data.get(key);
|
|||
|
if (temp != null)
|
|||
|
{
|
|||
|
return temp.list();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Object remove(Object key)
|
|||
|
{
|
|||
|
return data.remove(key);
|
|||
|
}
|
|||
|
|
|||
|
public Collection list(Object[] treePath)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
return list(Arrays.asList(treePath));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return list();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Collection list(Collection treePath)
|
|||
|
{
|
|||
|
return getTreePath(treePath).list();
|
|||
|
}
|
|||
|
|
|||
|
public Object replace(Object currentKey, Object newKey)
|
|||
|
{
|
|||
|
HashTree tree = getTree(currentKey);
|
|||
|
data.remove(currentKey);
|
|||
|
data.put(newKey, tree);
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public Object[] getArray()
|
|||
|
{
|
|||
|
return data.keySet().toArray();
|
|||
|
}
|
|||
|
|
|||
|
public Object[] getArray(Object key)
|
|||
|
{
|
|||
|
return getTree(key).getArray();
|
|||
|
}
|
|||
|
|
|||
|
public Object[] getArray(Object[] treePath)
|
|||
|
{
|
|||
|
if (treePath != null)
|
|||
|
{
|
|||
|
return getArray(Arrays.asList(treePath));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return getArray();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Object[] getArray(Collection treePath)
|
|||
|
{
|
|||
|
HashTree tree = getTreePath(treePath);
|
|||
|
return tree.getArray();
|
|||
|
}
|
|||
|
|
|||
|
protected HashTree getTreePath(Collection treePath)
|
|||
|
{
|
|||
|
HashTree tree = this;
|
|||
|
Iterator iter = treePath.iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object temp = iter.next();
|
|||
|
tree = tree.getTree(temp);
|
|||
|
}
|
|||
|
return tree;
|
|||
|
}
|
|||
|
|
|||
|
public int hashCode()
|
|||
|
{
|
|||
|
return data.hashCode() * 7;
|
|||
|
}
|
|||
|
|
|||
|
public boolean equals(Object o)
|
|||
|
{
|
|||
|
if (!(o instanceof HashTree))
|
|||
|
return false;
|
|||
|
HashTree oo = (HashTree) o;
|
|||
|
if (oo.size() != this.size())
|
|||
|
return false;
|
|||
|
return data.equals(oo.data);
|
|||
|
}
|
|||
|
|
|||
|
public Set keySet()
|
|||
|
{
|
|||
|
return data.keySet();
|
|||
|
}
|
|||
|
|
|||
|
public HashTree search(Object key)
|
|||
|
{
|
|||
|
HashTree result = getTree(key);
|
|||
|
if (result != null)
|
|||
|
{
|
|||
|
return result;
|
|||
|
}
|
|||
|
TreeSearcher searcher = new TreeSearcher(key);
|
|||
|
try
|
|||
|
{
|
|||
|
traverse(searcher);
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
// do nothing - means object is found
|
|||
|
}
|
|||
|
return searcher.getResult();
|
|||
|
}
|
|||
|
|
|||
|
void readObject(ObjectInputStream ois) throws ClassNotFoundException,
|
|||
|
IOException
|
|||
|
{
|
|||
|
ois.defaultReadObject();
|
|||
|
}
|
|||
|
|
|||
|
void writeObject(ObjectOutputStream oos) throws IOException
|
|||
|
{
|
|||
|
oos.defaultWriteObject();
|
|||
|
}
|
|||
|
|
|||
|
public int size()
|
|||
|
{
|
|||
|
return data.size();
|
|||
|
}
|
|||
|
|
|||
|
public void traverse(HashTreeTraverser visitor)
|
|||
|
{
|
|||
|
Iterator iter = list().iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object item = iter.next();
|
|||
|
visitor.addNode(item, getTree(item));
|
|||
|
getTree(item).traverseInto(visitor);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void traverseInto(HashTreeTraverser visitor)
|
|||
|
{
|
|||
|
if (list().size() == 0)
|
|||
|
{
|
|||
|
visitor.processPath();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Iterator iter = list().iterator();
|
|||
|
while (iter.hasNext())
|
|||
|
{
|
|||
|
Object item = iter.next();
|
|||
|
visitor.addNode(item, getTree(item));
|
|||
|
getTree(item).traverseInto(visitor);
|
|||
|
}
|
|||
|
}
|
|||
|
visitor.subtractNode();
|
|||
|
}
|
|||
|
|
|||
|
public String toString()
|
|||
|
{
|
|||
|
ConvertToString converter = new ConvertToString();
|
|||
|
traverse(converter);
|
|||
|
return converter.toString();
|
|||
|
}
|
|||
|
|
|||
|
protected Map data;
|
|||
|
|
|||
|
public static void main(String args[])
|
|||
|
{
|
|||
|
Collection treePath = Arrays
|
|||
|
.asList(new String[] { "1", "2", "3", "4" });
|
|||
|
HashTree tree = new HashTree();
|
|||
|
tree.add(treePath, "value");
|
|||
|
HashTree tree1 = new HashTree("abcd");
|
|||
|
HashTree tree2 = new HashTree("abcd");
|
|||
|
HashTree tree3 = new HashTree("abcde");
|
|||
|
HashTree tree4 = new HashTree("abcde");
|
|||
|
System.out.println("Is tree1 equals tree2: " + tree1.equals(tree1));
|
|||
|
System.out.println("Is hashcodes of tree1 and tree2 are equal: "
|
|||
|
+ (tree1.hashCode() == tree2.hashCode()));
|
|||
|
System.out.println("Is tree3 equals tree3: " + tree3.equals(tree3));
|
|||
|
tree1.add("abcd", tree3);
|
|||
|
System.out.println("Is modified tree1 is equal to tree3: "
|
|||
|
+ tree1.equals(tree2));
|
|||
|
tree2.add("abcd", tree4);
|
|||
|
System.out.println("Is modified tree2 equals tree1: "
|
|||
|
+ tree1.equals(tree2));
|
|||
|
System.out.println("Is hashcodes are equal: "
|
|||
|
+ (tree1.hashCode() == tree2.hashCode()));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
Is tree1 equals tree2: true
|
|||
|
Is hashcodes of tree1 and tree2 are equal: true
|
|||
|
Is tree3 equals tree3: true
|
|||
|
Is modified tree1 is equal to tree3: false
|
|||
|
Is modified tree2 equals tree1: true
|
|||
|
Is hashcodes are equal: true
|