mirror of
https://github.com/apache/commons-collections.git
synced 2025-02-22 18:28:46 +00:00
[COLLECTION-225] Added first version patricia trie contribution.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1365732 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
15a747624f
commit
c57a9af3e7
6
pom.xml
6
pom.xml
@ -130,6 +130,9 @@
|
||||
<contributor>
|
||||
<name>Ola Berg</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Sam Berlin</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Christopher Berry</name>
|
||||
</contributor>
|
||||
@ -205,6 +208,9 @@
|
||||
<contributor>
|
||||
<name>Marc Johnson</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Roger Kapsi</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Nissim Karpenstein</name>
|
||||
</contributor>
|
||||
|
238
src/main/java/org/apache/commons/collections/Trie.java
Normal file
238
src/main/java/org/apache/commons/collections/Trie.java
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Defines the interface for a prefix tree, an ordered tree data structure. For
|
||||
* more information, see <a href="http://en.wikipedia.org/wiki/Trie">Tries</a>.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface Trie<K, V> extends SortedMap<K, V> {
|
||||
|
||||
/**
|
||||
* Returns the {@link Entry} whose key is closest in a bitwise XOR
|
||||
* metric to the given key. This is NOT lexicographic closeness.
|
||||
* For example, given the keys:
|
||||
*
|
||||
* <ol>
|
||||
* <li>D = 1000100
|
||||
* <li>H = 1001000
|
||||
* <li>L = 1001100
|
||||
* </ol>
|
||||
*
|
||||
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
|
||||
* return 'L', because the XOR distance between D & L is smaller
|
||||
* than the XOR distance between D & H.
|
||||
*
|
||||
* @return The {@link Entry} whose key is closest in a bitwise XOR metric
|
||||
* to the provided key.
|
||||
*/
|
||||
public Map.Entry<K, V> select(K key);
|
||||
|
||||
/**
|
||||
* Returns the key that is closest in a bitwise XOR metric to the
|
||||
* provided key. This is NOT lexicographic closeness!
|
||||
*
|
||||
* For example, given the keys:
|
||||
*
|
||||
* <ol>
|
||||
* <li>D = 1000100
|
||||
* <li>H = 1001000
|
||||
* <li>L = 1001100
|
||||
* </ol>
|
||||
*
|
||||
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
|
||||
* return 'L', because the XOR distance between D & L is smaller
|
||||
* than the XOR distance between D & H.
|
||||
*
|
||||
* @return The key that is closest in a bitwise XOR metric to the provided key.
|
||||
*/
|
||||
public K selectKey(K key);
|
||||
|
||||
/**
|
||||
* Returns the value whose key is closest in a bitwise XOR metric to
|
||||
* the provided key. This is NOT lexicographic closeness!
|
||||
*
|
||||
* For example, given the keys:
|
||||
*
|
||||
* <ol>
|
||||
* <li>D = 1000100
|
||||
* <li>H = 1001000
|
||||
* <li>L = 1001100
|
||||
* </ol>
|
||||
*
|
||||
* If the {@link Trie} contained 'H' and 'L', a lookup of 'D' would
|
||||
* return 'L', because the XOR distance between D & L is smaller
|
||||
* than the XOR distance between D & H.
|
||||
*
|
||||
* @return The value whose key is closest in a bitwise XOR metric
|
||||
* to the provided key.
|
||||
*/
|
||||
public V selectValue(K key);
|
||||
|
||||
/**
|
||||
* Iterates through the {@link Trie}, starting with the entry whose bitwise
|
||||
* value is closest in an XOR metric to the given key. After the closest
|
||||
* entry is found, the {@link Trie} will call select on that entry and continue
|
||||
* calling select for each entry (traversing in order of XOR closeness,
|
||||
* NOT lexicographically) until the cursor returns {@link Decision#EXIT}.
|
||||
*
|
||||
* <p>The cursor can return {@link Decision#CONTINUE} to continue traversing.
|
||||
*
|
||||
* <p>{@link Decision#REMOVE_AND_EXIT} is used to remove the current element
|
||||
* and stop traversing.
|
||||
*
|
||||
* <p>Note: The {@link Decision#REMOVE} operation is not supported.
|
||||
*
|
||||
* @return The entry the cursor returned {@link Decision#EXIT} on, or null
|
||||
* if it continued till the end.
|
||||
*/
|
||||
public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor);
|
||||
|
||||
/**
|
||||
* Traverses the {@link Trie} in lexicographical order.
|
||||
* {@link Cursor#select(java.util.Map.Entry)} will be called on each entry.
|
||||
*
|
||||
* <p>The traversal will stop when the cursor returns {@link Decision#EXIT},
|
||||
* {@link Decision#CONTINUE} is used to continue traversing and
|
||||
* {@link Decision#REMOVE} is used to remove the element that was selected
|
||||
* and continue traversing.
|
||||
*
|
||||
* <p>{@link Decision#REMOVE_AND_EXIT} is used to remove the current element
|
||||
* and stop traversing.
|
||||
*
|
||||
* @return The entry the cursor returned {@link Decision#EXIT} on, or null
|
||||
* if it continued till the end.
|
||||
*/
|
||||
public Map.Entry<K,V> traverse(Cursor<? super K, ? super V> cursor);
|
||||
|
||||
/**
|
||||
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
|
||||
* by the given key.
|
||||
*
|
||||
* <p>In a {@link SortedTrie} with fixed size keys, this is essentially a
|
||||
* {@link #get(Object)} operation.
|
||||
*
|
||||
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael',
|
||||
* 'Analu', 'Andreas', 'Andrea', 'Andres', and 'Anatole', then
|
||||
* a lookup of 'And' would return 'Andreas', 'Andrea', and 'Andres'.
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key);
|
||||
|
||||
/**
|
||||
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
|
||||
* by the length of the key.
|
||||
*
|
||||
* <p>{@link SortedTrie}s with fixed size keys will not support this operation
|
||||
* (because all keys are the same length).
|
||||
*
|
||||
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael', 'Analu',
|
||||
* 'Andreas', 'Andrea', 'Andres', and 'Anatole', then a lookup for 'Andrey'
|
||||
* and a length of 4 would return 'Andreas', 'Andrea', and 'Andres'.
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key, int length);
|
||||
|
||||
/**
|
||||
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
|
||||
* by the key, starting at the given offset and for the given length.
|
||||
*
|
||||
* <p>{@link SortedTrie}s with fixed size keys will not support this operation
|
||||
* (because all keys are the same length).
|
||||
*
|
||||
* <p>For example, if the {@link SortedTrie} contains 'Anna', 'Anael', 'Analu',
|
||||
* 'Andreas', 'Andrea', 'Andres', and 'Anatole', then a lookup for
|
||||
* 'Hello Andrey Smith', an offset of 6 and a length of 4 would return
|
||||
* 'Andreas', 'Andrea', and 'Andres'.
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key, int offset, int length);
|
||||
|
||||
/**
|
||||
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
|
||||
* by the number of bits in the given Key.
|
||||
*
|
||||
* <p>In {@link SortedTrie}s with fixed size keys like IP addresses this method
|
||||
* can be used to lookup partial keys. That is you can lookup all addresses
|
||||
* that begin with '192.168' by providing the key '192.168.X.X' and a
|
||||
* length of 16.
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits);
|
||||
|
||||
/**
|
||||
* Returns a view of this {@link SortedTrie} of all elements that are prefixed
|
||||
* by the number of bits in the given Key.
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedByBits(K key, int offsetInBits, int lengthInBits);
|
||||
|
||||
/**
|
||||
* A {@link Cursor} can be used to traverse a {@link Trie}, visit each node
|
||||
* step by step and make {@link Decision}s on each step how to continue with
|
||||
* traversing the {@link Trie}.
|
||||
*/
|
||||
public interface Cursor<K, V> {
|
||||
|
||||
/**
|
||||
* The {@link Decision} tells the {@link Cursor} what to do on each step
|
||||
* while traversing the {@link Trie}.
|
||||
*
|
||||
* NOTE: Not all operations that work with a {@link Cursor} support all
|
||||
* {@link Decision} types
|
||||
*/
|
||||
public static enum Decision {
|
||||
|
||||
/**
|
||||
* Exit the traverse operation
|
||||
*/
|
||||
EXIT,
|
||||
|
||||
/**
|
||||
* Continue with the traverse operation
|
||||
*/
|
||||
CONTINUE,
|
||||
|
||||
/**
|
||||
* Remove the previously returned element
|
||||
* from the {@link Trie} and continue
|
||||
*/
|
||||
REMOVE,
|
||||
|
||||
/**
|
||||
* Remove the previously returned element
|
||||
* from the {@link Trie} and exit from the
|
||||
* traverse operation
|
||||
*/
|
||||
REMOVE_AND_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each {@link Entry} in the {@link Trie}. Return
|
||||
* {@link Decision#EXIT} to finish the {@link Trie} operation,
|
||||
* {@link Decision#CONTINUE} to go to the next {@link Entry},
|
||||
* {@link Decision#REMOVE} to remove the {@link Entry} and
|
||||
* continue iterating or {@link Decision#REMOVE_AND_EXIT} to
|
||||
* remove the {@link Entry} and stop iterating.
|
||||
*
|
||||
* Note: Not all operations support {@link Decision#REMOVE}.
|
||||
*/
|
||||
public Decision select(Map.Entry<? extends K, ? extends V> entry);
|
||||
}
|
||||
}
|
53
src/main/java/org/apache/commons/collections/TrieUtils.java
Normal file
53
src/main/java/org/apache/commons/collections/TrieUtils.java
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections;
|
||||
|
||||
import org.apache.commons.collections.trie.SynchronizedTrie;
|
||||
import org.apache.commons.collections.trie.UnmodifiableTrie;
|
||||
|
||||
/**
|
||||
* A collection of {@link Trie} utilities.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TrieUtils {
|
||||
|
||||
/**
|
||||
* {@link TrieUtils} should not normally be instantiated.
|
||||
*/
|
||||
private TrieUtils() {}
|
||||
|
||||
/**
|
||||
* Returns a synchronized instance of a {@link Trie}
|
||||
*
|
||||
* @see Collections#synchronizedMap(Map)
|
||||
*/
|
||||
public static <K, V> Trie<K, V> synchronizedTrie(Trie<K, V> trie) {
|
||||
return SynchronizedTrie.synchronizedTrie(trie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable instance of a {@link Trie}
|
||||
*
|
||||
* @see Collections#unmodifiableMap(Map)
|
||||
*/
|
||||
public static <K, V> Trie<K, V> unmodifiableTrie(Trie<K, V> trie) {
|
||||
return UnmodifiableTrie.unmodifiableTrie(trie);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* TODO: add javadoc
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractKeyAnalyzer<K> implements KeyAnalyzer<K> {
|
||||
|
||||
private static final long serialVersionUID = -20497563720380683L;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public int compare(K o1, K o2) {
|
||||
if (o1 == null) {
|
||||
return (o2 == null) ? 0 : -1;
|
||||
} else if (o2 == null) {
|
||||
return (o1 == null) ? 0 : 1;
|
||||
}
|
||||
|
||||
return ((Comparable<K>)o1).compareTo(o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bitIndex is a {@link KeyAnalyzer#OUT_OF_BOUNDS_BIT_KEY}
|
||||
*/
|
||||
static boolean isOutOfBoundsIndex(int bitIndex) {
|
||||
return bitIndex == OUT_OF_BOUNDS_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bitIndex is a {@link KeyAnalyzer#EQUAL_BIT_KEY}
|
||||
*/
|
||||
static boolean isEqualBitKey(int bitIndex) {
|
||||
return bitIndex == EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bitIndex is a {@link KeyAnalyzer#NULL_BIT_KEY}
|
||||
*/
|
||||
static boolean isNullBitKey(int bitIndex) {
|
||||
return bitIndex == NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given bitIndex is valid. Indices
|
||||
* are considered valid if they're between 0 and
|
||||
* {@link Integer#MAX_VALUE}
|
||||
*/
|
||||
static boolean isValidBitIndex(int bitIndex) {
|
||||
return 0 <= bitIndex && bitIndex <= Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections.Trie;
|
||||
|
||||
/**
|
||||
* This class provides some basic {@link Trie} functionality and
|
||||
* utility methods for actual {@link Trie} implementations.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
abstract class AbstractTrie<K, V> extends AbstractMap<K, V>
|
||||
implements Trie<K, V>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5826987063535505652L;
|
||||
|
||||
/**
|
||||
* The {@link KeyAnalyzer} that's being used to build the
|
||||
* PATRICIA {@link Trie}.
|
||||
*/
|
||||
protected final KeyAnalyzer<? super K> keyAnalyzer;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Trie} using the given {@link KeyAnalyzer}.
|
||||
*/
|
||||
public AbstractTrie(KeyAnalyzer<? super K> keyAnalyzer) {
|
||||
if (keyAnalyzer == null) {
|
||||
throw new NullPointerException("keyAnalyzer");
|
||||
}
|
||||
|
||||
this.keyAnalyzer = keyAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link KeyAnalyzer} that constructed the {@link Trie}.
|
||||
*/
|
||||
public KeyAnalyzer<? super K> getKeyAnalyzer() {
|
||||
return keyAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public K selectKey(K key) {
|
||||
Map.Entry<K, V> entry = select(key);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V selectValue(K key) {
|
||||
Map.Entry<K, V> entry = select(key);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("Trie[").append(size()).append("]={\n");
|
||||
for (Map.Entry<K, V> entry : entrySet()) {
|
||||
buffer.append(" ").append(entry).append("\n");
|
||||
}
|
||||
buffer.append("}\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to cast keys. It actually doesn't
|
||||
* cast anything. It's just fooling the compiler!
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
final K castKey(Object key) {
|
||||
return (K)key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given key in bits
|
||||
*
|
||||
* @see KeyAnalyzer#lengthInBits(Object)
|
||||
*/
|
||||
final int lengthInBits(K key) {
|
||||
if (key == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return keyAnalyzer.lengthInBits(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bits per element in the key
|
||||
*
|
||||
* @see KeyAnalyzer#bitsPerElement()
|
||||
*/
|
||||
final int bitsPerElement() {
|
||||
return keyAnalyzer.bitsPerElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given bit on the
|
||||
* key is set or false if the key is null.
|
||||
*
|
||||
* @see KeyAnalyzer#isBitSet(Object, int, int)
|
||||
*/
|
||||
final boolean isBitSet(K key, int bitIndex, int lengthInBits) {
|
||||
if (key == null) { // root's might be null!
|
||||
return false;
|
||||
}
|
||||
return keyAnalyzer.isBitSet(key, bitIndex, lengthInBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for calling {@link KeyAnalyzer#bitIndex(Object, int, int, Object, int, int)}
|
||||
*/
|
||||
final int bitIndex(K key, K foundKey) {
|
||||
return keyAnalyzer.bitIndex(key, 0, lengthInBits(key),
|
||||
foundKey, 0, lengthInBits(foundKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* An utility method for calling {@link KeyAnalyzer#compare(Object, Object)}
|
||||
*/
|
||||
final boolean compareKeys(K key, K other) {
|
||||
if (key == null) {
|
||||
return (other == null);
|
||||
} else if (other == null) {
|
||||
return (key == null);
|
||||
}
|
||||
|
||||
return keyAnalyzer.compare(key, other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if both values are either null or equal
|
||||
*/
|
||||
static boolean compare(Object a, Object b) {
|
||||
return (a == null ? b == null : a.equals(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic implementation of {@link Entry}
|
||||
*/
|
||||
abstract static class BasicEntry<K, V> implements Map.Entry<K, V>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -944364551314110330L;
|
||||
|
||||
protected K key;
|
||||
|
||||
protected V value;
|
||||
|
||||
private final int hashCode;
|
||||
|
||||
public BasicEntry(K key) {
|
||||
this.key = key;
|
||||
this.hashCode = (key != null ? key.hashCode() : 0);
|
||||
}
|
||||
|
||||
public BasicEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
|
||||
this.hashCode = (key != null ? key.hashCode() : 0)
|
||||
^ (value != null ? value.hashCode() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current key and value with the provided
|
||||
* key & value
|
||||
*/
|
||||
public V setKeyValue(K key, V value) {
|
||||
this.key = key;
|
||||
return setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V setValue(V value) {
|
||||
V previous = this.value;
|
||||
this.value = value;
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
} else if (!(o instanceof Map.Entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map.Entry<?, ?> other = (Map.Entry<?, ?>)o;
|
||||
if (compare(key, other.getKey())
|
||||
&& compare(value, other.getValue())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for byte[]s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ByteArrayKeyAnalyzer extends AbstractKeyAnalyzer<byte[]> {
|
||||
|
||||
private static final long serialVersionUID = 7382825097492285877L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link ByteArrayKeyAnalyzer}
|
||||
*/
|
||||
public static final ByteArrayKeyAnalyzer INSTANCE
|
||||
= new ByteArrayKeyAnalyzer(Integer.MAX_VALUE);
|
||||
|
||||
/**
|
||||
* The length of an {@link Byte} in bits
|
||||
*/
|
||||
public static final int LENGTH = Byte.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x80;
|
||||
|
||||
/**
|
||||
* A place holder for null
|
||||
*/
|
||||
private static final byte[] NULL = new byte[0];
|
||||
|
||||
/**
|
||||
* The maximum length of a key in bits
|
||||
*/
|
||||
private final int maxLengthInBits;
|
||||
|
||||
public ByteArrayKeyAnalyzer(int maxLengthInBits) {
|
||||
if (maxLengthInBits < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"maxLengthInBits=" + maxLengthInBits);
|
||||
}
|
||||
|
||||
this.maxLengthInBits = maxLengthInBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum length of a key in bits
|
||||
* @return the maximum key length in bits
|
||||
*/
|
||||
public int getMaxLengthInBits() {
|
||||
return maxLengthInBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(byte[] key) {
|
||||
return (key != null ? key.length * bitsPerElement() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(byte[] key, int bitIndex, int lengthInBits) {
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int prefix = maxLengthInBits - lengthInBits;
|
||||
int keyBitIndex = bitIndex - prefix;
|
||||
|
||||
if (keyBitIndex >= lengthInBits || keyBitIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = (int)(keyBitIndex / LENGTH);
|
||||
int bit = (int)(keyBitIndex % LENGTH);
|
||||
return (key[index] & mask(bit)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(byte[] key, int offsetInBits, int lengthInBits,
|
||||
byte[] other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (other == null) {
|
||||
other = NULL;
|
||||
}
|
||||
|
||||
boolean allNull = true;
|
||||
int length = Math.max(lengthInBits, otherLengthInBits);
|
||||
int prefix = maxLengthInBits - length;
|
||||
|
||||
if (prefix < 0) {
|
||||
return KeyAnalyzer.OUT_OF_BOUNDS_BIT_KEY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int index = prefix + (offsetInBits + i);
|
||||
boolean value = isBitSet(key, index, lengthInBits);
|
||||
|
||||
if (value) {
|
||||
allNull = false;
|
||||
}
|
||||
|
||||
int otherIndex = prefix + (otherOffsetInBits + i);
|
||||
boolean otherValue = isBitSet(other, otherIndex, otherLengthInBits);
|
||||
|
||||
if (value != otherValue) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if (allNull) {
|
||||
return KeyAnalyzer.NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(byte[] prefix, int offsetInBits,
|
||||
int lengthInBits, byte[] key) {
|
||||
|
||||
int keyLength = lengthInBits(key);
|
||||
if (lengthInBits > keyLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int elements = lengthInBits - offsetInBits;
|
||||
for (int i = 0; i < elements; i++) {
|
||||
if (isBitSet(prefix, i+offsetInBits, lengthInBits)
|
||||
!= isBitSet(key, i, keyLength)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compare(byte[] o1, byte[] o2) {
|
||||
if (o1 == null) {
|
||||
return (o2 == null) ? 0 : -1;
|
||||
} else if (o2 == null) {
|
||||
return (o1 == null) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (o1.length != o2.length) {
|
||||
return o1.length - o2.length;
|
||||
}
|
||||
|
||||
for (int i = 0; i < o1.length; i++) {
|
||||
int diff = (o1[i] & 0xFF) - (o2[i] & 0xFF);
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for {@link Byte}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ByteKeyAnalyzer extends AbstractKeyAnalyzer<Byte> {
|
||||
|
||||
private static final long serialVersionUID = 3395803342983289829L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link ByteKeyAnalyzer}
|
||||
*/
|
||||
public static final ByteKeyAnalyzer INSTANCE = new ByteKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The length of an {@link Byte} in bits
|
||||
*/
|
||||
public static final int LENGTH = Byte.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x80;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(Byte key) {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(Byte key, int bitIndex, int lengthInBits) {
|
||||
return (key & mask(bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(Byte key, int offsetInBits, int lengthInBits,
|
||||
Byte other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (offsetInBits != 0 || otherOffsetInBits != 0) {
|
||||
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
|
||||
+ ", otherOffsetInBits=" + otherOffsetInBits);
|
||||
}
|
||||
|
||||
byte keyValue = key.byteValue();
|
||||
if (keyValue == 0) {
|
||||
return NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
byte otherValue = (other != null ? other.byteValue() : 0);
|
||||
|
||||
if (keyValue != otherValue) {
|
||||
int xorValue = keyValue ^ otherValue;
|
||||
for (int i = 0; i < LENGTH; i++) {
|
||||
if ((xorValue & mask(i)) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(Byte prefix, int offsetInBits,
|
||||
int lengthInBits, Byte key) {
|
||||
|
||||
int value1 = (prefix.byteValue() << offsetInBits);
|
||||
int value2 = key.byteValue();
|
||||
|
||||
int mask = 0;
|
||||
for (int i = 0; i < lengthInBits; i++) {
|
||||
mask |= (0x1 << i);
|
||||
}
|
||||
|
||||
return (value1 & mask) == (value2 & mask);
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* An {@link KeyAnalyzer} for {@code char[]}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CharArrayKeyAnalyzer extends AbstractKeyAnalyzer<char[]> {
|
||||
|
||||
private static final long serialVersionUID = -8167897361549463457L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link CharArrayKeyAnalyzer}
|
||||
*/
|
||||
public static final CharArrayKeyAnalyzer INSTANCE = new CharArrayKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The number of bits per {@link Character}
|
||||
*/
|
||||
public static final int LENGTH = Character.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x8000;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(char[] key) {
|
||||
return (key != null ? key.length * LENGTH : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(char[] key, int offsetInBits, int lengthInBits,
|
||||
char[] other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
boolean allNull = true;
|
||||
|
||||
if (offsetInBits % LENGTH != 0 || otherOffsetInBits % LENGTH != 0
|
||||
|| lengthInBits % LENGTH != 0 || otherLengthInBits % LENGTH != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The offsets and lengths must be at Character boundaries");
|
||||
}
|
||||
|
||||
|
||||
int beginIndex1 = offsetInBits / LENGTH;
|
||||
int beginIndex2 = otherOffsetInBits / LENGTH;
|
||||
|
||||
int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
|
||||
int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
|
||||
|
||||
int length = Math.max(endIndex1, endIndex2);
|
||||
|
||||
// Look at each character, and if they're different
|
||||
// then figure out which bit makes the difference
|
||||
// and return it.
|
||||
char k = 0, f = 0;
|
||||
for(int i = 0; i < length; i++) {
|
||||
int index1 = beginIndex1 + i;
|
||||
int index2 = beginIndex2 + i;
|
||||
|
||||
if (index1 >= endIndex1) {
|
||||
k = 0;
|
||||
} else {
|
||||
k = key[index1];
|
||||
}
|
||||
|
||||
if (other == null || index2 >= endIndex2) {
|
||||
f = 0;
|
||||
} else {
|
||||
f = other[index2];
|
||||
}
|
||||
|
||||
if (k != f) {
|
||||
int x = k ^ f;
|
||||
return i * LENGTH + (Integer.numberOfLeadingZeros(x) - LENGTH);
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
allNull = false;
|
||||
}
|
||||
}
|
||||
|
||||
// All bits are 0
|
||||
if (allNull) {
|
||||
return KeyAnalyzer.NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
// Both keys are equal
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(char[] key, int bitIndex, int lengthInBits) {
|
||||
if (key == null || bitIndex >= lengthInBits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = (int)(bitIndex / LENGTH);
|
||||
int bit = (int)(bitIndex % LENGTH);
|
||||
|
||||
return (key[index] & mask(bit)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(char[] prefix, int offsetInBits,
|
||||
int lengthInBits, char[] key) {
|
||||
if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot determine prefix outside of Character boundaries");
|
||||
}
|
||||
|
||||
int off = offsetInBits / LENGTH;
|
||||
int len = lengthInBits / LENGTH;
|
||||
for (int i = 0; i < len; i ++) {
|
||||
if (prefix[i + off] != key[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for {@link Character}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CharacterKeyAnalyzer extends AbstractKeyAnalyzer<Character> {
|
||||
|
||||
private static final long serialVersionUID = 3928565962744720753L;
|
||||
|
||||
/**
|
||||
* A singleton instance of the {@link CharacterKeyAnalyzer}.
|
||||
*/
|
||||
public static final CharacterKeyAnalyzer INSTANCE
|
||||
= new CharacterKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The length of a {@link Character} in bits
|
||||
*/
|
||||
public static final int LENGTH = Character.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x8000;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(Character key) {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(Character key, int bitIndex, int lengthInBits) {
|
||||
return (key & mask(bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(Character key, int offsetInBits, int lengthInBits,
|
||||
Character other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (offsetInBits != 0 || otherOffsetInBits != 0) {
|
||||
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
|
||||
+ ", otherOffsetInBits=" + otherOffsetInBits);
|
||||
}
|
||||
|
||||
char keyValue = key.charValue();
|
||||
if (keyValue == Character.MIN_VALUE) {
|
||||
return NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
if (other == null) {
|
||||
other = Character.MIN_VALUE;
|
||||
}
|
||||
|
||||
char otherValue = (other != null ? other.charValue() : Character.MIN_VALUE);
|
||||
|
||||
if (keyValue != otherValue) {
|
||||
int xorValue = keyValue ^ otherValue;
|
||||
for (int i = 0; i < LENGTH; i++) {
|
||||
if ((xorValue & mask(i)) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(Character prefix, int offsetInBits,
|
||||
int lengthInBits, Character key) {
|
||||
|
||||
int value1 = (prefix.charValue() << offsetInBits);
|
||||
int value2 = key.charValue();
|
||||
|
||||
int mask = 0;
|
||||
for(int i = 0; i < lengthInBits; i++) {
|
||||
mask |= (0x1 << i);
|
||||
}
|
||||
|
||||
return (value1 & mask) == (value2 & mask);
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for {@link Integer}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class IntegerKeyAnalyzer extends AbstractKeyAnalyzer<Integer> {
|
||||
|
||||
private static final long serialVersionUID = 4928508653722068982L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link IntegerKeyAnalyzer}
|
||||
*/
|
||||
public static final IntegerKeyAnalyzer INSTANCE = new IntegerKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The length of an {@link Integer} in bits
|
||||
*/
|
||||
public static final int LENGTH = Integer.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x80000000;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(Integer key) {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(Integer key, int bitIndex, int lengthInBits) {
|
||||
return (key & mask(bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(Integer key, int offsetInBits, int lengthInBits,
|
||||
Integer other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (offsetInBits != 0 || otherOffsetInBits != 0) {
|
||||
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
|
||||
+ ", otherOffsetInBits=" + otherOffsetInBits);
|
||||
}
|
||||
|
||||
int keyValue = key.intValue();
|
||||
if (keyValue == 0) {
|
||||
return NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
int otherValue = (other != null ? other.intValue() : 0);
|
||||
|
||||
if (keyValue != otherValue) {
|
||||
int xorValue = keyValue ^ otherValue;
|
||||
for (int i = 0; i < LENGTH; i++) {
|
||||
if ((xorValue & mask(i)) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(Integer prefix, int offsetInBits,
|
||||
int lengthInBits, Integer key) {
|
||||
|
||||
int value1 = (prefix.intValue() << offsetInBits);
|
||||
int value2 = key.intValue();
|
||||
|
||||
int mask = 0;
|
||||
for (int i = 0; i < lengthInBits; i++) {
|
||||
mask |= (0x1 << i);
|
||||
}
|
||||
|
||||
return (value1 & mask) == (value2 & mask);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Defines the interface to analyze {@link Trie} keys on a bit level.
|
||||
* {@link KeyAnalyzer}'s methods return the length of the key in bits,
|
||||
* whether or not a bit is set, and bits per element in the key.
|
||||
*
|
||||
* <p>Additionally, a method determines if a key is a prefix of another
|
||||
* key and returns the bit index where one key is different from another
|
||||
* key (if the key and found key are equal than the return value is
|
||||
* {@link #EQUAL_BIT_KEY}).
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface KeyAnalyzer<K> extends Comparator<K>, Serializable {
|
||||
|
||||
/**
|
||||
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
|
||||
* if key's bits are all 0
|
||||
*/
|
||||
public static final int NULL_BIT_KEY = -1;
|
||||
|
||||
/**
|
||||
* Returned by {@link #bitIndex(Object, int, int, Object, int, int)}
|
||||
* if key and found key are equal. This is a very very specific case
|
||||
* and shouldn't happen on a regular basis
|
||||
*/
|
||||
public static final int EQUAL_BIT_KEY = -2;
|
||||
|
||||
public static final int OUT_OF_BOUNDS_BIT_KEY = -3;
|
||||
|
||||
/**
|
||||
* Returns the number of bits per element in the key.
|
||||
* This is only useful for variable-length keys, such as Strings.
|
||||
*/
|
||||
public int bitsPerElement();
|
||||
|
||||
/**
|
||||
* Returns the length of the Key in bits.
|
||||
*/
|
||||
public int lengthInBits(K key);
|
||||
|
||||
/**
|
||||
* Returns whether or not a bit is set
|
||||
*/
|
||||
public boolean isBitSet(K key, int bitIndex, int lengthInBits);
|
||||
|
||||
/**
|
||||
* Returns the n-th different bit between key and found.
|
||||
* This starts the comparison in key at 'keyStart' and goes
|
||||
* for 'keyLength' bits, and compares to the found key
|
||||
* starting at 'foundStart' and going for 'foundLength' bits.
|
||||
*/
|
||||
public int bitIndex(K key, int offsetInBits, int lengthInBits,
|
||||
K other, int otherOffsetInBits, int otherLengthInBits);
|
||||
|
||||
/**
|
||||
* Determines whether or not the given prefix (from offset to length)
|
||||
* is a prefix of the given key.
|
||||
*/
|
||||
public boolean isPrefix(K prefix, int offsetInBits, int lengthInBits, K key);
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for {@link Long}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LongKeyAnalyzer extends AbstractKeyAnalyzer<Long> {
|
||||
|
||||
private static final long serialVersionUID = -4119639247588227409L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link LongKeyAnalyzer}
|
||||
*/
|
||||
public static final LongKeyAnalyzer INSTANCE = new LongKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The length of an {@link Long} in bits
|
||||
*/
|
||||
public static final int LENGTH = Long.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final long MSB = 0x8000000000000000L;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static long mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(Long key) {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(Long key, int bitIndex, int lengthInBits) {
|
||||
return (key & mask(bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(Long key, int offsetInBits, int lengthInBits,
|
||||
Long other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (offsetInBits != 0 || otherOffsetInBits != 0) {
|
||||
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
|
||||
+ ", otherOffsetInBits=" + otherOffsetInBits);
|
||||
}
|
||||
|
||||
long keyValue = key.longValue();
|
||||
if (keyValue == 0L) {
|
||||
return NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
long otherValue = (other != null ? other.longValue() : 0L);
|
||||
|
||||
if (keyValue != otherValue) {
|
||||
long xorValue = keyValue ^ otherValue;
|
||||
for (int i = 0; i < LENGTH; i++) {
|
||||
if ((xorValue & mask(i)) != 0L) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(Long prefix, int offsetInBits,
|
||||
int lengthInBits, Long key) {
|
||||
|
||||
long value1 = (prefix.longValue() << offsetInBits);
|
||||
long value2 = key.longValue();
|
||||
|
||||
long mask = 0L;
|
||||
for (int i = 0; i < lengthInBits; i++) {
|
||||
mask |= (0x1L << i);
|
||||
}
|
||||
|
||||
return (value1 & mask) == (value2 & mask);
|
||||
}
|
||||
}
|
1447
src/main/java/org/apache/commons/collections/trie/PatriciaTrie.java
Normal file
1447
src/main/java/org/apache/commons/collections/trie/PatriciaTrie.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* A {@link KeyAnalyzer} for {@link Short}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ShortKeyAnalyzer implements KeyAnalyzer<Short> {
|
||||
|
||||
private static final long serialVersionUID = -8631376733513512017L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link ShortKeyAnalyzer}
|
||||
*/
|
||||
public static final ShortKeyAnalyzer INSTANCE = new ShortKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The length of an {@link Short} in bits
|
||||
*/
|
||||
public static final int LENGTH = Short.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x8000;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(Short key) {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(Short key, int bitIndex, int lengthInBits) {
|
||||
return (key & mask(bitIndex)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(Short key, int offsetInBits, int lengthInBits,
|
||||
Short other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
|
||||
if (offsetInBits != 0 || otherOffsetInBits != 0) {
|
||||
throw new IllegalArgumentException("offsetInBits=" + offsetInBits
|
||||
+ ", otherOffsetInBits=" + otherOffsetInBits);
|
||||
}
|
||||
|
||||
int keyValue = key.shortValue();
|
||||
if (keyValue == 0) {
|
||||
return NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
int otherValue = (other != null ? other.shortValue() : 0);
|
||||
|
||||
if (keyValue != otherValue) {
|
||||
int xorValue = keyValue ^ otherValue;
|
||||
for (int i = 0; i < LENGTH; i++) {
|
||||
if ((xorValue & mask(i)) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int compare(Short o1, Short o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(Short prefix, int offsetInBits,
|
||||
int lengthInBits, Short key) {
|
||||
|
||||
int value1 = (prefix.shortValue() << offsetInBits);
|
||||
int value2 = key.shortValue();
|
||||
|
||||
int mask = 0;
|
||||
for (int i = 0; i < lengthInBits; i++) {
|
||||
mask |= (0x1 << i);
|
||||
}
|
||||
|
||||
return (value1 & mask) == (value2 & mask);
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
/**
|
||||
* An {@link KeyAnalyzer} for {@link String}s.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class StringKeyAnalyzer extends AbstractKeyAnalyzer<String> {
|
||||
|
||||
private static final long serialVersionUID = -7032449491269434877L;
|
||||
|
||||
/**
|
||||
* A singleton instance of {@link StringKeyAnalyzer}
|
||||
*/
|
||||
public static final StringKeyAnalyzer INSTANCE = new StringKeyAnalyzer();
|
||||
|
||||
/**
|
||||
* The number of bits per {@link Character}
|
||||
*/
|
||||
public static final int LENGTH = Character.SIZE;
|
||||
|
||||
/**
|
||||
* A bit mask where the first bit is 1 and the others are zero
|
||||
*/
|
||||
private static final int MSB = 0x8000;
|
||||
|
||||
/**
|
||||
* Returns a bit mask where the given bit is set
|
||||
*/
|
||||
private static int mask(int bit) {
|
||||
return MSB >>> bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitsPerElement() {
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int lengthInBits(String key) {
|
||||
return (key != null ? key.length() * LENGTH : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int bitIndex(String key, int offsetInBits, int lengthInBits,
|
||||
String other, int otherOffsetInBits, int otherLengthInBits) {
|
||||
boolean allNull = true;
|
||||
|
||||
if (offsetInBits % LENGTH != 0 || otherOffsetInBits % LENGTH != 0
|
||||
|| lengthInBits % LENGTH != 0 || otherLengthInBits % LENGTH != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The offsets and lengths must be at Character boundaries");
|
||||
}
|
||||
|
||||
|
||||
int beginIndex1 = offsetInBits / LENGTH;
|
||||
int beginIndex2 = otherOffsetInBits / LENGTH;
|
||||
|
||||
int endIndex1 = beginIndex1 + lengthInBits / LENGTH;
|
||||
int endIndex2 = beginIndex2 + otherLengthInBits / LENGTH;
|
||||
|
||||
int length = Math.max(endIndex1, endIndex2);
|
||||
|
||||
// Look at each character, and if they're different
|
||||
// then figure out which bit makes the difference
|
||||
// and return it.
|
||||
char k = 0, f = 0;
|
||||
for(int i = 0; i < length; i++) {
|
||||
int index1 = beginIndex1 + i;
|
||||
int index2 = beginIndex2 + i;
|
||||
|
||||
if (index1 >= endIndex1) {
|
||||
k = 0;
|
||||
} else {
|
||||
k = key.charAt(index1);
|
||||
}
|
||||
|
||||
if (other == null || index2 >= endIndex2) {
|
||||
f = 0;
|
||||
} else {
|
||||
f = other.charAt(index2);
|
||||
}
|
||||
|
||||
if (k != f) {
|
||||
int x = k ^ f;
|
||||
return i * LENGTH + (Integer.numberOfLeadingZeros(x) - LENGTH);
|
||||
}
|
||||
|
||||
if (k != 0) {
|
||||
allNull = false;
|
||||
}
|
||||
}
|
||||
|
||||
// All bits are 0
|
||||
if (allNull) {
|
||||
return KeyAnalyzer.NULL_BIT_KEY;
|
||||
}
|
||||
|
||||
// Both keys are equal
|
||||
return KeyAnalyzer.EQUAL_BIT_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isBitSet(String key, int bitIndex, int lengthInBits) {
|
||||
if (key == null || bitIndex >= lengthInBits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = (int)(bitIndex / LENGTH);
|
||||
int bit = (int)(bitIndex % LENGTH);
|
||||
|
||||
return (key.charAt(index) & mask(bit)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isPrefix(String prefix, int offsetInBits,
|
||||
int lengthInBits, String key) {
|
||||
if (offsetInBits % LENGTH != 0 || lengthInBits % LENGTH != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot determine prefix outside of Character boundaries");
|
||||
}
|
||||
|
||||
String s1 = prefix.substring(offsetInBits / LENGTH, lengthInBits / LENGTH);
|
||||
return key.startsWith(s1);
|
||||
}
|
||||
}
|
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import org.apache.commons.collections.Trie;
|
||||
import org.apache.commons.collections.collection.SynchronizedCollection;
|
||||
import org.apache.commons.collections.set.SynchronizedSet;
|
||||
|
||||
/**
|
||||
* A synchronized {@link Trie}.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SynchronizedTrie<K, V> implements Trie<K, V>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3121878833178676939L;
|
||||
|
||||
private final Trie<K, V> delegate;
|
||||
|
||||
/**
|
||||
* Factory method to create a synchronized trie.
|
||||
*
|
||||
* @param trie the trie to decorate, must not be null
|
||||
* @return a new synchronized trie
|
||||
* @throws IllegalArgumentException if trie is null
|
||||
*/
|
||||
public static <K, V> SynchronizedTrie<K, V> synchronizedTrie(Trie<K, V> trie) {
|
||||
return new SynchronizedTrie<K, V>(trie);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
*
|
||||
* @param trie the trie to decorate, must not be null
|
||||
* @throws IllegalArgumentException if set is null
|
||||
*/
|
||||
public SynchronizedTrie(Trie<K, V> trie) {
|
||||
if (trie == null) {
|
||||
throw new IllegalArgumentException("Collection must not be null");
|
||||
}
|
||||
this.delegate = trie;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Entry<K, V> select(K key,
|
||||
Cursor<? super K, ? super V> cursor) {
|
||||
return delegate.select(key, cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Entry<K, V> select(K key) {
|
||||
return delegate.select(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized K selectKey(K key) {
|
||||
return delegate.selectKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized V selectValue(K key) {
|
||||
return delegate.selectValue(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Entry<K, V> traverse(Cursor<? super K, ? super V> cursor) {
|
||||
return delegate.traverse(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Set<Entry<K, V>> entrySet() {
|
||||
return SynchronizedSet.synchronizedSet(delegate.entrySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Set<K> keySet() {
|
||||
return SynchronizedSet.synchronizedSet(delegate.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Collection<V> values() {
|
||||
return SynchronizedCollection.synchronizedCollection(delegate.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized V put(K key, V value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized void putAll(Map<? extends K, ? extends V> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized V remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized K lastKey() {
|
||||
return delegate.lastKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> subMap(K fromKey, K toKey) {
|
||||
return Collections.synchronizedSortedMap(delegate.subMap(fromKey, toKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> tailMap(K fromKey) {
|
||||
return Collections.synchronizedSortedMap(delegate.tailMap(fromKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized Comparator<? super K> comparator() {
|
||||
return delegate.comparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized K firstKey() {
|
||||
return delegate.firstKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> headMap(K toKey) {
|
||||
return Collections.synchronizedSortedMap(delegate.headMap(toKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> getPrefixedBy(K key, int offset, int length) {
|
||||
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, offset, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> getPrefixedBy(K key, int length) {
|
||||
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> getPrefixedBy(K key) {
|
||||
return Collections.synchronizedSortedMap(delegate.getPrefixedBy(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits) {
|
||||
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, lengthInBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized SortedMap<K, V> getPrefixedByBits(K key,
|
||||
int offsetInBits, int lengthInBits) {
|
||||
return Collections.synchronizedSortedMap(delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public synchronized int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,301 @@
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import org.apache.commons.collections.Trie;
|
||||
|
||||
/**
|
||||
* An unmodifiable {@link Trie}.
|
||||
*
|
||||
* @since 4.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class UnmodifiableTrie<K, V> implements Trie<K, V>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7156426030315945159L;
|
||||
|
||||
private final Trie<K, V> delegate;
|
||||
|
||||
/**
|
||||
* Factory method to create a unmodifiable trie.
|
||||
*
|
||||
* @param trie the trie to decorate, must not be null
|
||||
* @return a new unmodifiable trie
|
||||
* @throws IllegalArgumentException if trie is null
|
||||
*/
|
||||
public static <K, V> UnmodifiableTrie<K, V> unmodifiableTrie(Trie<K, V> trie) {
|
||||
return new UnmodifiableTrie<K, V>(trie);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor that wraps (not copies).
|
||||
*
|
||||
* @param trie the trie to decorate, must not be null
|
||||
* @throws IllegalArgumentException if set is null
|
||||
*/
|
||||
public UnmodifiableTrie(Trie<K, V> trie) {
|
||||
if (trie == null) {
|
||||
throw new IllegalArgumentException("Collection must not be null");
|
||||
}
|
||||
this.delegate = trie;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Entry<K, V> select(K key, final Cursor<? super K, ? super V> cursor) {
|
||||
Cursor<K, V> c = new Cursor<K, V>() {
|
||||
public Decision select(Map.Entry<? extends K, ? extends V> entry) {
|
||||
Decision decision = cursor.select(entry);
|
||||
switch (decision) {
|
||||
case REMOVE:
|
||||
case REMOVE_AND_EXIT:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
// other decisions are fine
|
||||
break;
|
||||
}
|
||||
|
||||
return decision;
|
||||
}
|
||||
};
|
||||
|
||||
return delegate.select(key, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Entry<K, V> select(K key) {
|
||||
return delegate.select(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public K selectKey(K key) {
|
||||
return delegate.selectKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V selectValue(K key) {
|
||||
return delegate.selectValue(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Entry<K, V> traverse(final Cursor<? super K, ? super V> cursor) {
|
||||
Cursor<K, V> c = new Cursor<K, V>() {
|
||||
public Decision select(Map.Entry<? extends K, ? extends V> entry) {
|
||||
Decision decision = cursor.select(entry);
|
||||
switch (decision) {
|
||||
case REMOVE:
|
||||
case REMOVE_AND_EXIT:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
// other decisions are fine
|
||||
break;
|
||||
}
|
||||
|
||||
return decision;
|
||||
}
|
||||
};
|
||||
|
||||
return delegate.traverse(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return Collections.unmodifiableSet(delegate.entrySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Set<K> keySet() {
|
||||
return Collections.unmodifiableSet(delegate.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Collection<V> values() {
|
||||
return Collections.unmodifiableCollection(delegate.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V put(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public V remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public K firstKey() {
|
||||
return delegate.firstKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> headMap(K toKey) {
|
||||
return Collections.unmodifiableSortedMap(delegate.headMap(toKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public K lastKey() {
|
||||
return delegate.lastKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> subMap(K fromKey, K toKey) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.subMap(fromKey, toKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> tailMap(K fromKey) {
|
||||
return Collections.unmodifiableSortedMap(delegate.tailMap(fromKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key, int offset, int length) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.getPrefixedBy(key, offset, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key, int length) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.getPrefixedBy(key, length));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedBy(K key) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.getPrefixedBy(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedByBits(K key, int lengthInBits) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.getPrefixedByBits(key, lengthInBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SortedMap<K, V> getPrefixedByBits(K key, int offsetInBits,
|
||||
int lengthInBits) {
|
||||
return Collections.unmodifiableSortedMap(
|
||||
delegate.getPrefixedByBits(key, offsetInBits, lengthInBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Comparator<? super K> comparator() {
|
||||
return delegate.comparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* This package contains implementations of the
|
||||
* {@link org.apache.commons.collections.Trie Trie} interface.
|
||||
* <p>
|
||||
* The implementations are in the form of direct implementations and decorators.
|
||||
* A decorator wraps another implementation of the interface to add some
|
||||
* specific additional functionality.
|
||||
* <p>
|
||||
* The following implementations are provided in the package:
|
||||
* <ul>
|
||||
* <li>PatriciaTrie - an implementation of a PATRICIA trie
|
||||
* </ul>
|
||||
* <p>
|
||||
* The following decorators are provided:
|
||||
* <ul>
|
||||
* <li>Synchronized - synchronizes method access for multi-threaded environments
|
||||
* <li>Unmodifiable - ensures the collection cannot be altered
|
||||
* </ul>
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.collections.trie;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ByteArrayKeyAnalyzerTest {
|
||||
|
||||
private static final int SIZE = 20000;
|
||||
|
||||
@Test
|
||||
public void bitSet() {
|
||||
byte[] key = toByteArray("10100110", 2);
|
||||
ByteArrayKeyAnalyzer ka = new ByteArrayKeyAnalyzer(key.length * 8);
|
||||
int length = ka.lengthInBits(key);
|
||||
|
||||
Assert.assertTrue(ka.isBitSet(key, 0, length));
|
||||
Assert.assertFalse(ka.isBitSet(key, 1, length));
|
||||
Assert.assertTrue(ka.isBitSet(key, 2, length));
|
||||
Assert.assertFalse(ka.isBitSet(key, 3, length));
|
||||
Assert.assertFalse(ka.isBitSet(key, 4, length));
|
||||
Assert.assertTrue(ka.isBitSet(key, 5, length));
|
||||
Assert.assertTrue(ka.isBitSet(key, 6, length));
|
||||
Assert.assertFalse(ka.isBitSet(key, 7, length));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keys() {
|
||||
PatriciaTrie<byte[], BigInteger> trie
|
||||
= new PatriciaTrie<byte[], BigInteger>(ByteArrayKeyAnalyzer.INSTANCE);
|
||||
|
||||
Map<byte[], BigInteger> map
|
||||
= new TreeMap<byte[], BigInteger>(ByteArrayKeyAnalyzer.INSTANCE);
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
BigInteger value = BigInteger.valueOf(i);
|
||||
byte[] key = toByteArray(value);
|
||||
|
||||
BigInteger existing = trie.put(key, value);
|
||||
Assert.assertNull(existing);
|
||||
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
Assert.assertEquals(map.size(), trie.size());
|
||||
|
||||
for (byte[] key : map.keySet()) {
|
||||
BigInteger expected = new BigInteger(1, key);
|
||||
BigInteger value = trie.get(key);
|
||||
|
||||
Assert.assertEquals(expected, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefix() {
|
||||
byte[] prefix = toByteArray("00001010", 2);
|
||||
byte[] key1 = toByteArray("11001010", 2);
|
||||
byte[] key2 = toByteArray("10101100", 2);
|
||||
|
||||
ByteArrayKeyAnalyzer keyAnalyzer = new ByteArrayKeyAnalyzer(key1.length * 8);
|
||||
|
||||
int prefixLength = keyAnalyzer.lengthInBits(prefix);
|
||||
|
||||
Assert.assertFalse(keyAnalyzer.isPrefix(prefix, 4, prefixLength, key1));
|
||||
Assert.assertTrue(keyAnalyzer.isPrefix(prefix, 4, prefixLength, key2));
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(String value, int radix) {
|
||||
return toByteArray(Long.parseLong(value, radix));
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(long value) {
|
||||
return toByteArray(BigInteger.valueOf(value));
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(BigInteger value) {
|
||||
byte[] src = value.toByteArray();
|
||||
if (src.length <= 1) {
|
||||
return src;
|
||||
}
|
||||
|
||||
if (src[0] != 0) {
|
||||
return src;
|
||||
}
|
||||
|
||||
byte[] dst = new byte[src.length-1];
|
||||
System.arraycopy(src, 1, dst, 0, dst.length);
|
||||
return dst;
|
||||
}
|
||||
}
|
1125
src/test/java/org/apache/commons/collections/trie/PatriciaTrieTest.java
Executable file
1125
src/test/java/org/apache/commons/collections/trie/PatriciaTrieTest.java
Executable file
File diff suppressed because it is too large
Load Diff
6047
src/test/resources/org/apache/commons/collections/trie/hamlet.txt
Executable file
6047
src/test/resources/org/apache/commons/collections/trie/hamlet.txt
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user