Return-Path: Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: (qmail 9658 invoked from network); 15 Sep 2009 05:31:11 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 15 Sep 2009 05:31:11 -0000 Received: (qmail 68774 invoked by uid 500); 15 Sep 2009 05:31:08 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 68632 invoked by uid 500); 15 Sep 2009 05:31:08 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 68423 invoked by uid 99); 15 Sep 2009 05:31:07 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 15 Sep 2009 05:31:07 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 15 Sep 2009 05:30:59 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id C2E522388978; Tue, 15 Sep 2009 05:30:06 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r814997 [2/18] - in /commons/proper/collections/trunk/src: java/org/apache/commons/collections/ java/org/apache/commons/collections/bag/ java/org/apache/commons/collections/bidimap/ java/org/apache/commons/collections/buffer/ java/org/apach... Date: Tue, 15 Sep 2009 05:30:02 -0000 To: commits@commons.apache.org From: bayard@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090915053006.C2E522388978@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: commons/proper/collections/trunk/src/java/org/apache/commons/collections/bidimap/TreeBidiMap.java URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/java/org/apache/commons/collections/bidimap/TreeBidiMap.java?rev=814997&r1=814996&r2=814997&view=diff ============================================================================== --- commons/proper/collections/trunk/src/java/org/apache/commons/collections/bidimap/TreeBidiMap.java (original) +++ commons/proper/collections/trunk/src/java/org/apache/commons/collections/bidimap/TreeBidiMap.java Tue Sep 15 05:29:56 2009 @@ -16,8 +16,6 @@ */ package org.apache.commons.collections.bidimap; -import java.io.Serializable; - import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationException; @@ -26,7 +24,6 @@ import java.util.NoSuchElementException; import java.util.Set; -import org.apache.commons.collections.BidiMap; import org.apache.commons.collections.KeyValue; import org.apache.commons.collections.MapIterator; import org.apache.commons.collections.OrderedBidiMap; @@ -34,6 +31,7 @@ import org.apache.commons.collections.OrderedMapIterator; import org.apache.commons.collections.iterators.EmptyOrderedMapIterator; import org.apache.commons.collections.keyvalue.UnmodifiableMapEntry; +import static org.apache.commons.collections.bidimap.TreeBidiMap.DataElement.*; /** * Red-Black tree-based implementation of BidiMap where all objects added @@ -74,33 +72,47 @@ * * @author Marc Johnson * @author Stephen Colebourne + * @author Matt Benson */ -public class TreeBidiMap implements OrderedBidiMap, Serializable { +public class TreeBidiMap, V extends Comparable> implements OrderedBidiMap { + + static enum DataElement { + KEY("key"), VALUE("value"); + + private final String description; + + /** + * Create a new TreeBidiMap.DataElement. + */ + private DataElement(String description) { + this.description = description; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return description; + } + } - private static final int KEY = 0; - private static final int VALUE = 1; - private static final int MAPENTRY = 2; - private static final int INVERSEMAPENTRY = 3; - private static final int SUM_OF_INDICES = KEY + VALUE; - private static final int FIRST_INDEX = 0; - private static final int NUMBER_OF_INDICES = 2; - private static final String[] dataName = new String[] { "key", "value" }; - - private Node[] rootNode = new Node[2]; + private Node[] rootNode; private int nodeCount = 0; private int modifications = 0; - - private transient Set keySet; - private transient Set valuesSet; - private transient Set entrySet; - private transient TreeBidiMap.Inverse inverse = null; + private Set keySet; + private Set valuesSet; + private Set> entrySet; + private Inverse inverse = null; //----------------------------------------------------------------------- /** * Constructs a new empty TreeBidiMap. */ + @SuppressWarnings("unchecked") public TreeBidiMap() { super(); + rootNode = new Node[2]; } /** @@ -111,8 +123,8 @@ * not Comparable or are not mutually comparable * @throws NullPointerException if any key or value in the map is null */ - public TreeBidiMap(final Map map) { - super(); + public TreeBidiMap(final Map map) { + this(); putAll(map); } @@ -147,11 +159,11 @@ */ public boolean containsKey(final Object key) { checkKey(key); - return (lookup((Comparable) key, KEY) != null); + return (lookupKey(key) != null); } /** - * Checks whether this map contains a mapping for the specified value. + * Checks whether this map contains the a mapping for the specified value. *

* The value must implement Comparable. * @@ -162,7 +174,7 @@ */ public boolean containsValue(final Object value) { checkValue(value); - return (lookup((Comparable) value, VALUE) != null); + return (lookupValue(value) != null); } /** @@ -177,8 +189,10 @@ * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ - public Object get(final Object key) { - return doGet((Comparable) key, KEY); + public V get(final Object key) { + checkKey(key); + Node node = lookupKey(key); + return node == null ? null : node.getValue(); } /** @@ -191,7 +205,7 @@ * BidiMap map1 = new TreeBidiMap(); * map.put("A","B"); // contains A mapped to B, as per Map * map.put("A","C"); // contains A mapped to C, as per Map - * + * * BidiMap map2 = new TreeBidiMap(); * map.put("A","B"); // contains A mapped to B, as per Map * map.put("C","B"); // contains C mapped to B, key A is removed @@ -205,25 +219,25 @@ * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ - public Object put(final Object key, final Object value) { - return doPut((Comparable) key, (Comparable) value, KEY); + public V put(final K key, final V value) { + V result = get(key); + doPut(key, value); + return result; } /** * Puts all the mappings from the specified map into this map. *

* All keys and values must implement Comparable. - * + * * @param map the map to copy from */ - public void putAll(Map map) { - Iterator it = map.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - put(entry.getKey(), entry.getValue()); + public void putAll(Map map) { + for (Map.Entry e : map.entrySet()) { + put(e.getKey(), e.getValue()); } } - + /** * Removes the mapping for this key from this map if present. *

@@ -235,8 +249,8 @@ * @throws ClassCastException if the key is of an inappropriate type * @throws NullPointerException if the key is null */ - public Object remove(final Object key) { - return doRemove((Comparable) key, KEY); + public V remove(final Object key) { + return doRemoveKey(key); } /** @@ -246,8 +260,8 @@ modify(); nodeCount = 0; - rootNode[KEY] = null; - rootNode[VALUE] = null; + rootNode[KEY.ordinal()] = null; + rootNode[VALUE.ordinal()] = null; } //----------------------------------------------------------------------- @@ -263,8 +277,10 @@ * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ - public Object getKey(final Object value) { - return doGet((Comparable) value, VALUE); + public K getKey(final Object value) { + checkValue(value); + Node node = lookupValue(value); + return node == null ? null : node.getKey(); } /** @@ -278,8 +294,8 @@ * @throws ClassCastException if the value is of an inappropriate type * @throws NullPointerException if the value is null */ - public Object removeValue(final Object value) { - return doRemove((Comparable) value, VALUE); + public K removeValue(final Object value) { + return doRemoveValue(value); } //----------------------------------------------------------------------- @@ -289,11 +305,11 @@ * @return the first (lowest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ - public Object firstKey() { + public K firstKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } - return leastNode(rootNode[KEY], KEY).getKey(); + return leastNode(rootNode[KEY.ordinal()], KEY).getKey(); } /** @@ -302,13 +318,13 @@ * @return the last (highest) key currently in this sorted map * @throws NoSuchElementException if this map is empty */ - public Object lastKey() { + public K lastKey() { if (nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } - return greatestNode(rootNode[KEY], KEY).getKey(); + return greatestNode(rootNode[KEY.ordinal()], KEY).getKey(); } - + /** * Gets the next key after the one specified. *

@@ -317,10 +333,10 @@ * @param key the key to search for next from * @return the next key, null if no match or at end */ - public Object nextKey(Object key) { + public K nextKey(K key) { checkKey(key); - Node node = nextGreater(lookup((Comparable) key, KEY), KEY); - return (node == null ? null : node.getKey()); + Node node = nextGreater(lookupKey(key), KEY); + return node == null ? null : node.getKey(); } /** @@ -331,12 +347,12 @@ * @param key the key to search for previous from * @return the previous key, null if no match or at start */ - public Object previousKey(Object key) { + public K previousKey(K key) { checkKey(key); - Node node = nextSmaller(lookup((Comparable) key, KEY), KEY); - return (node == null ? null : node.getKey()); + Node node = nextSmaller(lookupKey(key), KEY); + return node == null ? null : node.getKey(); } - + //----------------------------------------------------------------------- /** * Returns a set view of the keys contained in this map in key order. @@ -350,9 +366,9 @@ * * @return a set view of the keys contained in this map. */ - public Set keySet() { + public Set keySet() { if (keySet == null) { - keySet = new View(this, KEY, KEY); + keySet = new KeyView(KEY); } return keySet; } @@ -371,9 +387,9 @@ * * @return a set view of the values contained in this map. */ - public Collection values() { + public Collection values() { if (valuesSet == null) { - valuesSet = new View(this, KEY, VALUE); + valuesSet = new ValueView(KEY); } return valuesSet; } @@ -393,60 +409,33 @@ * * @return a set view of the values contained in this map. */ - public Set entrySet() { + public Set> entrySet() { if (entrySet == null) { - entrySet = new EntryView(this, KEY, MAPENTRY); + return new EntryView(); } return entrySet; } //----------------------------------------------------------------------- /** - * Gets an iterator over the map entries. - *

- * For this map, this iterator is the fastest way to iterate over the entries. - * - * @return an iterator - */ - public MapIterator mapIterator() { - if (isEmpty()) { - return EmptyOrderedMapIterator.INSTANCE; - } - return new ViewMapIterator(this, KEY); - } - - /** - * Gets an ordered iterator over the map entries. - *

- * This iterator allows both forward and reverse iteration over the entries. - * - * @return an iterator + * {@inheritDoc} */ - public OrderedMapIterator orderedMapIterator() { + public OrderedMapIterator mapIterator() { if (isEmpty()) { - return EmptyOrderedMapIterator.INSTANCE; + return EmptyOrderedMapIterator.getInstance(); } - return new ViewMapIterator(this, KEY); + return new ViewMapIterator(KEY); } //----------------------------------------------------------------------- /** * Gets the inverse map for comparison. - * - * @return the inverse map - */ - public BidiMap inverseBidiMap() { - return inverseOrderedBidiMap(); - } - - /** - * Gets the inverse map for comparison. - * + * * @return the inverse map */ - public OrderedBidiMap inverseOrderedBidiMap() { + public OrderedBidiMap inverseBidiMap() { if (inverse == null) { - inverse = new Inverse(this); + inverse = new Inverse(); } return inverse; } @@ -461,7 +450,7 @@ public boolean equals(Object obj) { return this.doEquals(obj, KEY); } - + /** * Gets the hash code value for this map as per the API. * @@ -470,61 +459,43 @@ public int hashCode() { return this.doHashCode(KEY); } - + /** * Returns a string version of this Map in standard format. - * + * * @return a standard format string version of the map */ public String toString() { return this.doToString(KEY); } - + //----------------------------------------------------------------------- /** - * Common get logic, used to get by key or get by value + * Put logic. * - * @param obj the key or value that we're looking for - * @param index the KEY or VALUE int - * @return the key (if the value was mapped) or the value (if the - * key was mapped); null if we couldn't find the specified - * object - */ - private Object doGet(final Comparable obj, final int index) { - checkNonNullComparable(obj, index); - Node node = lookup(obj, index); - return ((node == null) ? null : node.getData(oppositeIndex(index))); - } - - /** - * Common put logic, differing only in the return value. - * * @param key the key, always the main map key * @param value the value, always the main map value - * @param index the KEY or VALUE int, for the return value only - * @return the previously mapped value */ - private Object doPut(final Comparable key, final Comparable value, final int index) { + private void doPut(final K key, final V value) { checkKeyAndValue(key, value); - + // store previous and remove previous mappings - Object prev = (index == KEY ? doGet(key, KEY) : doGet(value, VALUE)); - doRemove(key, KEY); - doRemove(value, VALUE); - - Node node = rootNode[KEY]; + doRemoveKey(key); + doRemoveValue(value); + + Node node = rootNode[KEY.ordinal()]; if (node == null) { // map is empty - Node root = new Node(key, value); - rootNode[KEY] = root; - rootNode[VALUE] = root; + Node root = new Node(key, value); + rootNode[KEY.ordinal()] = root; + rootNode[VALUE.ordinal()] = root; grow(); - + } else { // add new mapping while (true) { - int cmp = compare(key, node.getData(KEY)); - + int cmp = compare(key, node.getKey()); + if (cmp == 0) { // shouldn't happen throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map"); @@ -532,54 +503,51 @@ if (node.getLeft(KEY) != null) { node = node.getLeft(KEY); } else { - Node newNode = new Node(key, value); - + Node newNode = new Node(key, value); + insertValue(newNode); node.setLeft(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); - + break; } } else { // cmp > 0 if (node.getRight(KEY) != null) { node = node.getRight(KEY); } else { - Node newNode = new Node(key, value); - + Node newNode = new Node(key, value); + insertValue(newNode); node.setRight(newNode, KEY); newNode.setParent(node, KEY); doRedBlackInsert(newNode, KEY); grow(); - + break; } } } } - return prev; } - /** - * Remove by object (remove by key or remove by value) - * - * @param o the key, or value, that we're looking for - * @param index the KEY or VALUE int - * - * @return the key, if remove by value, or the value, if remove by - * key. null if the specified key or value could not be - * found - */ - private Object doRemove(final Comparable o, final int index) { - Node node = lookup(o, index); - Object rval = null; - if (node != null) { - rval = node.getData(oppositeIndex(index)); - doRedBlackDelete(node); + private V doRemoveKey(Object key) { + Node node = lookupKey(key); + if (node == null) { + return null; } - return rval; + doRedBlackDelete(node); + return node.getValue(); + } + + private K doRemoveValue(Object value) { + Node node = lookupValue(value); + if (node == null) { + return null; + } + doRedBlackDelete(node); + return node.getKey(); } /** @@ -590,23 +558,32 @@ * @return the desired Node, or null if there is no mapping of the * specified data */ - private Node lookup(final Comparable data, final int index) { - Node rval = null; - Node node = rootNode[index]; + @SuppressWarnings("unchecked") + private > Node lookup(final Object data, final DataElement dataElement) { + Node rval = null; + Node node = rootNode[dataElement.ordinal()]; while (node != null) { - int cmp = compare(data, node.getData(index)); + int cmp = compare((T) data, (T) node.getData(dataElement)); if (cmp == 0) { rval = node; break; } else { - node = (cmp < 0) ? node.getLeft(index) : node.getRight(index); + node = (cmp < 0) ? node.getLeft(dataElement) : node.getRight(dataElement); } } return rval; } + private Node lookupKey(Object key) { + return this.lookup(key, KEY); + } + + private Node lookupValue(Object value) { + return this.lookup(value, VALUE); + } + /** * get the next larger node from the specified node * @@ -614,14 +591,14 @@ * @param index the KEY or VALUE int * @return the specified node */ - private Node nextGreater(final Node node, final int index) { - Node rval = null; + private Node nextGreater(final Node node, final DataElement dataElement) { + Node rval = null; if (node == null) { rval = null; - } else if (node.getRight(index) != null) { + } else if (node.getRight(dataElement) != null) { // everything to the node's right is larger. The least of // the right node's descendants is the next larger node - rval = leastNode(node.getRight(index), index); + rval = leastNode(node.getRight(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose left child is our ancestor. If we @@ -629,12 +606,12 @@ // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's left // ... and that ancestor is the next greatest node - Node parent = node.getParent(index); - Node child = node; + Node parent = node.getParent(dataElement); + Node child = node; - while ((parent != null) && (child == parent.getRight(index))) { + while ((parent != null) && (child == parent.getRight(dataElement))) { child = parent; - parent = parent.getParent(index); + parent = parent.getParent(dataElement); } rval = parent; } @@ -648,14 +625,14 @@ * @param index the KEY or VALUE int * @return the specified node */ - private Node nextSmaller(final Node node, final int index) { - Node rval = null; + private Node nextSmaller(final Node node, final DataElement dataElement) { + Node rval = null; if (node == null) { rval = null; - } else if (node.getLeft(index) != null) { + } else if (node.getLeft(dataElement) != null) { // everything to the node's left is smaller. The greatest of // the left node's descendants is the next smaller node - rval = greatestNode(node.getLeft(index), index); + rval = greatestNode(node.getLeft(dataElement), dataElement); } else { // traverse up our ancestry until we find an ancestor that // is null or one whose right child is our ancestor. If we @@ -663,12 +640,12 @@ // tree, and there is no greater node. Otherwise, we are // the largest node in the subtree on that ancestor's right // ... and that ancestor is the next greatest node - Node parent = node.getParent(index); - Node child = node; + Node parent = node.getParent(dataElement); + Node child = node; - while ((parent != null) && (child == parent.getLeft(index))) { + while ((parent != null) && (child == parent.getLeft(dataElement))) { child = parent; - parent = parent.getParent(index); + parent = parent.getParent(dataElement); } rval = parent; } @@ -676,18 +653,6 @@ } //----------------------------------------------------------------------- - /** - * Get the opposite index of the specified index - * - * @param index the KEY or VALUE int - * @return VALUE (if KEY was specified), else KEY - */ - private static int oppositeIndex(final int index) { - // old trick ... to find the opposite of a value, m or n, - // subtract the value from the sum of the two possible - // values. (m + n) - m = n; (m + n) - n = m - return SUM_OF_INDICES - index; - } /** * Compare two objects @@ -698,7 +663,7 @@ * @return negative value if o1 < o2; 0 if o1 == o2; positive * value if o1 > o2 */ - private static int compare(final Comparable o1, final Comparable o2) { + private static > int compare(final T o1, final T o2) { return o1.compareTo(o2); } @@ -710,11 +675,11 @@ * @return the smallest node, from the specified node, in the * specified mapping */ - private static Node leastNode(final Node node, final int index) { - Node rval = node; + private Node leastNode(final Node node, final DataElement dataElement) { + Node rval = node; if (rval != null) { - while (rval.getLeft(index) != null) { - rval = rval.getLeft(index); + while (rval.getLeft(dataElement) != null) { + rval = rval.getLeft(dataElement); } } return rval; @@ -727,11 +692,11 @@ * @param index the KEY or VALUE int * @return the greatest node, from the specified node */ - private static Node greatestNode(final Node node, final int index) { - Node rval = node; + private Node greatestNode(final Node node, final DataElement dataElement) { + Node rval = node; if (rval != null) { - while (rval.getRight(index) != null) { - rval = rval.getRight(index); + while (rval.getRight(dataElement) != null) { + rval = rval.getRight(dataElement); } } return rval; @@ -745,13 +710,13 @@ * @param to the node whose color we're changing; may be null * @param index the KEY or VALUE int */ - private static void copyColor(final Node from, final Node to, final int index) { + private void copyColor(final Node from, final Node to, final DataElement dataElement) { if (to != null) { if (from == null) { // by default, make it black - to.setBlack(index); + to.setBlack(dataElement); } else { - to.copyColor(from, index); + to.copyColor(from, dataElement); } } } @@ -763,8 +728,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static boolean isRed(final Node node, final int index) { - return ((node == null) ? false : node.isRed(index)); + private static boolean isRed(final Node node, final DataElement dataElement) { + return node != null && node.isRed(dataElement); } /** @@ -774,8 +739,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static boolean isBlack(final Node node, final int index) { - return ((node == null) ? true : node.isBlack(index)); + private static boolean isBlack(final Node node, final DataElement dataElement) { + return node == null || node.isBlack(dataElement); } /** @@ -784,9 +749,9 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static void makeRed(final Node node, final int index) { + private static void makeRed(final Node node, final DataElement dataElement) { if (node != null) { - node.setRed(index); + node.setRed(dataElement); } } @@ -796,9 +761,9 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static void makeBlack(final Node node, final int index) { + private static void makeBlack(final Node node, final DataElement dataElement) { if (node != null) { - node.setBlack(index); + node.setBlack(dataElement); } } @@ -809,8 +774,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static Node getGrandParent(final Node node, final int index) { - return getParent(getParent(node, index), index); + private Node getGrandParent(final Node node, final DataElement dataElement) { + return getParent(getParent(node, dataElement), dataElement); } /** @@ -820,8 +785,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static Node getParent(final Node node, final int index) { - return ((node == null) ? null : node.getParent(index)); + private Node getParent(final Node node, final DataElement dataElement) { + return node == null ? null : node.getParent(dataElement); } /** @@ -831,8 +796,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static Node getRightChild(final Node node, final int index) { - return (node == null) ? null : node.getRight(index); + private Node getRightChild(final Node node, final DataElement dataElement) { + return node == null ? null : node.getRight(dataElement); } /** @@ -842,44 +807,8 @@ * @param node the node (may be null) in question * @param index the KEY or VALUE int */ - private static Node getLeftChild(final Node node, final int index) { - return (node == null) ? null : node.getLeft(index); - } - - /** - * is this node its parent's left child? mind you, the node, or - * its parent, may not exist. no problem. if the node doesn't - * exist ... it's its non-existent parent's left child. If the - * node does exist but has no parent ... no, we're not the - * non-existent parent's left child. Otherwise (both the specified - * node AND its parent exist), check. - * - * @param node the node (may be null) in question - * @param index the KEY or VALUE int - */ - private static boolean isLeftChild(final Node node, final int index) { - return (node == null) - ? true - : ((node.getParent(index) == null) ? - false : (node == node.getParent(index).getLeft(index))); - } - - /** - * is this node its parent's right child? mind you, the node, or - * its parent, may not exist. no problem. if the node doesn't - * exist ... it's its non-existent parent's right child. If the - * node does exist but has no parent ... no, we're not the - * non-existent parent's right child. Otherwise (both the - * specified node AND its parent exist), check. - * - * @param node the node (may be null) in question - * @param index the KEY or VALUE int - */ - private static boolean isRightChild(final Node node, final int index) { - return (node == null) - ? true - : ((node.getParent(index) == null) ? - false : (node == node.getParent(index).getRight(index))); + private Node getLeftChild(final Node node, final DataElement dataElement) { + return node == null ? null : node.getLeft(dataElement); } /** @@ -888,26 +817,26 @@ * @param node the node to be rotated * @param index the KEY or VALUE int */ - private void rotateLeft(final Node node, final int index) { - Node rightChild = node.getRight(index); - node.setRight(rightChild.getLeft(index), index); - - if (rightChild.getLeft(index) != null) { - rightChild.getLeft(index).setParent(node, index); - } - rightChild.setParent(node.getParent(index), index); - - if (node.getParent(index) == null) { + private void rotateLeft(final Node node, final DataElement dataElement) { + Node rightChild = node.getRight(dataElement); + node.setRight(rightChild.getLeft(dataElement), dataElement); + + if (rightChild.getLeft(dataElement) != null) { + rightChild.getLeft(dataElement).setParent(node, dataElement); + } + rightChild.setParent(node.getParent(dataElement), dataElement); + + if (node.getParent(dataElement) == null) { // node was the root ... now its right child is the root - rootNode[index] = rightChild; - } else if (node.getParent(index).getLeft(index) == node) { - node.getParent(index).setLeft(rightChild, index); + rootNode[dataElement.ordinal()] = rightChild; + } else if (node.getParent(dataElement).getLeft(dataElement) == node) { + node.getParent(dataElement).setLeft(rightChild, dataElement); } else { - node.getParent(index).setRight(rightChild, index); + node.getParent(dataElement).setRight(rightChild, dataElement); } - rightChild.setLeft(node, index); - node.setParent(rightChild, index); + rightChild.setLeft(node, dataElement); + node.setParent(rightChild, dataElement); } /** @@ -916,25 +845,25 @@ * @param node the node to be rotated * @param index the KEY or VALUE int */ - private void rotateRight(final Node node, final int index) { - Node leftChild = node.getLeft(index); - node.setLeft(leftChild.getRight(index), index); - if (leftChild.getRight(index) != null) { - leftChild.getRight(index).setParent(node, index); + private void rotateRight(final Node node, final DataElement dataElement) { + Node leftChild = node.getLeft(dataElement); + node.setLeft(leftChild.getRight(dataElement), dataElement); + if (leftChild.getRight(dataElement) != null) { + leftChild.getRight(dataElement).setParent(node, dataElement); } - leftChild.setParent(node.getParent(index), index); + leftChild.setParent(node.getParent(dataElement), dataElement); - if (node.getParent(index) == null) { + if (node.getParent(dataElement) == null) { // node was the root ... now its left child is the root - rootNode[index] = leftChild; - } else if (node.getParent(index).getRight(index) == node) { - node.getParent(index).setRight(leftChild, index); + rootNode[dataElement.ordinal()] = leftChild; + } else if (node.getParent(dataElement).getRight(dataElement) == node) { + node.getParent(dataElement).setRight(leftChild, dataElement); } else { - node.getParent(index).setLeft(leftChild, index); + node.getParent(dataElement).setLeft(leftChild, dataElement); } - leftChild.setRight(node, index); - node.setParent(leftChild, index); + leftChild.setRight(node, dataElement); + node.setParent(leftChild, dataElement); } /** @@ -942,67 +871,69 @@ * implementation, though it's barely recognizable any more * * @param insertedNode the node to be inserted - * @param index the KEY or VALUE int + * @param dataElement the KEY or VALUE int */ - private void doRedBlackInsert(final Node insertedNode, final int index) { - Node currentNode = insertedNode; - makeRed(currentNode, index); + private void doRedBlackInsert(final Node insertedNode, final DataElement dataElement) { + Node currentNode = insertedNode; + makeRed(currentNode, dataElement); while ((currentNode != null) - && (currentNode != rootNode[index]) - && (isRed(currentNode.getParent(index), index))) { - if (isLeftChild(getParent(currentNode, index), index)) { - Node y = getRightChild(getGrandParent(currentNode, index), index); - - if (isRed(y, index)) { - makeBlack(getParent(currentNode, index), index); - makeBlack(y, index); - makeRed(getGrandParent(currentNode, index), index); + && (currentNode != rootNode[dataElement.ordinal()]) + && (isRed(currentNode.getParent(dataElement), dataElement))) { + if (currentNode.isLeftChild(dataElement)) { + Node y = getRightChild(getGrandParent(currentNode, dataElement), dataElement); + + if (isRed(y, dataElement)) { + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(y, dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); - currentNode = getGrandParent(currentNode, index); + currentNode = getGrandParent(currentNode, dataElement); } else { - if (isRightChild(currentNode, index)) { - currentNode = getParent(currentNode, index); + //dead code? + if (currentNode.isRightChild(dataElement)) { + currentNode = getParent(currentNode, dataElement); - rotateLeft(currentNode, index); + rotateLeft(currentNode, dataElement); } - makeBlack(getParent(currentNode, index), index); - makeRed(getGrandParent(currentNode, index), index); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); - if (getGrandParent(currentNode, index) != null) { - rotateRight(getGrandParent(currentNode, index), index); + if (getGrandParent(currentNode, dataElement) != null) { + rotateRight(getGrandParent(currentNode, dataElement), dataElement); } } } else { // just like clause above, except swap left for right - Node y = getLeftChild(getGrandParent(currentNode, index), index); + Node y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement); - if (isRed(y, index)) { - makeBlack(getParent(currentNode, index), index); - makeBlack(y, index); - makeRed(getGrandParent(currentNode, index), index); + if (isRed(y, dataElement)) { + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(y, dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); - currentNode = getGrandParent(currentNode, index); + currentNode = getGrandParent(currentNode, dataElement); } else { - if (isLeftChild(currentNode, index)) { - currentNode = getParent(currentNode, index); + //dead code? + if (currentNode.isLeftChild(dataElement)) { + currentNode = getParent(currentNode, dataElement); - rotateRight(currentNode, index); + rotateRight(currentNode, dataElement); } - makeBlack(getParent(currentNode, index), index); - makeRed(getGrandParent(currentNode, index), index); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); - if (getGrandParent(currentNode, index) != null) { - rotateLeft(getGrandParent(currentNode, index), index); + if (getGrandParent(currentNode, dataElement) != null) { + rotateLeft(getGrandParent(currentNode, dataElement), dataElement); } } } } - makeBlack(rootNode[index], index); + makeBlack(rootNode[dataElement.ordinal()], dataElement); } /** @@ -1011,57 +942,57 @@ * * @param deletedNode the node to be deleted */ - private void doRedBlackDelete(final Node deletedNode) { - for (int index = FIRST_INDEX; index < NUMBER_OF_INDICES; index++) { + private void doRedBlackDelete(final Node deletedNode) { + for (DataElement dataElement : DataElement.values()) { // if deleted node has both left and children, swap with // the next greater node - if ((deletedNode.getLeft(index) != null) && (deletedNode.getRight(index) != null)) { - swapPosition(nextGreater(deletedNode, index), deletedNode, index); + if ((deletedNode.getLeft(dataElement) != null) && (deletedNode.getRight(dataElement) != null)) { + swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement); } - Node replacement = - ((deletedNode.getLeft(index) != null) ? deletedNode.getLeft(index) : deletedNode.getRight(index)); + Node replacement = + ((deletedNode.getLeft(dataElement) != null) ? deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement)); if (replacement != null) { - replacement.setParent(deletedNode.getParent(index), index); + replacement.setParent(deletedNode.getParent(dataElement), dataElement); - if (deletedNode.getParent(index) == null) { - rootNode[index] = replacement; - } else if (deletedNode == deletedNode.getParent(index).getLeft(index)) { - deletedNode.getParent(index).setLeft(replacement, index); + if (deletedNode.getParent(dataElement) == null) { + rootNode[dataElement.ordinal()] = replacement; + } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { + deletedNode.getParent(dataElement).setLeft(replacement, dataElement); } else { - deletedNode.getParent(index).setRight(replacement, index); + deletedNode.getParent(dataElement).setRight(replacement, dataElement); } - deletedNode.setLeft(null, index); - deletedNode.setRight(null, index); - deletedNode.setParent(null, index); + deletedNode.setLeft(null, dataElement); + deletedNode.setRight(null, dataElement); + deletedNode.setParent(null, dataElement); - if (isBlack(deletedNode, index)) { - doRedBlackDeleteFixup(replacement, index); + if (isBlack(deletedNode, dataElement)) { + doRedBlackDeleteFixup(replacement, dataElement); } } else { // replacement is null - if (deletedNode.getParent(index) == null) { + if (deletedNode.getParent(dataElement) == null) { // empty tree - rootNode[index] = null; + rootNode[dataElement.ordinal()] = null; } else { // deleted node had no children - if (isBlack(deletedNode, index)) { - doRedBlackDeleteFixup(deletedNode, index); + if (isBlack(deletedNode, dataElement)) { + doRedBlackDeleteFixup(deletedNode, dataElement); } - if (deletedNode.getParent(index) != null) { - if (deletedNode == deletedNode.getParent(index).getLeft(index)) { - deletedNode.getParent(index).setLeft(null, index); + if (deletedNode.getParent(dataElement) != null) { + if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { + deletedNode.getParent(dataElement).setLeft(null, dataElement); } else { - deletedNode.getParent(index).setRight(null, index); + deletedNode.getParent(dataElement).setRight(null, dataElement); } - deletedNode.setParent(null, index); + deletedNode.setParent(null, dataElement); } } } @@ -1076,80 +1007,80 @@ * perfectly balanced -- perfect balancing takes longer) * * @param replacementNode the node being replaced - * @param index the KEY or VALUE int + * @param dataElement the KEY or VALUE int */ - private void doRedBlackDeleteFixup(final Node replacementNode, final int index) { - Node currentNode = replacementNode; + private void doRedBlackDeleteFixup(final Node replacementNode, final DataElement dataElement) { + Node currentNode = replacementNode; + + while ((currentNode != rootNode[dataElement.ordinal()]) && (isBlack(currentNode, dataElement))) { + if (currentNode.isLeftChild(dataElement)) { + Node siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); - while ((currentNode != rootNode[index]) && (isBlack(currentNode, index))) { - if (isLeftChild(currentNode, index)) { - Node siblingNode = getRightChild(getParent(currentNode, index), index); - - if (isRed(siblingNode, index)) { - makeBlack(siblingNode, index); - makeRed(getParent(currentNode, index), index); - rotateLeft(getParent(currentNode, index), index); + if (isRed(siblingNode, dataElement)) { + makeBlack(siblingNode, dataElement); + makeRed(getParent(currentNode, dataElement), dataElement); + rotateLeft(getParent(currentNode, dataElement), dataElement); - siblingNode = getRightChild(getParent(currentNode, index), index); + siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } - if (isBlack(getLeftChild(siblingNode, index), index) - && isBlack(getRightChild(siblingNode, index), index)) { - makeRed(siblingNode, index); + if (isBlack(getLeftChild(siblingNode, dataElement), dataElement) + && isBlack(getRightChild(siblingNode, dataElement), dataElement)) { + makeRed(siblingNode, dataElement); - currentNode = getParent(currentNode, index); + currentNode = getParent(currentNode, dataElement); } else { - if (isBlack(getRightChild(siblingNode, index), index)) { - makeBlack(getLeftChild(siblingNode, index), index); - makeRed(siblingNode, index); - rotateRight(siblingNode, index); + if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) { + makeBlack(getLeftChild(siblingNode, dataElement), dataElement); + makeRed(siblingNode, dataElement); + rotateRight(siblingNode, dataElement); - siblingNode = getRightChild(getParent(currentNode, index), index); + siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); } - copyColor(getParent(currentNode, index), siblingNode, index); - makeBlack(getParent(currentNode, index), index); - makeBlack(getRightChild(siblingNode, index), index); - rotateLeft(getParent(currentNode, index), index); + copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(getRightChild(siblingNode, dataElement), dataElement); + rotateLeft(getParent(currentNode, dataElement), dataElement); - currentNode = rootNode[index]; + currentNode = rootNode[dataElement.ordinal()]; } } else { - Node siblingNode = getLeftChild(getParent(currentNode, index), index); + Node siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); - if (isRed(siblingNode, index)) { - makeBlack(siblingNode, index); - makeRed(getParent(currentNode, index), index); - rotateRight(getParent(currentNode, index), index); + if (isRed(siblingNode, dataElement)) { + makeBlack(siblingNode, dataElement); + makeRed(getParent(currentNode, dataElement), dataElement); + rotateRight(getParent(currentNode, dataElement), dataElement); - siblingNode = getLeftChild(getParent(currentNode, index), index); + siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } - if (isBlack(getRightChild(siblingNode, index), index) - && isBlack(getLeftChild(siblingNode, index), index)) { - makeRed(siblingNode, index); + if (isBlack(getRightChild(siblingNode, dataElement), dataElement) + && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { + makeRed(siblingNode, dataElement); - currentNode = getParent(currentNode, index); + currentNode = getParent(currentNode, dataElement); } else { - if (isBlack(getLeftChild(siblingNode, index), index)) { - makeBlack(getRightChild(siblingNode, index), index); - makeRed(siblingNode, index); - rotateLeft(siblingNode, index); + if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { + makeBlack(getRightChild(siblingNode, dataElement), dataElement); + makeRed(siblingNode, dataElement); + rotateLeft(siblingNode, dataElement); - siblingNode = getLeftChild(getParent(currentNode, index), index); + siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); } - copyColor(getParent(currentNode, index), siblingNode, index); - makeBlack(getParent(currentNode, index), index); - makeBlack(getLeftChild(siblingNode, index), index); - rotateRight(getParent(currentNode, index), index); + copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(getLeftChild(siblingNode, dataElement), dataElement); + rotateRight(getParent(currentNode, dataElement), dataElement); - currentNode = rootNode[index]; + currentNode = rootNode[dataElement.ordinal()]; } } } - makeBlack(currentNode, index); + makeBlack(currentNode, dataElement); } /** @@ -1159,94 +1090,94 @@ * * @param x one node * @param y another node - * @param index the KEY or VALUE int + * @param dataElement the KEY or VALUE int */ - private void swapPosition(final Node x, final Node y, final int index) { + private void swapPosition(final Node x, final Node y, final DataElement dataElement) { // Save initial values. - Node xFormerParent = x.getParent(index); - Node xFormerLeftChild = x.getLeft(index); - Node xFormerRightChild = x.getRight(index); - Node yFormerParent = y.getParent(index); - Node yFormerLeftChild = y.getLeft(index); - Node yFormerRightChild = y.getRight(index); - boolean xWasLeftChild = (x.getParent(index) != null) && (x == x.getParent(index).getLeft(index)); - boolean yWasLeftChild = (y.getParent(index) != null) && (y == y.getParent(index).getLeft(index)); + Node xFormerParent = x.getParent(dataElement); + Node xFormerLeftChild = x.getLeft(dataElement); + Node xFormerRightChild = x.getRight(dataElement); + Node yFormerParent = y.getParent(dataElement); + Node yFormerLeftChild = y.getLeft(dataElement); + Node yFormerRightChild = y.getRight(dataElement); + boolean xWasLeftChild = (x.getParent(dataElement) != null) && (x == x.getParent(dataElement).getLeft(dataElement)); + boolean yWasLeftChild = (y.getParent(dataElement) != null) && (y == y.getParent(dataElement).getLeft(dataElement)); // Swap, handling special cases of one being the other's parent. if (x == yFormerParent) { // x was y's parent - x.setParent(y, index); + x.setParent(y, dataElement); if (yWasLeftChild) { - y.setLeft(x, index); - y.setRight(xFormerRightChild, index); + y.setLeft(x, dataElement); + y.setRight(xFormerRightChild, dataElement); } else { - y.setRight(x, index); - y.setLeft(xFormerLeftChild, index); + y.setRight(x, dataElement); + y.setLeft(xFormerLeftChild, dataElement); } } else { - x.setParent(yFormerParent, index); + x.setParent(yFormerParent, dataElement); if (yFormerParent != null) { if (yWasLeftChild) { - yFormerParent.setLeft(x, index); + yFormerParent.setLeft(x, dataElement); } else { - yFormerParent.setRight(x, index); + yFormerParent.setRight(x, dataElement); } } - y.setLeft(xFormerLeftChild, index); - y.setRight(xFormerRightChild, index); + y.setLeft(xFormerLeftChild, dataElement); + y.setRight(xFormerRightChild, dataElement); } if (y == xFormerParent) { // y was x's parent - y.setParent(x, index); + y.setParent(x, dataElement); if (xWasLeftChild) { - x.setLeft(y, index); - x.setRight(yFormerRightChild, index); + x.setLeft(y, dataElement); + x.setRight(yFormerRightChild, dataElement); } else { - x.setRight(y, index); - x.setLeft(yFormerLeftChild, index); + x.setRight(y, dataElement); + x.setLeft(yFormerLeftChild, dataElement); } } else { - y.setParent(xFormerParent, index); + y.setParent(xFormerParent, dataElement); if (xFormerParent != null) { if (xWasLeftChild) { - xFormerParent.setLeft(y, index); + xFormerParent.setLeft(y, dataElement); } else { - xFormerParent.setRight(y, index); + xFormerParent.setRight(y, dataElement); } } - x.setLeft(yFormerLeftChild, index); - x.setRight(yFormerRightChild, index); + x.setLeft(yFormerLeftChild, dataElement); + x.setRight(yFormerRightChild, dataElement); } // Fix children's parent pointers - if (x.getLeft(index) != null) { - x.getLeft(index).setParent(x, index); + if (x.getLeft(dataElement) != null) { + x.getLeft(dataElement).setParent(x, dataElement); } - if (x.getRight(index) != null) { - x.getRight(index).setParent(x, index); + if (x.getRight(dataElement) != null) { + x.getRight(dataElement).setParent(x, dataElement); } - if (y.getLeft(index) != null) { - y.getLeft(index).setParent(y, index); + if (y.getLeft(dataElement) != null) { + y.getLeft(dataElement).setParent(y, dataElement); } - if (y.getRight(index) != null) { - y.getRight(index).setParent(y, index); + if (y.getRight(dataElement) != null) { + y.getRight(dataElement).setParent(y, dataElement); } - x.swapColors(y, index); + x.swapColors(y, dataElement); // Check if root changed - if (rootNode[index] == x) { - rootNode[index] = y; - } else if (rootNode[index] == y) { - rootNode[index] = x; + if (rootNode[dataElement.ordinal()] == x) { + rootNode[dataElement.ordinal()] = y; + } else if (rootNode[dataElement.ordinal()] == y) { + rootNode[dataElement.ordinal()] = x; } } @@ -1261,12 +1192,12 @@ * @throws NullPointerException if o is null * @throws ClassCastException if o is not Comparable */ - private static void checkNonNullComparable(final Object o, final int index) { + private static void checkNonNullComparable(final Object o, final DataElement dataElement) { if (o == null) { - throw new NullPointerException(dataName[index] + " cannot be null"); + throw new NullPointerException(dataElement + " cannot be null"); } if (!(o instanceof Comparable)) { - throw new ClassCastException(dataName[index] + " must be Comparable"); + throw new ClassCastException(dataElement + " must be Comparable"); } } @@ -1342,11 +1273,11 @@ * @throws IllegalArgumentException if the node already exists * in the value mapping */ - private void insertValue(final Node newNode) throws IllegalArgumentException { - Node node = rootNode[VALUE]; + private void insertValue(final Node newNode) throws IllegalArgumentException { + Node node = rootNode[VALUE.ordinal()]; while (true) { - int cmp = compare(newNode.getData(VALUE), node.getData(VALUE)); + int cmp = compare(newNode.getValue(), node.getValue()); if (cmp == 0) { throw new IllegalArgumentException( @@ -1374,7 +1305,7 @@ } } } - + //----------------------------------------------------------------------- /** * Compares for equals as per the API. @@ -1383,21 +1314,21 @@ * @param type the KEY or VALUE int * @return true if equal */ - private boolean doEquals(Object obj, final int type) { + private boolean doEquals(Object obj, DataElement dataElement) { if (obj == this) { return true; } if (obj instanceof Map == false) { return false; } - Map other = (Map) obj; + Map other = (Map) obj; if (other.size() != size()) { return false; } if (nodeCount > 0) { try { - for (MapIterator it = new ViewMapIterator(this, type); it.hasNext(); ) { + for (MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { Object key = it.next(); Object value = it.getValue(); if (value.equals(other.get(key)) == false) { @@ -1419,10 +1350,10 @@ * @param type the KEY or VALUE int * @return the hash code value for this map */ - private int doHashCode(final int type) { + private int doHashCode(DataElement dataElement) { int total = 0; if (nodeCount > 0) { - for (MapIterator it = new ViewMapIterator(this, type); it.hasNext(); ) { + for (MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { Object key = it.next(); Object value = it.getValue(); total += (key.hashCode() ^ value.hashCode()); @@ -1430,20 +1361,20 @@ } return total; } - + /** * Gets the string form of this map as per AbstractMap. * * @param type the KEY or VALUE int * @return the string form of this map */ - private String doToString(final int type) { + private String doToString(DataElement dataElement) { if (nodeCount == 0) { return "{}"; } StringBuffer buf = new StringBuffer(nodeCount * 32); buf.append('{'); - MapIterator it = new ViewMapIterator(this, type); + MapIterator it = getMapIterator(dataElement); boolean hasNext = it.hasNext(); while (hasNext) { Object key = it.next(); @@ -1462,52 +1393,174 @@ return buf.toString(); } + private MapIterator getMapIterator(DataElement dataElement) { + switch (dataElement) { + case KEY: + return new ViewMapIterator(KEY); + case VALUE: + return new InverseViewMapIterator(VALUE); + default: + throw new IllegalArgumentException(); + } + } + //----------------------------------------------------------------------- /** * A view of this map. */ - static class View extends AbstractSet { - - /** The parent map. */ - protected final TreeBidiMap main; + abstract class View extends AbstractSet { + /** Whether to return KEY or VALUE order. */ - protected final int orderType; - /** Whether to return KEY, VALUE, MAPENTRY or INVERSEMAPENTRY data. */ - protected final int dataType; + protected final DataElement orderType; /** * Constructor. - * - * @param main the main map * @param orderType the KEY or VALUE int for the order - * @param dataType the KEY, VALUE, MAPENTRY or INVERSEMAPENTRY int + * @param main the main map */ - View(final TreeBidiMap main, final int orderType, final int dataType) { + View(final DataElement orderType) { super(); - this.main = main; this.orderType = orderType; - this.dataType = dataType; - } - - public Iterator iterator() { - return new ViewIterator(main, orderType, dataType); } public int size() { - return main.size(); + return TreeBidiMap.this.size(); + } + + public void clear() { + TreeBidiMap.this.clear(); + } + } + + class KeyView extends View { + + /** + * Create a new TreeBidiMap.KeyView. + */ + public KeyView(DataElement orderType) { + super(orderType); + } + + @Override + public Iterator iterator() { + return new ViewMapIterator(orderType); } + @Override public boolean contains(final Object obj) { - checkNonNullComparable(obj, dataType); - return (main.lookup((Comparable) obj, dataType) != null); + checkNonNullComparable(obj, KEY); + return (lookupKey(obj) != null); } - public boolean remove(final Object obj) { - return (main.doRemove((Comparable) obj, dataType) != null); + @Override + public boolean remove(Object o) { + return doRemoveKey(o) != null; } - public void clear() { - main.clear(); + } + + class ValueView extends View { + + /** + * Create a new TreeBidiMap.ValueView. + */ + public ValueView(DataElement orderType) { + super(orderType); + } + + @Override + public Iterator iterator() { + return new InverseViewMapIterator(orderType); + } + + @Override + public boolean contains(final Object obj) { + checkNonNullComparable(obj, VALUE); + return (lookupValue(obj) != null); + } + + @Override + public boolean remove(Object o) { + return doRemoveValue(o) != null; + } + + } + + /** + * A view of this map. + */ + class EntryView extends View> { + + EntryView() { + super(KEY); + } + + public boolean contains(Object obj) { + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry entry = (Map.Entry) obj; + Object value = entry.getValue(); + Node node = lookupKey(entry.getKey()); + return node != null && node.getValue().equals(value); + } + + public boolean remove(Object obj) { + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry entry = (Map.Entry) obj; + Object value = entry.getValue(); + Node node = lookupKey(entry.getKey()); + if (node != null && node.getValue().equals(value)) { + doRedBlackDelete(node); + return true; + } + return false; + } + + @Override + public Iterator> iterator() { + return new ViewMapEntryIterator(); + } + } + + /** + * A view of this map. + */ + class InverseEntryView extends View> { + + InverseEntryView() { + super(VALUE); + } + + public boolean contains(Object obj) { + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry entry = (Map.Entry) obj; + Object value = entry.getValue(); + Node node = lookupValue(entry.getKey()); + return node != null && node.getKey().equals(value); + } + + public boolean remove(Object obj) { + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry entry = (Map.Entry) obj; + Object value = entry.getValue(); + Node node = lookupValue(entry.getKey()); + if (node != null && node.getKey().equals(value)) { + doRedBlackDelete(node); + return true; + } + return false; + } + + @Override + public Iterator> iterator() { + return new InverseViewMapEntryIterator(); } } @@ -1515,110 +1568,84 @@ /** * An iterator over the map. */ - static class ViewIterator implements OrderedIterator { + abstract class ViewIterator { - /** The parent map. */ - protected final TreeBidiMap main; /** Whether to return KEY or VALUE order. */ - protected final int orderType; - /** Whether to return KEY, VALUE, MAPENTRY or INVERSEMAPENTRY data. */ - protected final int dataType; + protected final DataElement orderType; /** The last node returned by the iterator. */ - protected Node lastReturnedNode; + protected Node lastReturnedNode; /** The next node to be returned by the iterator. */ - protected Node nextNode; + protected Node nextNode; /** The previous node in the sequence returned by the iterator. */ - protected Node previousNode; + protected Node previousNode; /** The modification count. */ private int expectedModifications; /** * Constructor. - * - * @param main the main map * @param orderType the KEY or VALUE int for the order - * @param dataType the KEY, VALUE, MAPENTRY or INVERSEMAPENTRY int + * @param main the main map */ - ViewIterator(final TreeBidiMap main, final int orderType, final int dataType) { + ViewIterator(final DataElement orderType) { super(); - this.main = main; this.orderType = orderType; - this.dataType = dataType; - expectedModifications = main.modifications; - nextNode = leastNode(main.rootNode[orderType], orderType); + expectedModifications = modifications; + nextNode = leastNode(rootNode[orderType.ordinal()], orderType); lastReturnedNode = null; previousNode = null; } public final boolean hasNext() { - return (nextNode != null); + return nextNode != null; } - public final Object next() { + protected Node navigateNext() { if (nextNode == null) { throw new NoSuchElementException(); } - if (main.modifications != expectedModifications) { + if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } lastReturnedNode = nextNode; previousNode = nextNode; - nextNode = main.nextGreater(nextNode, orderType); - return doGetData(); + nextNode = nextGreater(nextNode, orderType); + return lastReturnedNode; } public boolean hasPrevious() { - return (previousNode != null); + return previousNode != null; } - public Object previous() { + protected Node navigatePrevious() { if (previousNode == null) { throw new NoSuchElementException(); } - if (main.modifications != expectedModifications) { + if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } nextNode = lastReturnedNode; if (nextNode == null) { - nextNode = main.nextGreater(previousNode, orderType); + nextNode = nextGreater(previousNode, orderType); } lastReturnedNode = previousNode; - previousNode = main.nextSmaller(previousNode, orderType); - return doGetData(); - } - - /** - * Gets the data value for the lastReturnedNode field. - * @return the data value - */ - protected Object doGetData() { - switch (dataType) { - case KEY: - return lastReturnedNode.getKey(); - case VALUE: - return lastReturnedNode.getValue(); - case MAPENTRY: - return lastReturnedNode; - case INVERSEMAPENTRY: - return new UnmodifiableMapEntry(lastReturnedNode.getValue(), lastReturnedNode.getKey()); - } - return null; + previousNode = nextSmaller(previousNode, orderType); + return lastReturnedNode; } public final void remove() { if (lastReturnedNode == null) { throw new IllegalStateException(); } - if (main.modifications != expectedModifications) { + if (modifications != expectedModifications) { throw new ConcurrentModificationException(); } - main.doRedBlackDelete(lastReturnedNode); + doRedBlackDelete(lastReturnedNode); expectedModifications++; lastReturnedNode = null; if (nextNode == null) { - previousNode = TreeBidiMap.greatestNode(main.rootNode[orderType], orderType); + previousNode = greatestNode(rootNode[orderType.ordinal()], orderType); } else { - previousNode = main.nextSmaller(nextNode, orderType); + previousNode = nextSmaller(nextNode, orderType); } } } @@ -1627,95 +1654,139 @@ /** * An iterator over the map. */ - static class ViewMapIterator extends ViewIterator implements OrderedMapIterator { + class ViewMapIterator extends ViewIterator implements OrderedMapIterator { - private final int oppositeType; - /** * Constructor. - * - * @param main the main map - * @param orderType the KEY or VALUE int for the order */ - ViewMapIterator(final TreeBidiMap main, final int orderType) { - super(main, orderType, orderType); - this.oppositeType = oppositeIndex(dataType); + ViewMapIterator(DataElement orderType) { + super(orderType); } - - public Object getKey() { + + public K getKey() { if (lastReturnedNode == null) { throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()"); } - return lastReturnedNode.getData(dataType); + return lastReturnedNode.getKey(); } - public Object getValue() { + public V getValue() { if (lastReturnedNode == null) { throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()"); } - return lastReturnedNode.getData(oppositeType); + return lastReturnedNode.getValue(); } - public Object setValue(final Object obj) { + public V setValue(final V obj) { throw new UnsupportedOperationException(); } + + public K next() { + return navigateNext().getKey(); + } + + public K previous() { + return navigatePrevious().getKey(); + } } - //----------------------------------------------------------------------- /** - * A view of this map. + * An iterator over the map. */ - static class EntryView extends View { - - private final int oppositeType; - + class InverseViewMapIterator extends ViewIterator implements OrderedMapIterator { + /** - * Constructor. - * - * @param main the main map - * @param orderType the KEY or VALUE int for the order - * @param dataType the MAPENTRY or INVERSEMAPENTRY int for the returned data + * Create a new TreeBidiMap.InverseViewMapIterator. */ - EntryView(final TreeBidiMap main, final int orderType, final int dataType) { - super(main, orderType, dataType); - this.oppositeType = TreeBidiMap.oppositeIndex(orderType); + public InverseViewMapIterator(DataElement orderType) { + super(orderType); } - - public boolean contains(Object obj) { - if (obj instanceof Map.Entry == false) { - return false; + + public V getKey() { + if (lastReturnedNode == null) { + throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()"); } - Map.Entry entry = (Map.Entry) obj; - Object value = entry.getValue(); - Node node = main.lookup((Comparable) entry.getKey(), orderType); - return (node != null && node.getData(oppositeType).equals(value)); + return lastReturnedNode.getValue(); } - public boolean remove(Object obj) { - if (obj instanceof Map.Entry == false) { - return false; - } - Map.Entry entry = (Map.Entry) obj; - Object value = entry.getValue(); - Node node = main.lookup((Comparable) entry.getKey(), orderType); - if (node != null && node.getData(oppositeType).equals(value)) { - main.doRedBlackDelete(node); - return true; + public K getValue() { + if (lastReturnedNode == null) { + throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()"); } - return false; + return lastReturnedNode.getKey(); + } + + public K setValue(final K obj) { + throw new UnsupportedOperationException(); + } + + public V next() { + return navigateNext().getValue(); + } + + public V previous() { + return navigatePrevious().getValue(); + } + } + + /** + * An iterator over the map entries. + */ + class ViewMapEntryIterator extends ViewIterator implements OrderedIterator> { + + /** + * Constructor. + */ + ViewMapEntryIterator() { + super(KEY); + } + + public Map.Entry next() { + return navigateNext(); + } + + public Map.Entry previous() { + return navigatePrevious(); + } + } + + /** + * An iterator over the inverse map entries. + */ + class InverseViewMapEntryIterator extends ViewIterator implements OrderedIterator> { + + /** + * Constructor. + */ + InverseViewMapEntryIterator() { + super(VALUE); + } + + public Map.Entry next() { + return createEntry(navigateNext()); + } + + public Map.Entry previous() { + return createEntry(navigatePrevious()); + } + + private Map.Entry createEntry(Node node) { + return new UnmodifiableMapEntry(node.getValue(), node.getKey()); } } //----------------------------------------------------------------------- + //----------------------------------------------------------------------- /** * A node used to store the data. */ - static class Node implements Map.Entry, KeyValue, Serializable { + static class Node, V extends Comparable> implements Map.Entry, KeyValue { - private Comparable[] data; - private Node[] leftNode; - private Node[] rightNode; - private Node[] parentNode; + private K key; + private V value; + private Node[] leftNode; + private Node[] rightNode; + private Node[] parentNode; private boolean[] blackColor; private int hashcodeValue; private boolean calculatedHashCode; @@ -1727,9 +1798,11 @@ * @param key * @param value */ - Node(final Comparable key, final Comparable value) { + @SuppressWarnings("unchecked") + Node(final K key, final V value) { super(); - data = new Comparable[] { key, value }; + this.key = key; + this.value = value; leftNode = new Node[2]; rightNode = new Node[2]; parentNode = new Node[2]; @@ -1737,54 +1810,31 @@ calculatedHashCode = false; } - /** - * Get the specified data. - * - * @param index the KEY or VALUE int - * @return the key or value - */ - private Comparable getData(final int index) { - return data[index]; + private Object getData(final DataElement dataElement) { + switch (dataElement) { + case KEY: + return getKey(); + case VALUE: + return getValue(); + default: + throw new IllegalArgumentException(); + } } - /** - * Set this node's left node. - * - * @param node the new left node - * @param index the KEY or VALUE int - */ - private void setLeft(final Node node, final int index) { - leftNode[index] = node; + private void setLeft(final Node node, final DataElement dataElement) { + leftNode[dataElement.ordinal()] = node; } - /** - * Get the left node. - * - * @param index the KEY or VALUE int - * @return the left node, may be null - */ - private Node getLeft(final int index) { - return leftNode[index]; + private Node getLeft(final DataElement dataElement) { + return leftNode[dataElement.ordinal()]; } - /** - * Set this node's right node. - * - * @param node the new right node - * @param index the KEY or VALUE int - */ - private void setRight(final Node node, final int index) { - rightNode[index] = node; + private void setRight(final Node node, final DataElement dataElement) { + rightNode[dataElement.ordinal()] = node; } - /** - * Get the right node. - * - * @param index the KEY or VALUE int - * @return the right node, may be null - */ - private Node getRight(final int index) { - return rightNode[index]; + private Node getRight(final DataElement dataElement) { + return rightNode[dataElement.ordinal()]; } /** @@ -1793,8 +1843,8 @@ * @param node the new parent node * @param index the KEY or VALUE int */ - private void setParent(final Node node, final int index) { - parentNode[index] = node; + private void setParent(final Node node, final DataElement dataElement) { + parentNode[dataElement.ordinal()] = node; } /** @@ -1803,8 +1853,8 @@ * @param index the KEY or VALUE int * @return the parent node, may be null */ - private Node getParent(final int index) { - return parentNode[index]; + private Node getParent(final DataElement dataElement) { + return parentNode[dataElement.ordinal()]; } /** @@ -1813,11 +1863,11 @@ * @param node the node to swap with * @param index the KEY or VALUE int */ - private void swapColors(final Node node, final int index) { + private void swapColors(final Node node, final DataElement dataElement) { // Swap colors -- old hacker's trick - blackColor[index] ^= node.blackColor[index]; - node.blackColor[index] ^= blackColor[index]; - blackColor[index] ^= node.blackColor[index]; + blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; + node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()]; + blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; } /** @@ -1826,8 +1876,8 @@ * @param index the KEY or VALUE int * @return true if black (which is represented as a true boolean) */ - private boolean isBlack(final int index) { - return blackColor[index]; + private boolean isBlack(final DataElement dataElement) { + return blackColor[dataElement.ordinal()]; } /** @@ -1836,8 +1886,8 @@ * @param index the KEY or VALUE int * @return true if non-black */ - private boolean isRed(final int index) { - return !blackColor[index]; + private boolean isRed(final DataElement dataElement) { + return !blackColor[dataElement.ordinal()]; } /** @@ -1845,8 +1895,8 @@ * * @param index the KEY or VALUE int */ - private void setBlack(final int index) { - blackColor[index] = true; + private void setBlack(final DataElement dataElement) { + blackColor[dataElement.ordinal()] = true; } /** @@ -1854,8 +1904,8 @@ * * @param index the KEY or VALUE int */ - private void setRed(final int index) { - blackColor[index] = false; + private void setRed(final DataElement dataElement) { + blackColor[dataElement.ordinal()] = false; } /** @@ -1864,27 +1914,37 @@ * @param node the node whose color we're adopting * @param index the KEY or VALUE int */ - private void copyColor(final Node node, final int index) { - blackColor[index] = node.blackColor[index]; + private void copyColor(final Node node, final DataElement dataElement) { + blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()]; + } + + private boolean isLeftChild(final DataElement dataElement) { + return parentNode[dataElement.ordinal()] != null + && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this; + } + + private boolean isRightChild(final DataElement dataElement) { + return parentNode[dataElement.ordinal()] != null + && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this; } //------------------------------------------------------------------- /** * Gets the key. - * + * * @return the key corresponding to this entry. */ - public Object getKey() { - return data[KEY]; + public K getKey() { + return key; } /** * Gets the value. - * + * * @return the value corresponding to this entry. */ - public Object getValue() { - return data[VALUE]; + public V getValue() { + return value; } /** @@ -1894,10 +1954,8 @@ * @return does not return * @throws UnsupportedOperationException always */ - public Object setValue(final Object ignored) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "Map.Entry.setValue is not supported"); + public V setValue(final V ignored) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Map.Entry.setValue is not supported"); } /** @@ -1915,8 +1973,8 @@ if (!(obj instanceof Map.Entry)) { return false; } - Map.Entry e = (Map.Entry) obj; - return data[KEY].equals(e.getKey()) && data[VALUE].equals(e.getValue()); + Map.Entry e = (Map.Entry) obj; + return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); } /** @@ -1924,164 +1982,142 @@ */ public int hashCode() { if (!calculatedHashCode) { - hashcodeValue = data[KEY].hashCode() ^ data[VALUE].hashCode(); + hashcodeValue = getKey().hashCode() ^ getValue().hashCode(); calculatedHashCode = true; } return hashcodeValue; } } - + //----------------------------------------------------------------------- /** - * A node used to store the data. + * The inverse map implementation. */ - static class Inverse implements OrderedBidiMap { - - /** The parent map. */ - private final TreeBidiMap main; + class Inverse implements OrderedBidiMap { + /** Store the keySet once created. */ - private Set keySet; + private Set keySet; /** Store the valuesSet once created. */ - private Set valuesSet; + private Set valuesSet; /** Store the entrySet once created. */ - private Set entrySet; - - /** - * Constructor. - * @param main the main map - */ - Inverse(final TreeBidiMap main) { - super(); - this.main = main; - } + private Set> entrySet; public int size() { - return main.size(); + return TreeBidiMap.this.size(); } public boolean isEmpty() { - return main.isEmpty(); + return TreeBidiMap.this.isEmpty(); } - public Object get(final Object key) { - return main.getKey(key); + public K get(final Object key) { + return TreeBidiMap.this.getKey(key); } - public Object getKey(final Object value) { - return main.get(value); + public V getKey(final Object value) { + return TreeBidiMap.this.get(value); } public boolean containsKey(final Object key) { - return main.containsValue(key); + return TreeBidiMap.this.containsValue(key); } public boolean containsValue(final Object value) { - return main.containsKey(value); + return TreeBidiMap.this.containsKey(value); } - public Object firstKey() { - if (main.nodeCount == 0) { + public V firstKey() { + if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } - return TreeBidiMap.leastNode(main.rootNode[VALUE], VALUE).getValue(); + return leastNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue(); } - public Object lastKey() { - if (main.nodeCount == 0) { + public V lastKey() { + if (TreeBidiMap.this.nodeCount == 0) { throw new NoSuchElementException("Map is empty"); } - return TreeBidiMap.greatestNode(main.rootNode[VALUE], VALUE).getValue(); [... 133 lines stripped ...]