/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.collections; import java.util.AbstractMap; import java.util.Comparator; import java.util.Set; import xxl.core.binarySearchTrees.BinarySearchTree; import xxl.core.comparators.ComparableComparator; import xxl.core.functions.Function; import xxl.core.functions.Identity; import xxl.core.predicates.Predicates; /** * This class provides an implementation of the Map interface that * internally uses a binary search tree to store its key-value mappings. * <p> * * The performance of the set depends on the performance of the internally * used binary search tree (e.g., an avl tree guaratees that insertion, * removal and searching requires logarithmic time, but binary search * trees can be degenerated).<p> * * The iterators returned by the <tt>iterator</tt> method of the set * returned by this class' <tt>entrySet</tt> and <tt>keySet</tt> method * are <i>fail-fast</i>: if the set is structurally modified at any time * after the iterator is created, in any way except through the iterator's * own <tt>remove</tt> method, the iterator will throw a * ConcurrentModificationException. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than * risking arbitrary, non-deterministic behavior at an undetermined time * in the future.<p> * * Example usage (1). * <pre> // create a binary search tree map that is using a binary search // tree and the natural ordering of the elements BinarySearchTreeMap map = new BinarySearchTreeMap(); // create a new iterator with 100 random number lower than 1000 xxl.core.cursors.Cursor cursor = new DiscreteRandomNumber(new JavaDiscreteRandomWrapper(1000), 100); // insert all elements of the given iterator while (cursor.hasNext()) map.put(cursor.peek(), cursor.next()); // create an iteration of the elements of the set java.util.Iterator iterator = map.entrySet().iterator(); // print all elements of the iteration (set) while (iterator.hasNext()) { MapEntry entry = (MapEntry)iterator.next(); System.out.println("key \t= "+entry.getKey()+"\t & value \t= "+entry.getValue()); } System.out.println(); </pre> * * Example usage (2). * <pre> // create a binary search tree map that is using a avl tree and // the natural ordering of the elements map = new BinarySearchTreeMap(new AbstractFunction() { public Object invoke (Object f1, Object f2) { return AVLTree.FACTORY_METHOD.invoke(f1, f2); } }); // create a new iterator with the numbers from 0 to 100 cursor = new xxl.core.cursors.sources.Enumerator(101); // insert all elements of the given iterator while (cursor.hasNext()) map.put( cursor.peek(), new Integer(100 * ((Integer)cursor.next()).intValue()) ); // create an iteration of the elements of the set iterator = map.entrySet().iterator(); // print all elements of the iteration (set) while (iterator.hasNext()) { MapEntry entry = (MapEntry)iterator.next(); System.out.println("key \t= "+entry.getKey()+"\t & value \t= "+entry.getValue()); } System.out.println(); </pre> * * @see AbstractMap * @see BinarySearchTree * @see BinarySearchTreeSet * @see ComparableComparator * @see Comparator * @see Function * @see MapEntry * @see Set */ public class BinarySearchTreeMap extends AbstractMap { /** * The binary search tree is internally used to store the elements of * the map. */ protected BinarySearchTree tree; /** * The comparator to determine the order of the map (and the * internally used binary search tree). More exactly, there can be * three different cases when two entries <tt>e1</tt> and <tt>e2</tt> * are put into the map * <ul> * <dl> * <dt><li><tt>comparator.compare(e1.getKey(), e2.getKey()) < 0</tt> :</dt> * <dd>the map returns <tt>e1</tt> prior to returning <tt>e2</tt>.</dd> * <dt><li><tt>comparator.compare(e1.getKey(), e2.getKey()) == 0</tt> :</dt> * <dd>the entry <tt>e1</tt> is replaced by the entry <tt>e2</tt>.</dd> * <dt><li><tt>comparator.compare(e1.getKey(), e2.getKey()) > 0</tt> :</dt> * <dd>the map returns <tt>e2</tt> prior to returning <tt>e1</tt>.</dd> * </dl> * </ul> */ protected Comparator comparator; /** * The comparator to determine the subtree of a node of a binary * search tree that would be used to insert a given entry. The * compare method of the comparator is called with an array * (<i>parameter list</i>) of objects (to insert) and a node of a * binary search tree. */ protected Comparator chooseSubtree = new Comparator () { public int compare (Object object0, Object object1) { return comparator.compare(((MapEntry)((Object[]) object0)[0]).getKey(), ((MapEntry)((BinarySearchTree.Node)object1).object()).getKey()); } }; /** * Constructs a new binary search tree map that initializes the * internally used binary search tree with a new tree and uses the * specified comparator to order entries according to their keys when * inserted. The specified function is used to create a new binary * search tree (it works like the * {@link BinarySearchTree#FACTORY_METHOD FACTORY_METHOD} of * BinarySearchTree). * * @param comparator the comparator to determine the order of the * map. * @param newBinarySearchTree a function to create a new binary search * tree. */ public BinarySearchTreeMap (Comparator comparator, Function newBinarySearchTree) { (this.tree = (BinarySearchTree) newBinarySearchTree.invoke( Predicates.TRUE, Identity.DEFAULT_INSTANCE) ).clear(); this.comparator = comparator; } /** * Constructs a new binary search tree map that uses the specified * comparator to order entries according to their keys when inserted. * The internally used binary search tree is initialized by the * FACTORY_METHOD of BinarySearchTree). This constructor is equivalent * to the call of * <code>BinarySearchTreeMap(comparator, BinarySearchTree.FACTORY_METHOD)</code>. * * @param comparator the comparator to determine the order of the * map. * @see BinarySearchTree#FACTORY_METHOD */ public BinarySearchTreeMap (Comparator comparator) { this(comparator, BinarySearchTree.FACTORY_METHOD); } /** * Constructs a new binary search tree map that initializes the * internally used binary search tree with a new tree and uses the * <i>natural ordering</i> of its entries according to their keys to * order them. The specified function is used to create a new binary * search tree (it works like the * {@link BinarySearchTree#FACTORY_METHOD FACTORY_METHOD} of * BinarySearchTree). This constructor is equivalent to the call of * <code>BinarySearchTreeMap(ComparableComparator.DEFAULT_INSTANCE, newBinarySearchTree)</code>. * * @param newBinarySearchTree a function to create a new binary search * tree. */ public BinarySearchTreeMap (Function newBinarySearchTree) { this(new ComparableComparator(), newBinarySearchTree); } /** * Constructs a new binary search tree map that uses the <i>natural * ordering</i> of its entries according to their keys to order them. * This constructor is equivalent to the call of * <code>BinarySearchTreeMap(ComparableComparator.DEFAULT_INSTANCE, BinarySearchTree.FACTORY_METHOD)</code>. * * @see BinarySearchTree#FACTORY_METHOD */ public BinarySearchTreeMap () { this(new ComparableComparator()); } /** * Returns the number of key-value mappings in this map. If the map * contains more than <tt>Integer.MAX_VALUE</tt> elements, * <tt>Integer.MAX_VALUE</tt> is returned.<br> * This implementation returns <code>tree.size()</code>. * * @return the number of key-value mappings in this map. */ public int size () { return tree.size(); } /** * Returns <tt>true</tt> if this map contains a mapping for the * specified key. <br> * This implementation uses the get method of BinarySearchTree to * search efficiently for the key. The standard implementation of * AbstractMap requires linear time in the size of the map but the * performance of this implementation depends on the performance of * the used binary search tree. * * @param key key whose presence in this map is to be tested. * @return <tt>true</tt> if this map contains a mapping for the * specified key. */ public boolean containsKey(Object key) { int[] result = new int[1]; return tree.get(chooseSubtree, new Object [] {new MapEntry(key, null)}, result)!=null && result[0]==0; } /** * Returns the value to which this map maps the specified key. Returns * <tt>null</tt> if the map contains no mapping for this key. A return * value of <tt>null</tt> does not <i>necessarily</i> indicate that * the map contains no mapping for the key; it's also possible that * the map explicitly maps the key to <tt>null</tt>. The containsKey * operation may be used to distinguish these two cases.<br> * This implementation uses the get method of the binary search tree * to search efficiently for the key. The standard implementation of * AbstractMap requires linear time in the size of the map but the * performance of this implementation depends on the performance of * the used binary search tree. * * @param key key whose associated value is to be returned. * @return the value to which this map maps the specified key. * @see #containsKey(Object) */ public Object get(Object key) { int[] result = new int[1]; BinarySearchTree.Node node = tree.get(chooseSubtree, new Object [] {new MapEntry(key, null)}, result); return node!=null && result[0]==0 ? ((MapEntry)node.object()).getValue() : null; } /** * Associates the specified value with the specified key in this map * (optional operation). If the map previously contained a mapping for * this key, the old value is replaced.<br> * This implementation uses the insert method of the binary search * tree to insert the key-value mapping. The standard implementation * of AbstractMap requires linear time in the size of the map but the * performance of this implementation depends on the performance of * the used binary search tree. * * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @return previous value associated with specified key, or * <tt>null</tt> if there was no mapping for key. (A * <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with the specified key, * if the implementation supports <tt>null</tt> values.) * @throws UnsupportedOperationException if the <tt>put</tt> operation * is not supported by this map. */ public Object put(Object key, Object value) { int[] result = new int[1]; BinarySearchTree.Node node = tree.get(chooseSubtree, new Object [] {new MapEntry(key, null)}, result); if (node!=null && result[0]==0) return ((MapEntry)node.object()).setValue(value); tree.insert(chooseSubtree, new Object [] {new MapEntry(key, value)}); return null; } /** * Removes the mapping for this key from this map if present (optional * operation). <br> * This implementation uses the remove method of the binary search * tree to remove the mapping. The standard implementation of * AbstractMap requires linear time in the size of the map but the * performance of this implementation depends on the performance of * the used binary search tree. * * @param key key whose mapping is to be removed from the map. * @return previous value associated with specified key, or * <tt>null</tt> if there was no entry for key. (A * <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with the specified key, * if the implementation supports <tt>null</tt> values.) * @throws UnsupportedOperationException if the <tt>remove</tt> * operation is not supported by this map. */ public Object remove(Object key) { BinarySearchTree.Node node = tree.remove(chooseSubtree, new Object[] {new MapEntry(key, null)}, tree.size()%2); return node!=null ? ((MapEntry)node.object()).getValue() : null; } /** * Removes all mappings from this map (optional operation). <br> * This implementation calls <tt>tree.clear()</tt>. * * @throws UnsupportedOperationException clear is not supported by * this map. */ public void clear() { tree.clear(); } /** * Returns a set view of the mappings contained in this map. Each * element in this set is a MapEntry. The set is backed by the map, so * changes to the map are reflected in the set, and vice-versa. (If * the map is modified while an iteration over the set is in progress, * the results of the iteration are undefined.) The set supports * element removal, which removes the corresponding entry from the * map, via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> * operations.<br> * This implementation overrides the class BinarySearchTreeSet and * contains a new constructor that initializes the set's binary search * tree with the field <tt>tree</tt> and the set's comparator with the * the field <tt>comparator</tt>. * * @return a set view of the mappings contained in this map. */ public Set entrySet() { return new BinarySearchTreeSet() { { this.tree = BinarySearchTreeMap.this.tree; this.comparator = BinarySearchTreeMap.this.comparator; } }; } }