您的位置:首页 > 编程语言 > Java开发

用radix tree简单实现java.util.Map的功能

2018-01-28 21:09 811 查看
代码地址:https://github.com/kaikai-sk/MyRadixTree

RadixTreeNode.java

package com.sk.radixtree;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;

import javax.xml.soap.Node;

public class RadixTreeNode <V extends Serializable> implements Iterable<RadixTreeNode<V>>,
Comparable<RadixTreeNode<V>>
{
//在节点中的前缀
private String prefix;
//存储在节点中的value
private V value;
//该节点是否存储一个值。 这个值主要由RadixTreeVisitor用来判断是否应该访问这个节点。
private boolean hasValue;
//这个节点的孩子节点。请注意,因为我们在这里使用了{@link TreeSet},所以RadixTree的遍历将按字典顺序排列。
private Collection<RadixTreeNode<V>> children;

/**
* 打印node节点的信息
*/
public void printInfo()
{
if(children!=null)
{
System.out.println("[prefix="+prefix
+",value="+value+
",hasValue="+hasValue+
",numOfChildren"+children.size()+"]");
}
else
{
System.out.println("[prefix="+prefix
+",value="+value+
",hasValue="+hasValue+
"]");
}
}

/**
* 根据给出的prefix构造节点
* @param prefix
*/
public RadixTreeNode(String prefix)
{
this.prefix=prefix;
this.hasValue=false;
}

/**
* Constructs a node from the given prefix and value.
*
* @param prefix  the prefix
* @param value  the value
*/
public RadixTreeNode(String prefix,V value)
{
this.prefix=prefix;
this.value=value;
this.hasValue=true;
}

/**
* Gets the value attached to this node.
*
* @return the value, or <code>null</code> if an internal node
*/
public V getValue()
{
return value;
}

/**
* Sets the value attached to this node.
*
* @param value  the value, or <code>null</code> if an internal node
*/
public void setValue(V value)
{
this.value=value;
}

/**
* Gets the prefix associated with this node.
*
* @return the prefix
*/
String getPrefix()
{
return prefix;
}

/**
* Sets the prefix associated with this node.
*
* @param prefix  the prefix
*/
void setPrefix(String prefix)
{
this.prefix=prefix;
}

/**
* Gets the children of this node.
*
* @return the list of children
*/
Collection<RadixTreeNode<V>> getChildren()
{
//延迟创建children以节约内存
if(children==null)
{
children=new TreeSet<RadixTreeNode<V>>();
}
return children;
}

/**
* Whether or not this node has a value attached to it.
*
* @return whether or not this node has a value
*/
boolean hasValue()
{
return hasValue;
}

/**
* Sets whether or not this node has a value attached to it.
*
* @param hasValue  <code>true</code> if this node will have a value,
*                  <code>false</code> otherwise. If <code>false</code>,
*                  {@link #getValue()} will return <code>null</code>
*                  after this call.
*/
void setHasValue(boolean hasValue)
{
this.hasValue=hasValue;
if(!hasValue)
{
//this.hasValue=(Boolean) null;
}
}

@Override
public int compareTo(RadixTreeNode<V> node)
{
return prefix.toString().compareTo(node.getPrefix().toString());
}

@Override
public Iterator<RadixTreeNode<V>> iterator()
{
if(children==null)
{
return new Iterator<RadixTreeNode<V>>()
{

@Override
public boolean hasNext()
{
return false;
}

@Override
public RadixTreeNode<V> next()
{
return null;
}
};
}
return children.iterator();
}
}


RadixTreeUtil.java

package com.sk.radixtree;

import java.io.Serializable;

/**
* Radix tree utility functions
* @author root
*
*/
public class RadixTreeUtil
{
private RadixTreeUtil()
{

}

static <V extends Serializable> void dumpTree(RadixTreeNode<V> node,String outputPrefix)
{
if(node.hasValue())
{
System.out.format("%s{%s : %s}%n", outputPrefix,node.getPrefix(),node.getValue());
}
else
{
System.out.format("%s{%s}%n", outputPrefix,node.getPrefix(),node.getValue());
}
for (RadixTreeNode<V> child:node)
{
dumpTree(child, outputPrefix+"\t");
}
}

/**
* Prints a radix tree to <code>System.out</code>.
*
* @param tree  the tree
*/
public static <V extends Serializable> void dumpTree(RadixTree <V> tree)
{
dumpTree(tree.root,"");
}

/**
* Finds the length of the largest prefix for two character sequences.
*
* @param a  character sequence
* @param b  character sequence
*
* @return the length of largest prefix of <code>a</code> and <code>b</code>
*
* @throws IllegalArgumentException  if either <code>a</code> or <code>b</code>
*                                   is <code>null</code>
*/
public static int largestPrefixLength(CharSequence a,CharSequence b)
{
int length=0;

for(int i=0;i<Math.min(a.length(), b.length());i++)
{
if(a.charAt(i)!=b.charAt(i))
{
break;
}
length++;
}

return length;
}
}


RadixTreeVisitor.java

package com.sk.radixtree;

public interface RadixTreeVisitor<V,R>
{
/**
* Visits a node in a radix tree.
*
* @param key  the key of the node being visited
* @param value  the value of the node being visited
*/
public abstract void visit(String key,V value);

/**
* An overall result from the traversal of the radix tree.
*
* @return the result
*/
public abstract R getResult();
}


RadixTree.java

package com.sk.radixtree;

import java.util.List;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.sk.radixtree.*;

/**
* A radix tree. Radix trees are String -> Object mappings which allow quick
* lookups on the strings. Radix trees also make it easy to grab the objects
* with a common prefix.
*
* @see <a href="http://en.wikipedia.org/wiki/Radix_tree">Wikipedia</a>
*
* @param <V>  the type of values stored in the tree
*/
public class RadixTree <V extends Serializable> implements Map<String, V>,Serializable
{
public static final String KEY_CANNOT_BE_NULL="key cannot be null";
public static final String KEYS_MUST_BE_STRING_INSTANCES="keys must be String instaces";

//radix tree 的根节点
RadixTreeNode<V> root;

/**
* Gets a list of values whose associated keys have the given prefix.
*
* @param prefix  the prefix to look for
*
* @return the list of values
*
* @throws NullPointerException if prefix is <code>null</code>
*/
public List<V> getValuesWithPrefix(String prefix)
{
RadixTreeVisitor<V,List<V>> visitor=new RadixTreeVisitor<V, List<V>>()
{
List<V> result=new ArrayList<V>();

@Override
public void visit(String key, V value)
{
result.add(value);
}

@Override
public List<V> getResult()
{
return result;
}

};
visit(visitor,prefix);
return visitor.getResult();
}

/**
* Gets a list of keys with the given prefix.
*
* @param prefix  the prefix to look for
*
* @return the list of prefixes
*
* @throws NullPointerException if prefix is <code>null</code>
*/
public List<String> getKeysWithPrefix(String prefix)
{
RadixTreeVisitor<V, List<String>> visitor=new RadixTreeVisitor<V, List<String>>()
{
List<String> result=new ArrayList<String>();

@Override
public void visit(String key, V value)
{
result.add(key);
}

@Override
public List<String> getResult()
{
return result;
}

};
visit(visitor, prefix);
return visitor.getResult();
}

/**
* Gets a list of entries whose associated keys have the given prefix.
*
* @param prefix  the prefix to look for
*
* @return the list of values
*
* @throws NullPointerException if prefix is <code>null</code>
*/
public List<Map.Entry<String,V>> getEntriesWithPrefix(String prefix)
{
RadixTreeVisitor<V, List<Map.Entry<String,V>>> visitor=new
RadixTreeVisitor<V, List<Map.Entry<String,V>>>()
{
List<Map.Entry<String,V>> result=new ArrayList<Map.Entry<String,V>>();

@Override
public void visit(String key, V value)
{
result.add(new AbstractMap.SimpleEntry(key,value));
}

@Override
public List<java.util.Map.Entry<String, V>> getResult()
{
return result;
}
};
visit(visitor, prefix);
return visitor.getResult();
}

/**
* Visits the given node of this tree with the given prefix and visitor. Also,
* recursively visits the left/right subtrees of this node.
*
* @param node  the node
* @param prefix  the prefix
* @param visitor  the visitor
*/
public void  visit(RadixTreeNode<V> node,String prefixAllowed,String prefix,RadixTreeVisitor<V, ?>visitor)
{
if(node.hasValue() && prefix.startsWith(prefixAllowed))
{
visitor.visit(prefix, node.getValue());
}

for(RadixTreeNode<V> child:node)
{
final int prefixLength=prefix.length();
final String newPrefix=prefix+child.getPrefix();
/**
* 还需要继续查找的条件
*/
if(prefixAllowed.length()<=prefixLength
|| newPrefix.length()<=prefixLength
|| newPrefix.charAt(prefixLength) == prefixAllowed.charAt(prefixLength))
{
visit(child, prefixAllowed, newPrefix, visitor);
}
}
}

/**
* Traverses this radix tree using the given visitor. Only values with
* the given prefix will be visited. Note that the tree will be traversed
* in lexicographical order.
*
* @param visitor  the visitor
* @param prefix  the prefix used to restrict visitation
*/
public void visit(RadixTreeVisitor<V, ?> visitor,String prefix)
{
visit(root,prefix,"",visitor);
}

/**
* Traverses this radix tree using the given visitor. Note that the tree
* will be traversed in lexicographical order.
*
* @param visitor  the visitor
*/
public void visit(RadixTreeVisitor<V, ?> visitor )
{
visit(root,"","",visitor);
}

/**
* 缺省的构造函数
*/
public RadixTree()
{
this.root=new RadixTreeNode<V>("");
}

@Override
public int size()
{
RadixTreeVisitor<V, Integer> visitor=new RadixTreeVisitor<V, Integer>()
{
int count=0;

@Override
public void visit(String key, V value)
{
count++;
}

@Override
public Integer getResult()
{
return count;
}

};
visit(visitor);
return visitor.getResult() ;
}

@Override
public boolean isEmpty()
{
return root.getChildren().isEmpty();
}

@Override
public boolean containsKey(final Object keyToCheck)
{
if (keyToCheck==null)
{
throw new NullPointerException(KEY_CANNOT_BE_NULL);
}
if(!(keyToCheck instanceof String))
{
throw new ClassCastException(KEYS_MUST_BE_STRING_INSTANCES);
}

RadixTreeVisitor<V, Boolean> visitor=new RadixTreeVisitor<V, Boolean>()
{
boolean found =false;

@Override
public void visit(String key, V value)
{
if(key.equals(keyToCheck))
{
found=true;
}
}

@Override
public Boolean getResult()
{
return found;
}
};
visit(visitor, (String)keyToCheck);
return visitor.getResult();

}

@Override
public boolean containsValue(final Object val)
{
RadixTreeVisitor<V, Boolean> visitor=new RadixTreeVisitor<V, Boolean>()
{
boolean found=false;

@Override
public void visit(String key, V value)
{
if(val==value || (value!=null) && value.equals(val))
{
found=true;
}
}

@Override
public Boolean getResult()
{
return found;
}

};
visit(visitor);
return visitor.getResult();
}

@Override
public V get(final Object keyToCheck)
{
if(keyToCheck==null)
{
throw new NullPointerException(KEY_CANNOT_BE_NULL);
}
if(!(keyToCheck instanceof String))
{
throw new ClassCastException(KEYS_MUST_BE_STRING_INSTANCES);
}

RadixTreeVisitor<V, V> visitor=new RadixTreeVisitor<V, V>()
{
V result=null;

@Override
public void visit(String key, V value)
{
if(key.equals(keyToCheck))
{
result=value;
}

}

@Override
public V getResult()
{
return result;
}
};
visit(visitor, (String)keyToCheck);
return visitor.getResult();
}

@Override
public V put(String key, V value)
{
if(key==null)
{
throw new NullPointerException(KEY_CANNOT_BE_NULL);
}

return put(key, value,root);
}

/**
* Remove the value with the given key from the subtree rooted at the
* given node.
*
* @param key  the key
* @param node  the node to start searching from
*
* @return the old value associated with the given key, or <code>null</code>
*         if there was no mapping for <code>key</code>
*/
private V put(String key, V value, RadixTreeNode<V> node)
{
V ret =null;
final int largestPrefixLength=RadixTreeUtil.largestPrefixLength(key, node.getPrefix());

//key就是node的情况
if(largestPrefixLength==node.getPrefix().length() && largestPrefixLength==key.length())
{
ret=node.getValue();
node.setValue(value);
node.setHasValue(true);
}
//key所在节点在node的子节点中
else if(largestPrefixLength==0 ||
(largestPrefixLength<key.length()) && largestPrefixLength>=node.getPrefix().length() )
{
// Key is bigger than the prefix located at this node, so we need to see if
// there's a child that can possibly share a prefix, and if not, we just add
// a new node to this node
final String leftoverKey=key.substring(largestPrefixLength);
boolean found=false;
for(RadixTreeNode<V> child:node)
{
if(child.getPrefix().charAt(0)==leftoverKey.charAt(0))
{
found=true;
ret=put(leftoverKey, value, child);
break;
}
}

if(!found)
{
// No child exists with any prefix of the given key, so add a new one
RadixTreeNode<V> n=new RadixTreeNode<V>(leftoverKey,value);
node.getChildren().add(n);
}
}
// Key and node.getPrefix() share a prefix, so split node
else if(largestPrefixLength<node.getPrefix().length())
{
final String leftoverPrefix=node.getPrefix().substring(largestPrefixLength);

node.printInfo();

//创建一个新的节点
final RadixTreeNode<V> n=new RadixTreeNode<V>(leftoverPrefix,node.getValue());
n.setHasValue(node.hasValue());
n.getChildren().addAll(node.getChildren());

n.printInfo();

//原来的节点升级了(喜当爹)
node.setPrefix(node.getPrefix().substring(0,largestPrefixLength));
node.getChildren().clear();
node.getChildren().add(n);

node.printInfo();

// The largest prefix is equal to the key, so set this node's value
if(largestPrefixLength==key.length())
{
ret=node.getValue();
node.setValue(value);
node.setHasValue(true);
}
//// There's a leftover suffix on the key, so add another child
else
{
final String leftoverKey=key.substring(largestPrefixLength);
final RadixTreeNode<V> n1=new RadixTreeNode<V>(leftoverKey,value);
node.getChildren().add(n1);

node.printInfo();

node.setHasValue(false);
}
}
else
{
// node.getPrefix() is a prefix of key, so add as child
final String leftoverKey=key.substring(largestPrefixLength);
RadixTreeNode<V> n=new RadixTreeNode<V>(leftoverKey,value);
node.getChildren().add(n);
}
return ret;
}

@Override
public V remove(Object key)
{
if(key==null)
{
throw new NullPointerException(KEY_CANNOT_BE_NULL);
}
if(!(key instanceof String))
{
throw new ClassCastException(KEYS_MUST_BE_STRING_INSTANCES);
}

final String sKey=(String)key;
if(sKey.equals(""))
{
final V value=root.getValue();
root.setHasValue(false);
return value;
}
return remove(sKey,root);
}

/**
* Remove the value with the given key from the subtree rooted at the
* given node.
*
* @param key  the key
* @param node  the node to start searching from
*
* @return the value associated with the given key, or <code>null</code>
*         if there was no mapping for <code>key</code>
*/
private V remove(String key,RadixTreeNode<V> node)
{
V ret =null;
final Iterator<RadixTreeNode<V>> iterator=node.getChildren().iterator();
while(iterator.hasNext())
{
final RadixTreeNode<V> child=iterator.next();
final int largestPrefixLen=RadixTreeUtil.largestPrefixLength(key, child.getPrefix());
// Found our match, remove the value from this node
if(largestPrefixLen==key.length() && largestPrefixLen==child.getPrefix().length())
{
if(child.getChildren().isEmpty())
{
ret=child.getValue();
iterator.remove();
break;
}
else if(child.hasValue())
{
//internal node
ret=child.getValue();
child.setHasValue(false);

if(child.getChildren().size()==1)
{
// The subchild's prefix can be reused, with a little modification
final RadixTreeNode<V> subChild=child.getChildren().iterator().next();
final String newPrefix=child.getPrefix()+subChild.getPrefix();

// Merge child node with its single child
child.setValue(subChild.getValue());
child.setHasValue(subChild.hasValue());
child.setPrefix(newPrefix);
child.getChildren().clear();
}
break;
}
}
else if(largestPrefixLen>0 && largestPrefixLen<key.length())
{
// Continue down subtree of child
final String leftoverKey=key.substring(largestPrefixLen);
ret=remove(leftoverKey, child);
break;
}
}
return ret;
}

@Override
public void putAll(Map<? extends String, ? extends V> map)
{
for(Map.Entry<? extends String, ? extends V> entry:map.entrySet())
{
put(entry.getKey(), entry.getValue());
}

}

@Override
public void clear()
{
root.getChildren().clear();
}

@Override
public Set<String> keySet()
{
// TODO documentation Of Map.keySet() specifies that this is a view of
//      the keys, and modifications to this collection should be
//      reflected in the parent structure
//
RadixTreeVisitor<V, Set<String>> visitor=new RadixTreeVisitor<V, Set<String>>()
{
Set<String> result=new TreeSet<String>();

@Override
public void visit(String key, V value)
{
result.add(key);
}

@Override
public Set<String> getResult()
{
// TODO Auto-generated method stub
return result;
}

};
visit(visitor);
return visitor.getResult();
}

@Override
public Collection<V> values()
{
// TODO documentation Of Map.values() specifies that this is a view of
//      the values, and modifications to this collection should be
//      reflected in the parent structure
//
RadixTreeVisitor<V, Collection<V>> visitor=new RadixTreeVisitor<V, Collection<V>>()
{
Collection<V> values=new ArrayList<V>();

@Override
public void visit(String key, V value)
{
values.add(value);
}

@Override
public Collection<V> getResult()
{
return values;
}
};
visit(visitor);
return visitor.getResult();
}

@Override
public Set<java.util.Map.Entry<String, V>> entrySet()
{
// TODO documentation Of Map.entrySet() specifies that this is a view of
//      the entries, and modifications to this collection should be
//      reflected in the parent structure
//
RadixTreeVisitor<V, Set<Map.Entry<String, V>>> visitor=new
RadixTreeVisitor<V, Set<Map.Entry<String, V>>>()
{
Set<Map.Entry<String, V>> result = new HashSet<Map.Entry<String, V>>();

@Override
public void visit(String key, V value)
{
result.add(new AbstractMap.SimpleEntry(key,value));
}

@Override
public Set<java.util.Map.Entry<String, V>> getResult()
{
return result;
}

};

visit(visitor);
return visitor.getResult();
}

}


RadixTreeUtilTest.java

package com.sk.radixtree;

import static org.junit.Assert.*;

import org.junit.Test;

public class RadixTreeUtilTest
{

@Test
public void testDumpTreeRadixTreeNodeOfVString()
{
}

@Test
public void testDumpTreeRadixTreeOfV()
{
}

@Test
public void testLargestPrefixLength()
{
assertEquals(5, RadixTreeUtil.largestPrefixLength("abcdefg", "abcdexyz"));
assertEquals(3, RadixTreeUtil.largestPrefixLength("abcdefg", "abcxyz"));
assertEquals(3, RadixTreeUtil.largestPrefixLength("abcdefg", "abctuvxyz"));
assertEquals(0, RadixTreeUtil.largestPrefixLength("abcdefg", ""));
assertEquals(0, RadixTreeUtil.largestPrefixLength("", "abcxyz"));
assertEquals(0, RadixTreeUtil.largestPrefixLength("xyz", "abcxyz"));
}
}


TestRadixTree.java

package com.sk.radixtree;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.Test;

import com.sk.radixtree.*;

public class TestRadixTree
{
private SecureRandom rng=new SecureRandom();

@Test
public void testManyInsertions() {
RadixTree<BigInteger> tree = new RadixTree<BigInteger>();

int n = rng.nextInt(401) + 100; // n in [100, 500]

List<BigInteger> strings = generateRandomStrings(n);
for(BigInteger x : strings)
tree.put(x.toString(32), x);

assertEquals(strings.size(), tree.size());
for(BigInteger x : strings)
assertTrue(tree.containsKey(x.toString(32)));

assertEqualsWithSort(strings, new ArrayList<BigInteger>(tree.values()));
}

private List<BigInteger> generateRandomStrings(int n) {
List<BigInteger> strings = new ArrayList<BigInteger>();
while(n-- > 0) {
final BigInteger bigint = new BigInteger(20, rng);
if(!strings.contains(bigint))
strings.add(bigint);
}
return strings;
}

@Test
public void testRealData() throws IOException {
BufferedReader br = null;

try {
br=new BufferedReader(new InputStreamReader(new FileInputStream("data/dict.dat")));
final RadixTree<String> tree = new RadixTree<String>();

// Read data into a list first, because we want to see how long it
// takes to construct the radix tree but don't want to include
// I/O reads in that time
int numLines = 0;
String line = null;
ArrayList<String[]> lines = new ArrayList<String[]>();
while((line = br.readLine()) != null) {
line = line.trim();
if(line.length() > 0) {
String pieces[] = line.split("\\s+", 2);
if(pieces.length == 2)
lines.add(pieces);
}
}

// Construct tree and time the construction
for(String [] pieces : lines) {
// Just in case the DAT files has multiple of the same key
if(tree.put(pieces[0], pieces[1]) == null)
++numLines;
}

// Perform tests
assertEquals(numLines, tree.size());

assertEqualsWithSort(tree.getValuesWithPrefix("ACCI").toArray(new String[0]), new String[] {
"a269c0caf5ed6edaaf0dfe6af2756256", // ACCIDENT
"cd465f695982b126e418243260f60f9b", // ACCIDENTAL
"e7fdb552e28b0fa234e7f3363d2577ed", // ACCIDENTALLY
"9300fe6261f9198d86879c0925ca652c", // ACCIDENTLY
"6524df69cc016e5d9a3cd03cb8a58815", // ACCIDENTS
"edad4ecb40b0d95df72d3e587692f601", // ACCION
"7868257ae2c6e06f8ccc6841cdc64ff1", // ACCIVAL
});

assertEqualsWithSort(tree.getValuesWithPrefix("OSTEN").toArray(new String[0]), new String[] {
"1eef84bdf3b7307e72b99c0ed0327165", // OSTEN
"5011b15cd7e790336d23b70a48f7bed7", // OSTENDORF
"4ed1b43b743a0dee269d4184880bbffd", // OSTENSIBLE
"d318060019c4f987986e383cf700e7f9", // OSTENSIBLY
"d32a6e75341b4c1797485622bb388c8a", // OSTENSON
"4df4a85a0a9a345a03493c484090002d", // OSTENTATION
"4849c1ab49637b63ba1570b5abf33d1c", // OSTENTATIOUS
"d65f9261a233810f51c73cd38ab1398e", // OSTENTATIOUSLY
});

assertEqualsWithSort(tree.getKeysWithPrefix("OSTEN").toArray(new String[0]), new String[] {
"OSTEN",
"OSTENDORF",
"OSTENSIBLE",
"OSTENSIBLY",
"OSTENSON",
"OSTENTATION",
"OSTENTATIOUS",
"OSTENTATIOUSLY"
});
} finally {
if(br != null)
br.close();
}
}

@Test
public void testRemoval()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("test", 1);
tree.put("tent", 2);
tree.put("tank", 3);
assertEquals(3, tree.size());
assertTrue(tree.containsKey("tent"));

tree.remove("key");
assertEquals(3, tree.size());
assertTrue(tree.containsKey("tent"));

tree.remove("tent");
assertEquals(2, tree.size());
assertEquals(1, tree.get("test").intValue());
assertFalse(tree.containsKey("tent"));
assertEquals(3, tree.get("tank").intValue());
}

@Test
public void testSpook()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("pook", 1);
tree.put("spook", 2);

assertEquals(2, tree.size());
assertEqualsWithSort(tree.keySet().toArray(new String[0]),
new String[]{"pook","spook"});
}

@Test
public void testPrefixFetch()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("test", 1);
tree.put("tent", 2);
tree.put("rest", 3);
tree.put("tank", 4);

assertEquals(4, tree.size());
assertEqualsWithSort(tree.getValuesWithPrefix(""), new ArrayList<Integer>(tree.values()));
assertEqualsWithSort(tree.getValuesWithPrefix("t").toArray(new Integer[0]),
new Integer[]{1,2,4});
assertEqualsWithSort(tree.getValuesWithPrefix("te").toArray(new Integer[0]),
new Integer[]{1,2});
assertEqualsWithSort(tree.getValuesWithPrefix("asd").toArray(new Integer[0]),
new Integer[0]);

}

@Test
public void testMultipleInsertionsOfTheSameKey()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("test", 1);
tree.put("tent", 2);
tree.put("tank", 3);
tree.put("rest", 4);

assertEquals(4, tree.size());
assertEquals(1, tree.get("test").intValue());
assertEquals(2, tree.get("tent").intValue());
assertEquals(3, tree.get("tank").intValue());
assertEquals(4, tree.get("rest").intValue());

tree.put("test", 9);
assertEquals(4, tree.size());
assertEquals(9, tree.get("test").intValue());
assertEquals(2, tree.get("tent").intValue());
assertEquals(3, tree.get("tank").intValue());
assertEquals(4, tree.get("rest").intValue());
}

@Test
public void testMultipleInsertions()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("test", 1);
tree.put("tent", 2);
tree.put("tank", 3);
tree.put("rest", 4);

assertEquals(4, tree.size());
assertEquals(1, tree.get("test").intValue());
assertEquals(2, tree.get("tent").intValue());
assertEquals(3, tree.get("tank").intValue());
assertEquals(4, tree.get("rest").intValue());
}

@Test
public void testSingleInsertion()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
tree.put("test", 1);
assertEquals(1, tree.size());
assertTrue(tree.containsKey("test"));
}

@Test
public void testEmptyTree()
{
RadixTree<Integer> tree=new RadixTree<Integer>();
assertEquals(0, tree.size());
}

public static <T extends Comparable<? super T>> void assertEqualsWithSort(T[] a,T[] b)
{
Arrays.sort(a);
Arrays.sort(b);
assertArrayEquals(a, b);
}

/**
* A variation of <code>assertEquals</code> that sorts its lists before comparing them.
*
* @param a  one of the lists
* @param b  one of the lists
*/
public static <T extends Comparable<? super T>> void assertEqualsWithSort(
List<T> a,List<T> b)
{
Collections.sort(a);
Collections.sort(b);
assertEquals(a, b);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: