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

代码地址: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);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值