2
* Copyright 1999-2004 The Apache Software Foundation
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
16
package org.apache.commons.collections;
20
import java.util.AbstractCollection;
21
import java.util.AbstractMap;
22
import java.util.AbstractSet;
23
import java.util.Collection;
24
import java.util.ConcurrentModificationException;
25
import java.util.Iterator;
27
import java.util.NoSuchElementException;
32
* Red-Black tree-based implementation of Map. This class guarantees
33
* that the map will be in both ascending key order and ascending
34
* value order, sorted according to the natural order for the key's
35
* and value's classes.<p>
37
* This Map is intended for applications that need to be able to look
38
* up a key-value pairing by either key or value, and need to do so
39
* with equal efficiency.<p>
41
* While that goal could be accomplished by taking a pair of TreeMaps
42
* and redirecting requests to the appropriate TreeMap (e.g.,
43
* containsKey would be directed to the TreeMap that maps values to
44
* keys, containsValue would be directed to the TreeMap that maps keys
45
* to values), there are problems with that implementation,
46
* particularly when trying to keep the two TreeMaps synchronized with
47
* each other. And if the data contained in the TreeMaps is large, the
48
* cost of redundant storage becomes significant.<p>
50
* This solution keeps the data properly synchronized and minimizes
51
* the data storage. The red-black algorithm is based on TreeMap's,
52
* but has been modified to simultaneously map a tree node by key and
53
* by value. This doubles the cost of put operations (but so does
54
* using two TreeMaps), and nearly doubles the cost of remove
55
* operations (there is a savings in that the lookup of the node to be
56
* removed only has to be performed once). And since only one node
57
* contains the key and value, storage is significantly less than that
58
* required by two TreeMaps.<p>
60
* There are some limitations placed on data kept in this Map. The
61
* biggest one is this:<p>
63
* When performing a put operation, neither the key nor the value may
64
* already exist in the Map. In the java.util Map implementations
65
* (HashMap, TreeMap), you can perform a put with an already mapped
66
* key, and neither cares about duplicate values at all ... but this
67
* implementation's put method with throw an IllegalArgumentException
68
* if either the key or the value is already in the Map.<p>
70
* Obviously, that same restriction (and consequence of failing to
71
* heed that restriction) applies to the putAll method.<p>
73
* The Map.Entry instances returned by the appropriate methods will
74
* not allow setValue() and will throw an
75
* UnsupportedOperationException on attempts to call that method.<p>
77
* New methods are added to take advantage of the fact that values are
78
* kept sorted independently of their keys:<p>
80
* Object getKeyForValue(Object value) is the opposite of get; it
81
* takes a value and returns its key, if any.<p>
83
* Object removeValue(Object value) finds and removes the specified
84
* value and returns the now un-used key.<p>
86
* Set entrySetByValue() returns the Map.Entry's in a Set whose
87
* iterator will iterate over the Map.Entry's in ascending order by
88
* their corresponding values.<p>
90
* Set keySetByValue() returns the keys in a Set whose iterator will
91
* iterate over the keys in ascending order by their corresponding
94
* Collection valuesByValue() returns the values in a Collection whose
95
* iterator will iterate over the values in ascending order.<p>
98
* @author Marc Johnson (marcj at users dot sourceforge dot net)
101
// final for performance
102
public final class DoubleOrderedMap extends AbstractMap {
104
private Node[] rootNode = new Node[]{ null,
106
private int nodeCount = 0;
107
private int modifications = 0;
108
private Set[] setOfKeys = new Set[]{ null,
110
private Set[] setOfEntries = new Set[]{ null,
112
private Collection[] collectionOfValues = new Collection[]{
116
private static final int KEY = 0;
117
private static final int VALUE = 1;
118
private static final int SUM_OF_INDICES = KEY + VALUE;
119
private static final int FIRST_INDEX = 0;
120
private static final int NUMBER_OF_INDICES = 2;
121
private static final String[] dataName = new String[]{ "key",
126
* Construct a new DoubleOrderedMap
128
public DoubleOrderedMap() {}
131
* Constructs a new DoubleOrderedMap from an existing Map, with keys and
134
* @param map the map whose mappings are to be placed in this map.
136
* @exception ClassCastException if the keys in the map are not
137
* Comparable, or are not mutually
138
* comparable; also if the values in
139
* the map are not Comparable, or
140
* are not mutually Comparable
141
* @exception NullPointerException if any key or value in the map
143
* @exception IllegalArgumentException if there are duplicate keys
144
* or duplicate values in the
147
public DoubleOrderedMap(final Map map)
148
throws ClassCastException, NullPointerException,
149
IllegalArgumentException {
154
* Returns the key to which this map maps the specified value.
155
* Returns null if the map contains no mapping for this value.
157
* @param value value whose associated key is to be returned.
159
* @return the key to which this map maps the specified value, or
160
* null if the map contains no mapping for this value.
162
* @exception ClassCastException if the value is of an
163
* inappropriate type for this map.
164
* @exception NullPointerException if the value is null
166
public Object getKeyForValue(final Object value)
167
throws ClassCastException, NullPointerException {
168
return doGet((Comparable) value, VALUE);
172
* Removes the mapping for this value from this map if present
174
* @param value value whose mapping is to be removed from the map.
176
* @return previous key associated with specified value, or null
177
* if there was no mapping for value.
179
public Object removeValue(final Object value) {
180
return doRemove((Comparable) value, VALUE);
184
* Returns a set view of the mappings contained in this map. Each
185
* element in the returned set is a Map.Entry. The set is backed
186
* by the map, so changes to the map are reflected in the set, and
187
* vice-versa. If the map is modified while an iteration over the
188
* set is in progress, the results of the iteration are
189
* undefined. The set supports element removal, which removes the
190
* corresponding mapping from the map, via the Iterator.remove,
191
* Set.remove, removeAll, retainAll and clear operations. It does
192
* not support the add or addAll operations.<p>
194
* The difference between this method and entrySet is that
195
* entrySet's iterator() method returns an iterator that iterates
196
* over the mappings in ascending order by key. This method's
197
* iterator method iterates over the mappings in ascending order
200
* @return a set view of the mappings contained in this map.
202
public Set entrySetByValue() {
204
if (setOfEntries[VALUE] == null) {
205
setOfEntries[VALUE] = new AbstractSet() {
207
public Iterator iterator() {
209
return new DoubleOrderedMapIterator(VALUE) {
211
protected Object doGetNext() {
212
return lastReturnedNode;
217
public boolean contains(Object o) {
219
if (!(o instanceof Map.Entry)) {
223
Map.Entry entry = (Map.Entry) o;
224
Object key = entry.getKey();
225
Node node = lookup((Comparable) entry.getValue(),
228
return (node != null) && node.getData(KEY).equals(key);
231
public boolean remove(Object o) {
233
if (!(o instanceof Map.Entry)) {
237
Map.Entry entry = (Map.Entry) o;
238
Object key = entry.getKey();
239
Node node = lookup((Comparable) entry.getValue(),
242
if ((node != null) && node.getData(KEY).equals(key)) {
243
doRedBlackDelete(node);
252
return DoubleOrderedMap.this.size();
255
public void clear() {
256
DoubleOrderedMap.this.clear();
261
return setOfEntries[VALUE];
265
* Returns a set view of the keys contained in this map. The set
266
* is backed by the map, so changes to the map are reflected in
267
* the set, and vice-versa. If the map is modified while an
268
* iteration over the set is in progress, the results of the
269
* iteration are undefined. The set supports element removal,
270
* which removes the corresponding mapping from the map, via the
271
* Iterator.remove, Set.remove, removeAll, retainAll, and clear
272
* operations. It does not support the add or addAll
275
* The difference between this method and keySet is that keySet's
276
* iterator() method returns an iterator that iterates over the
277
* keys in ascending order by key. This method's iterator method
278
* iterates over the keys in ascending order by value.
280
* @return a set view of the keys contained in this map.
282
public Set keySetByValue() {
284
if (setOfKeys[VALUE] == null) {
285
setOfKeys[VALUE] = new AbstractSet() {
287
public Iterator iterator() {
289
return new DoubleOrderedMapIterator(VALUE) {
291
protected Object doGetNext() {
292
return lastReturnedNode.getData(KEY);
298
return DoubleOrderedMap.this.size();
301
public boolean contains(Object o) {
302
return containsKey(o);
305
public boolean remove(Object o) {
307
int oldnodeCount = nodeCount;
309
DoubleOrderedMap.this.remove(o);
311
return nodeCount != oldnodeCount;
314
public void clear() {
315
DoubleOrderedMap.this.clear();
320
return setOfKeys[VALUE];
324
* Returns a collection view of the values contained in this
325
* map. The collection is backed by the map, so changes to the map
326
* are reflected in the collection, and vice-versa. If the map is
327
* modified while an iteration over the collection is in progress,
328
* the results of the iteration are undefined. The collection
329
* supports element removal, which removes the corresponding
330
* mapping from the map, via the Iterator.remove,
331
* Collection.remove, removeAll, retainAll and clear operations.
332
* It does not support the add or addAll operations.<p>
334
* The difference between this method and values is that values's
335
* iterator() method returns an iterator that iterates over the
336
* values in ascending order by key. This method's iterator method
337
* iterates over the values in ascending order by key.
339
* @return a collection view of the values contained in this map.
341
public Collection valuesByValue() {
343
if (collectionOfValues[VALUE] == null) {
344
collectionOfValues[VALUE] = new AbstractCollection() {
346
public Iterator iterator() {
348
return new DoubleOrderedMapIterator(VALUE) {
350
protected Object doGetNext() {
351
return lastReturnedNode.getData(VALUE);
357
return DoubleOrderedMap.this.size();
360
public boolean contains(Object o) {
361
return containsValue(o);
364
public boolean remove(Object o) {
366
int oldnodeCount = nodeCount;
370
return nodeCount != oldnodeCount;
373
public boolean removeAll(Collection c) {
375
boolean modified = false;
376
Iterator iter = c.iterator();
378
while (iter.hasNext()) {
379
if (removeValue(iter.next()) != null) {
387
public void clear() {
388
DoubleOrderedMap.this.clear();
393
return collectionOfValues[VALUE];
397
* common remove logic (remove by key or remove by value)
399
* @param o the key, or value, that we're looking for
400
* @param index KEY or VALUE
402
* @return the key, if remove by value, or the value, if remove by
403
* key. null if the specified key or value could not be
406
private Object doRemove(final Comparable o, final int index) {
408
Node node = lookup(o, index);
412
rval = node.getData(oppositeIndex(index));
414
doRedBlackDelete(node);
421
* common get logic, used to get by key or get by value
423
* @param o the key or value that we're looking for
424
* @param index KEY or VALUE
426
* @return the key (if the value was mapped) or the value (if the
427
* key was mapped); null if we couldn't find the specified
430
private Object doGet(final Comparable o, final int index) {
432
checkNonNullComparable(o, index);
434
Node node = lookup(o, index);
436
return ((node == null)
438
: node.getData(oppositeIndex(index)));
442
* Get the opposite index of the specified index
444
* @param index KEY or VALUE
446
* @return VALUE (if KEY was specified), else KEY
448
private int oppositeIndex(final int index) {
450
// old trick ... to find the opposite of a value, m or n,
451
// subtract the value from the sum of the two possible
452
// values. (m + n) - m = n; (m + n) - n = m
453
return SUM_OF_INDICES - index;
457
* do the actual lookup of a piece of data
459
* @param data the key or value to be looked up
460
* @param index KEY or VALUE
462
* @return the desired Node, or null if there is no mapping of the
465
private Node lookup(final Comparable data, final int index) {
468
Node node = rootNode[index];
470
while (node != null) {
471
int cmp = compare(data, node.getData(index));
479
? node.getLeft(index)
480
: node.getRight(index);
488
* Compare two objects
490
* @param o1 the first object
491
* @param o2 the second object
493
* @return negative value if o1 < o2; 0 if o1 == o2; positive
496
private static int compare(final Comparable o1, final Comparable o2) {
497
return ((Comparable) o1).compareTo(o2);
501
* find the least node from a given node. very useful for starting
502
* a sorting iterator ...
504
* @param node the node from which we will start searching
505
* @param index KEY or VALUE
507
* @return the smallest node, from the specified node, in the
510
private static Node leastNode(final Node node, final int index) {
515
while (rval.getLeft(index) != null) {
516
rval = rval.getLeft(index);
524
* get the next larger node from the specified node
526
* @param node the node to be searched from
527
* @param index KEY or VALUE
529
* @return the specified node
531
private Node nextGreater(final Node node, final int index) {
537
} else if (node.getRight(index) != null) {
539
// everything to the node's right is larger. The least of
540
// the right node's descendents is the next larger node
541
rval = leastNode(node.getRight(index), index);
544
// traverse up our ancestry until we find an ancestor that
545
// is null or one whose left child is our ancestor. If we
546
// find a null, then this node IS the largest node in the
547
// tree, and there is no greater node. Otherwise, we are
548
// the largest node in the subtree on that ancestor's left
549
// ... and that ancestor is the next greatest node
550
Node parent = node.getParent(index);
553
while ((parent != null) && (child == parent.getRight(index))) {
555
parent = parent.getParent(index);
565
* copy the color from one node to another, dealing with the fact
566
* that one or both nodes may, in fact, be null
568
* @param from the node whose color we're copying; may be null
569
* @param to the node whose color we're changing; may be null
570
* @param index KEY or VALUE
572
private static void copyColor(final Node from, final Node to,
578
// by default, make it black
581
to.copyColor(from, index);
587
* is the specified node red? if the node does not exist, no, it's
590
* @param node the node (may be null) in question
591
* @param index KEY or VALUE
593
private static boolean isRed(final Node node, final int index) {
595
return ((node == null)
597
: node.isRed(index));
601
* is the specified black red? if the node does not exist, sure,
602
* it's black, thank you
604
* @param node the node (may be null) in question
605
* @param index KEY or VALUE
607
private static boolean isBlack(final Node node, final int index) {
609
return ((node == null)
611
: node.isBlack(index));
615
* force a node (if it exists) red
617
* @param node the node (may be null) in question
618
* @param index KEY or VALUE
620
private static void makeRed(final Node node, final int index) {
628
* force a node (if it exists) black
630
* @param node the node (may be null) in question
631
* @param index KEY or VALUE
633
private static void makeBlack(final Node node, final int index) {
636
node.setBlack(index);
641
* get a node's grandparent. mind you, the node, its parent, or
642
* its grandparent may not exist. no problem
644
* @param node the node (may be null) in question
645
* @param index KEY or VALUE
647
private static Node getGrandParent(final Node node, final int index) {
648
return getParent(getParent(node, index), index);
652
* get a node's parent. mind you, the node, or its parent, may not
655
* @param node the node (may be null) in question
656
* @param index KEY or VALUE
658
private static Node getParent(final Node node, final int index) {
660
return ((node == null)
662
: node.getParent(index));
666
* get a node's right child. mind you, the node may not exist. no
669
* @param node the node (may be null) in question
670
* @param index KEY or VALUE
672
private static Node getRightChild(final Node node, final int index) {
674
return (node == null)
676
: node.getRight(index);
680
* get a node's left child. mind you, the node may not exist. no
683
* @param node the node (may be null) in question
684
* @param index KEY or VALUE
686
private static Node getLeftChild(final Node node, final int index) {
688
return (node == null)
690
: node.getLeft(index);
694
* is this node its parent's left child? mind you, the node, or
695
* its parent, may not exist. no problem. if the node doesn't
696
* exist ... it's its non-existent parent's left child. If the
697
* node does exist but has no parent ... no, we're not the
698
* non-existent parent's left child. Otherwise (both the specified
699
* node AND its parent exist), check.
701
* @param node the node (may be null) in question
702
* @param index KEY or VALUE
704
private static boolean isLeftChild(final Node node, final int index) {
706
return (node == null)
708
: ((node.getParent(index) == null)
710
: (node == node.getParent(index).getLeft(index)));
714
* is this node its parent's right child? mind you, the node, or
715
* its parent, may not exist. no problem. if the node doesn't
716
* exist ... it's its non-existent parent's right child. If the
717
* node does exist but has no parent ... no, we're not the
718
* non-existent parent's right child. Otherwise (both the
719
* specified node AND its parent exist), check.
721
* @param node the node (may be null) in question
722
* @param index KEY or VALUE
724
private static boolean isRightChild(final Node node, final int index) {
726
return (node == null)
728
: ((node.getParent(index) == null)
730
: (node == node.getParent(index).getRight(index)));
734
* do a rotate left. standard fare in the world of balanced trees
736
* @param node the node to be rotated
737
* @param index KEY or VALUE
739
private void rotateLeft(final Node node, final int index) {
741
Node rightChild = node.getRight(index);
743
node.setRight(rightChild.getLeft(index), index);
745
if (rightChild.getLeft(index) != null) {
746
rightChild.getLeft(index).setParent(node, index);
749
rightChild.setParent(node.getParent(index), index);
751
if (node.getParent(index) == null) {
753
// node was the root ... now its right child is the root
754
rootNode[index] = rightChild;
755
} else if (node.getParent(index).getLeft(index) == node) {
756
node.getParent(index).setLeft(rightChild, index);
758
node.getParent(index).setRight(rightChild, index);
761
rightChild.setLeft(node, index);
762
node.setParent(rightChild, index);
766
* do a rotate right. standard fare in the world of balanced trees
768
* @param node the node to be rotated
769
* @param index KEY or VALUE
771
private void rotateRight(final Node node, final int index) {
773
Node leftChild = node.getLeft(index);
775
node.setLeft(leftChild.getRight(index), index);
777
if (leftChild.getRight(index) != null) {
778
leftChild.getRight(index).setParent(node, index);
781
leftChild.setParent(node.getParent(index), index);
783
if (node.getParent(index) == null) {
785
// node was the root ... now its left child is the root
786
rootNode[index] = leftChild;
787
} else if (node.getParent(index).getRight(index) == node) {
788
node.getParent(index).setRight(leftChild, index);
790
node.getParent(index).setLeft(leftChild, index);
793
leftChild.setRight(node, index);
794
node.setParent(leftChild, index);
798
* complicated red-black insert stuff. Based on Sun's TreeMap
799
* implementation, though it's barely recognizeable any more
801
* @param insertedNode the node to be inserted
802
* @param index KEY or VALUE
804
private void doRedBlackInsert(final Node insertedNode, final int index)
807
Node currentNode = insertedNode;
809
makeRed(currentNode, index);
811
while ((currentNode != null) && (currentNode != rootNode[index])
812
&& (isRed(currentNode.getParent(index), index))) {
813
if (isLeftChild(getParent(currentNode, index), index)) {
814
Node y = getRightChild(getGrandParent(currentNode, index),
817
if (isRed(y, index)) {
818
makeBlack(getParent(currentNode, index), index);
820
makeRed(getGrandParent(currentNode, index), index);
822
currentNode = getGrandParent(currentNode, index);
824
if (isRightChild(currentNode, index)) {
825
currentNode = getParent(currentNode, index);
827
rotateLeft(currentNode, index);
830
makeBlack(getParent(currentNode, index), index);
831
makeRed(getGrandParent(currentNode, index), index);
833
if (getGrandParent(currentNode, index) != null) {
834
rotateRight(getGrandParent(currentNode, index),
840
// just like clause above, except swap left for right
841
Node y = getLeftChild(getGrandParent(currentNode, index),
844
if (isRed(y, index)) {
845
makeBlack(getParent(currentNode, index), index);
847
makeRed(getGrandParent(currentNode, index), index);
849
currentNode = getGrandParent(currentNode, index);
851
if (isLeftChild(currentNode, index)) {
852
currentNode = getParent(currentNode, index);
854
rotateRight(currentNode, index);
857
makeBlack(getParent(currentNode, index), index);
858
makeRed(getGrandParent(currentNode, index), index);
860
if (getGrandParent(currentNode, index) != null) {
861
rotateLeft(getGrandParent(currentNode, index),
868
makeBlack(rootNode[index], index);
872
* complicated red-black delete stuff. Based on Sun's TreeMap
873
* implementation, though it's barely recognizeable any more
875
* @param deletedNode the node to be deleted
877
private void doRedBlackDelete(final Node deletedNode) {
879
for (int index = FIRST_INDEX; index < NUMBER_OF_INDICES; index++) {
881
// if deleted node has both left and children, swap with
882
// the next greater node
883
if ((deletedNode.getLeft(index) != null)
884
&& (deletedNode.getRight(index) != null)) {
885
swapPosition(nextGreater(deletedNode, index), deletedNode,
889
Node replacement = ((deletedNode.getLeft(index) != null)
890
? deletedNode.getLeft(index)
891
: deletedNode.getRight(index));
893
if (replacement != null) {
894
replacement.setParent(deletedNode.getParent(index), index);
896
if (deletedNode.getParent(index) == null) {
897
rootNode[index] = replacement;
898
} else if (deletedNode
899
== deletedNode.getParent(index).getLeft(index)) {
900
deletedNode.getParent(index).setLeft(replacement,
903
deletedNode.getParent(index).setRight(replacement,
907
deletedNode.setLeft(null, index);
908
deletedNode.setRight(null, index);
909
deletedNode.setParent(null, index);
911
if (isBlack(deletedNode, index)) {
912
doRedBlackDeleteFixup(replacement, index);
916
// replacement is null
917
if (deletedNode.getParent(index) == null) {
920
rootNode[index] = null;
923
// deleted node had no children
924
if (isBlack(deletedNode, index)) {
925
doRedBlackDeleteFixup(deletedNode, index);
928
if (deletedNode.getParent(index) != null) {
930
== deletedNode.getParent(index)
932
deletedNode.getParent(index).setLeft(null,
935
deletedNode.getParent(index).setRight(null,
939
deletedNode.setParent(null, index);
949
* complicated red-black delete stuff. Based on Sun's TreeMap
950
* implementation, though it's barely recognizeable any more. This
951
* rebalances the tree (somewhat, as red-black trees are not
952
* perfectly balanced -- perfect balancing takes longer)
954
* @param replacementNode the node being replaced
955
* @param index KEY or VALUE
957
private void doRedBlackDeleteFixup(final Node replacementNode,
960
Node currentNode = replacementNode;
962
while ((currentNode != rootNode[index])
963
&& (isBlack(currentNode, index))) {
964
if (isLeftChild(currentNode, index)) {
966
getRightChild(getParent(currentNode, index), index);
968
if (isRed(siblingNode, index)) {
969
makeBlack(siblingNode, index);
970
makeRed(getParent(currentNode, index), index);
971
rotateLeft(getParent(currentNode, index), index);
973
siblingNode = getRightChild(getParent(currentNode,
978
if (isBlack(getLeftChild(siblingNode, index), index)
979
&& isBlack(getRightChild(siblingNode, index),
981
makeRed(siblingNode, index);
983
currentNode = getParent(currentNode, index);
985
if (isBlack(getRightChild(siblingNode, index), index)) {
986
makeBlack(getLeftChild(siblingNode, index), index);
987
makeRed(siblingNode, index);
988
rotateRight(siblingNode, index);
991
getRightChild(getParent(currentNode, index),
995
copyColor(getParent(currentNode, index), siblingNode,
997
makeBlack(getParent(currentNode, index), index);
998
makeBlack(getRightChild(siblingNode, index), index);
999
rotateLeft(getParent(currentNode, index), index);
1001
currentNode = rootNode[index];
1004
Node siblingNode = getLeftChild(getParent(currentNode,
1008
if (isRed(siblingNode, index)) {
1009
makeBlack(siblingNode, index);
1010
makeRed(getParent(currentNode, index), index);
1011
rotateRight(getParent(currentNode, index), index);
1013
siblingNode = getLeftChild(getParent(currentNode,
1018
if (isBlack(getRightChild(siblingNode, index), index)
1019
&& isBlack(getLeftChild(siblingNode, index), index))
1021
makeRed(siblingNode, index);
1023
currentNode = getParent(currentNode, index);
1025
if (isBlack(getLeftChild(siblingNode, index), index)) {
1026
makeBlack(getRightChild(siblingNode, index), index);
1027
makeRed(siblingNode, index);
1028
rotateLeft(siblingNode, index);
1031
getLeftChild(getParent(currentNode, index),
1035
copyColor(getParent(currentNode, index), siblingNode,
1037
makeBlack(getParent(currentNode, index), index);
1038
makeBlack(getLeftChild(siblingNode, index), index);
1039
rotateRight(getParent(currentNode, index), index);
1041
currentNode = rootNode[index];
1046
makeBlack(currentNode, index);
1050
* swap two nodes (except for their content), taking care of
1051
* special cases where one is the other's parent ... hey, it
1055
* @param y another node
1056
* @param index KEY or VALUE
1058
private void swapPosition(final Node x, final Node y, final int index) {
1060
// Save initial values.
1061
Node xFormerParent = x.getParent(index);
1062
Node xFormerLeftChild = x.getLeft(index);
1063
Node xFormerRightChild = x.getRight(index);
1064
Node yFormerParent = y.getParent(index);
1065
Node yFormerLeftChild = y.getLeft(index);
1066
Node yFormerRightChild = y.getRight(index);
1067
boolean xWasLeftChild =
1068
(x.getParent(index) != null)
1069
&& (x == x.getParent(index).getLeft(index));
1070
boolean yWasLeftChild =
1071
(y.getParent(index) != null)
1072
&& (y == y.getParent(index).getLeft(index));
1074
// Swap, handling special cases of one being the other's parent.
1075
if (x == yFormerParent) { // x was y's parent
1076
x.setParent(y, index);
1078
if (yWasLeftChild) {
1079
y.setLeft(x, index);
1080
y.setRight(xFormerRightChild, index);
1082
y.setRight(x, index);
1083
y.setLeft(xFormerLeftChild, index);
1086
x.setParent(yFormerParent, index);
1088
if (yFormerParent != null) {
1089
if (yWasLeftChild) {
1090
yFormerParent.setLeft(x, index);
1092
yFormerParent.setRight(x, index);
1096
y.setLeft(xFormerLeftChild, index);
1097
y.setRight(xFormerRightChild, index);
1100
if (y == xFormerParent) { // y was x's parent
1101
y.setParent(x, index);
1103
if (xWasLeftChild) {
1104
x.setLeft(y, index);
1105
x.setRight(yFormerRightChild, index);
1107
x.setRight(y, index);
1108
x.setLeft(yFormerLeftChild, index);
1111
y.setParent(xFormerParent, index);
1113
if (xFormerParent != null) {
1114
if (xWasLeftChild) {
1115
xFormerParent.setLeft(y, index);
1117
xFormerParent.setRight(y, index);
1121
x.setLeft(yFormerLeftChild, index);
1122
x.setRight(yFormerRightChild, index);
1125
// Fix children's parent pointers
1126
if (x.getLeft(index) != null) {
1127
x.getLeft(index).setParent(x, index);
1130
if (x.getRight(index) != null) {
1131
x.getRight(index).setParent(x, index);
1134
if (y.getLeft(index) != null) {
1135
y.getLeft(index).setParent(y, index);
1138
if (y.getRight(index) != null) {
1139
y.getRight(index).setParent(y, index);
1142
x.swapColors(y, index);
1144
// Check if root changed
1145
if (rootNode[index] == x) {
1146
rootNode[index] = y;
1147
} else if (rootNode[index] == y) {
1148
rootNode[index] = x;
1153
* check if an object is fit to be proper input ... has to be
1154
* Comparable and non-null
1156
* @param o the object being checked
1157
* @param index KEY or VALUE (used to put the right word in the
1158
* exception message)
1160
* @exception NullPointerException if o is null
1161
* @exception ClassCastException if o is not Comparable
1163
private static void checkNonNullComparable(final Object o,
1167
throw new NullPointerException(dataName[index]
1168
+ " cannot be null");
1171
if (!(o instanceof Comparable)) {
1172
throw new ClassCastException(dataName[index]
1173
+ " must be Comparable");
1178
* check a key for validity (non-null and implements Comparable)
1180
* @param key the key to be checked
1182
* @exception NullPointerException if key is null
1183
* @exception ClassCastException if key is not Comparable
1185
private static void checkKey(final Object key) {
1186
checkNonNullComparable(key, KEY);
1190
* check a value for validity (non-null and implements Comparable)
1192
* @param value the value to be checked
1194
* @exception NullPointerException if value is null
1195
* @exception ClassCastException if value is not Comparable
1197
private static void checkValue(final Object value) {
1198
checkNonNullComparable(value, VALUE);
1202
* check a key and a value for validity (non-null and implements
1205
* @param key the key to be checked
1206
* @param value the value to be checked
1208
* @exception NullPointerException if key or value is null
1209
* @exception ClassCastException if key or value is not Comparable
1211
private static void checkKeyAndValue(final Object key,
1212
final Object value) {
1218
* increment the modification count -- used to check for
1219
* concurrent modification of the map through the map and through
1220
* an Iterator from one of its Set or Collection views
1222
private void modify() {
1227
* bump up the size and note that the map has changed
1229
private void grow() {
1237
* decrement the size and note that the map has changed
1239
private void shrink() {
1247
* insert a node by its value
1249
* @param newNode the node to be inserted
1251
* @exception IllegalArgumentException if the node already exists
1252
* in the value mapping
1254
private void insertValue(final Node newNode)
1255
throws IllegalArgumentException {
1257
Node node = rootNode[VALUE];
1260
int cmp = compare(newNode.getData(VALUE), node.getData(VALUE));
1263
throw new IllegalArgumentException(
1264
"Cannot store a duplicate value (\""
1265
+ newNode.getData(VALUE) + "\") in this Map");
1266
} else if (cmp < 0) {
1267
if (node.getLeft(VALUE) != null) {
1268
node = node.getLeft(VALUE);
1270
node.setLeft(newNode, VALUE);
1271
newNode.setParent(node, VALUE);
1272
doRedBlackInsert(newNode, VALUE);
1277
if (node.getRight(VALUE) != null) {
1278
node = node.getRight(VALUE);
1280
node.setRight(newNode, VALUE);
1281
newNode.setParent(node, VALUE);
1282
doRedBlackInsert(newNode, VALUE);
1290
/* ********** START implementation of Map ********** */
1293
* Returns the number of key-value mappings in this map. If the
1294
* map contains more than Integer.MAXVALUE elements, returns
1297
* @return the number of key-value mappings in this map.
1304
* Returns true if this map contains a mapping for the specified
1307
* @param key key whose presence in this map is to be tested.
1309
* @return true if this map contains a mapping for the specified
1312
* @exception ClassCastException if the key is of an inappropriate
1313
* type for this map.
1314
* @exception NullPointerException if the key is null
1316
public boolean containsKey(final Object key)
1317
throws ClassCastException, NullPointerException {
1321
return lookup((Comparable) key, KEY) != null;
1325
* Returns true if this map maps one or more keys to the
1328
* @param value value whose presence in this map is to be tested.
1330
* @return true if this map maps one or more keys to the specified
1333
public boolean containsValue(final Object value) {
1337
return lookup((Comparable) value, VALUE) != null;
1341
* Returns the value to which this map maps the specified
1342
* key. Returns null if the map contains no mapping for this key.
1344
* @param key key whose associated value is to be returned.
1346
* @return the value to which this map maps the specified key, or
1347
* null if the map contains no mapping for this key.
1349
* @exception ClassCastException if the key is of an inappropriate
1350
* type for this map.
1351
* @exception NullPointerException if the key is null
1353
public Object get(final Object key)
1354
throws ClassCastException, NullPointerException {
1355
return doGet((Comparable) key, KEY);
1359
* Associates the specified value with the specified key in this
1362
* @param key key with which the specified value is to be
1364
* @param value value to be associated with the specified key.
1368
* @exception ClassCastException if the class of the specified key
1369
* or value prevents it from being
1370
* stored in this map.
1371
* @exception NullPointerException if the specified key or value
1373
* @exception IllegalArgumentException if the key duplicates an
1374
* existing key, or if the
1375
* value duplicates an
1378
public Object put(final Object key, final Object value)
1379
throws ClassCastException, NullPointerException,
1380
IllegalArgumentException {
1382
checkKeyAndValue(key, value);
1384
Node node = rootNode[KEY];
1387
Node root = new Node((Comparable) key, (Comparable) value);
1389
rootNode[KEY] = root;
1390
rootNode[VALUE] = root;
1395
int cmp = compare((Comparable) key, node.getData(KEY));
1398
throw new IllegalArgumentException(
1399
"Cannot store a duplicate key (\"" + key
1400
+ "\") in this Map");
1401
} else if (cmp < 0) {
1402
if (node.getLeft(KEY) != null) {
1403
node = node.getLeft(KEY);
1405
Node newNode = new Node((Comparable) key,
1406
(Comparable) value);
1408
insertValue(newNode);
1409
node.setLeft(newNode, KEY);
1410
newNode.setParent(node, KEY);
1411
doRedBlackInsert(newNode, KEY);
1417
if (node.getRight(KEY) != null) {
1418
node = node.getRight(KEY);
1420
Node newNode = new Node((Comparable) key,
1421
(Comparable) value);
1423
insertValue(newNode);
1424
node.setRight(newNode, KEY);
1425
newNode.setParent(node, KEY);
1426
doRedBlackInsert(newNode, KEY);
1439
* Removes the mapping for this key from this map if present
1441
* @param key key whose mapping is to be removed from the map.
1443
* @return previous value associated with specified key, or null
1444
* if there was no mapping for key.
1446
public Object remove(final Object key) {
1447
return doRemove((Comparable) key, KEY);
1451
* Removes all mappings from this map
1453
public void clear() {
1458
rootNode[KEY] = null;
1459
rootNode[VALUE] = null;
1463
* Returns a set view of the keys contained in this map. The set
1464
* is backed by the map, so changes to the map are reflected in
1465
* the set, and vice-versa. If the map is modified while an
1466
* iteration over the set is in progress, the results of the
1467
* iteration are undefined. The set supports element removal,
1468
* which removes the corresponding mapping from the map, via the
1469
* Iterator.remove, Set.remove, removeAll, retainAll, and clear
1470
* operations. It does not support the add or addAll operations.
1472
* @return a set view of the keys contained in this map.
1474
public Set keySet() {
1476
if (setOfKeys[KEY] == null) {
1477
setOfKeys[KEY] = new AbstractSet() {
1479
public Iterator iterator() {
1481
return new DoubleOrderedMapIterator(KEY) {
1483
protected Object doGetNext() {
1484
return lastReturnedNode.getData(KEY);
1490
return DoubleOrderedMap.this.size();
1493
public boolean contains(Object o) {
1494
return containsKey(o);
1497
public boolean remove(Object o) {
1499
int oldNodeCount = nodeCount;
1501
DoubleOrderedMap.this.remove(o);
1503
return nodeCount != oldNodeCount;
1506
public void clear() {
1507
DoubleOrderedMap.this.clear();
1512
return setOfKeys[KEY];
1516
* Returns a collection view of the values contained in this
1517
* map. The collection is backed by the map, so changes to the map
1518
* are reflected in the collection, and vice-versa. If the map is
1519
* modified while an iteration over the collection is in progress,
1520
* the results of the iteration are undefined. The collection
1521
* supports element removal, which removes the corresponding
1522
* mapping from the map, via the Iterator.remove,
1523
* Collection.remove, removeAll, retainAll and clear operations.
1524
* It does not support the add or addAll operations.
1526
* @return a collection view of the values contained in this map.
1528
public Collection values() {
1530
if (collectionOfValues[KEY] == null) {
1531
collectionOfValues[KEY] = new AbstractCollection() {
1533
public Iterator iterator() {
1535
return new DoubleOrderedMapIterator(KEY) {
1537
protected Object doGetNext() {
1538
return lastReturnedNode.getData(VALUE);
1544
return DoubleOrderedMap.this.size();
1547
public boolean contains(Object o) {
1548
return containsValue(o);
1551
public boolean remove(Object o) {
1553
int oldNodeCount = nodeCount;
1557
return nodeCount != oldNodeCount;
1560
public boolean removeAll(Collection c) {
1562
boolean modified = false;
1563
Iterator iter = c.iterator();
1565
while (iter.hasNext()) {
1566
if (removeValue(iter.next()) != null) {
1574
public void clear() {
1575
DoubleOrderedMap.this.clear();
1580
return collectionOfValues[KEY];
1584
* Returns a set view of the mappings contained in this map. Each
1585
* element in the returned set is a Map.Entry. The set is backed
1586
* by the map, so changes to the map are reflected in the set, and
1587
* vice-versa. If the map is modified while an iteration over the
1588
* set is in progress, the results of the iteration are
1589
* undefined. The set supports element removal, which removes the
1590
* corresponding mapping from the map, via the Iterator.remove,
1591
* Set.remove, removeAll, retainAll and clear operations. It does
1592
* not support the add or addAll operations.
1594
* @return a set view of the mappings contained in this map.
1596
public Set entrySet() {
1598
if (setOfEntries[KEY] == null) {
1599
setOfEntries[KEY] = new AbstractSet() {
1601
public Iterator iterator() {
1603
return new DoubleOrderedMapIterator(KEY) {
1605
protected Object doGetNext() {
1606
return lastReturnedNode;
1611
public boolean contains(Object o) {
1613
if (!(o instanceof Map.Entry)) {
1617
Map.Entry entry = (Map.Entry) o;
1618
Object value = entry.getValue();
1619
Node node = lookup((Comparable) entry.getKey(),
1622
return (node != null)
1623
&& node.getData(VALUE).equals(value);
1626
public boolean remove(Object o) {
1628
if (!(o instanceof Map.Entry)) {
1632
Map.Entry entry = (Map.Entry) o;
1633
Object value = entry.getValue();
1634
Node node = lookup((Comparable) entry.getKey(),
1637
if ((node != null) && node.getData(VALUE).equals(value))
1639
doRedBlackDelete(node);
1648
return DoubleOrderedMap.this.size();
1651
public void clear() {
1652
DoubleOrderedMap.this.clear();
1657
return setOfEntries[KEY];
1660
/* ********** END implementation of Map ********** */
1661
private abstract class DoubleOrderedMapIterator implements Iterator {
1663
private int expectedModifications;
1664
protected Node lastReturnedNode;
1665
private Node nextNode;
1666
private int iteratorType;
1673
DoubleOrderedMapIterator(final int type) {
1675
iteratorType = type;
1676
expectedModifications = DoubleOrderedMap.this.modifications;
1677
lastReturnedNode = null;
1678
nextNode = leastNode(rootNode[iteratorType],
1683
* @return 'next', whatever that means for a given kind of
1684
* DoubleOrderedMapIterator
1686
protected abstract Object doGetNext();
1688
/* ********** START implementation of Iterator ********** */
1691
* @return true if the iterator has more elements.
1693
public final boolean hasNext() {
1694
return nextNode != null;
1698
* @return the next element in the iteration.
1700
* @exception NoSuchElementException if iteration has no more
1702
* @exception ConcurrentModificationException if the
1703
* DoubleOrderedMap is
1708
public final Object next()
1709
throws NoSuchElementException,
1710
ConcurrentModificationException {
1712
if (nextNode == null) {
1713
throw new NoSuchElementException();
1716
if (modifications != expectedModifications) {
1717
throw new ConcurrentModificationException();
1720
lastReturnedNode = nextNode;
1721
nextNode = nextGreater(nextNode, iteratorType);
1727
* Removes from the underlying collection the last element
1728
* returned by the iterator. This method can be called only
1729
* once per call to next. The behavior of an iterator is
1730
* unspecified if the underlying collection is modified while
1731
* the iteration is in progress in any way other than by
1732
* calling this method.
1734
* @exception IllegalStateException if the next method has not
1735
* yet been called, or the
1736
* remove method has already
1737
* been called after the last
1738
* call to the next method.
1739
* @exception ConcurrentModificationException if the
1740
* DoubleOrderedMap is
1745
public final void remove()
1746
throws IllegalStateException,
1747
ConcurrentModificationException {
1749
if (lastReturnedNode == null) {
1750
throw new IllegalStateException();
1753
if (modifications != expectedModifications) {
1754
throw new ConcurrentModificationException();
1757
doRedBlackDelete(lastReturnedNode);
1759
expectedModifications++;
1761
lastReturnedNode = null;
1764
/* ********** END implementation of Iterator ********** */
1765
} // end private abstract class DoubleOrderedMapIterator
1767
// final for performance
1768
private static final class Node implements Map.Entry {
1770
private Comparable[] data;
1771
private Node[] leftNode;
1772
private Node[] rightNode;
1773
private Node[] parentNode;
1774
private boolean[] blackColor;
1775
private int hashcodeValue;
1776
private boolean calculatedHashCode;
1779
* Make a new cell with given key and value, and with null
1780
* links, and black (true) colors.
1785
Node(final Comparable key, final Comparable value) {
1787
data = new Comparable[]{ key, value };
1788
leftNode = new Node[]{ null, null };
1789
rightNode = new Node[]{ null, null };
1790
parentNode = new Node[]{ null, null };
1791
blackColor = new boolean[]{ true, true };
1792
calculatedHashCode = false;
1796
* get the specified data
1798
* @param index KEY or VALUE
1800
* @return the key or value
1802
private Comparable getData(final int index) {
1807
* Set this node's left node
1809
* @param node the new left node
1810
* @param index KEY or VALUE
1812
private void setLeft(final Node node, final int index) {
1813
leftNode[index] = node;
1819
* @param index KEY or VALUE
1821
* @return the left node -- may be null
1823
private Node getLeft(final int index) {
1824
return leftNode[index];
1828
* Set this node's right node
1830
* @param node the new right node
1831
* @param index KEY or VALUE
1833
private void setRight(final Node node, final int index) {
1834
rightNode[index] = node;
1838
* get the right node
1840
* @param index KEY or VALUE
1842
* @return the right node -- may be null
1844
private Node getRight(final int index) {
1845
return rightNode[index];
1849
* Set this node's parent node
1851
* @param node the new parent node
1852
* @param index KEY or VALUE
1854
private void setParent(final Node node, final int index) {
1855
parentNode[index] = node;
1859
* get the parent node
1861
* @param index KEY or VALUE
1863
* @return the parent node -- may be null
1865
private Node getParent(final int index) {
1866
return parentNode[index];
1870
* exchange colors with another node
1872
* @param node the node to swap with
1873
* @param index KEY or VALUE
1875
private void swapColors(final Node node, final int index) {
1877
// Swap colors -- old hacker's trick
1878
blackColor[index] ^= node.blackColor[index];
1879
node.blackColor[index] ^= blackColor[index];
1880
blackColor[index] ^= node.blackColor[index];
1884
* is this node black?
1886
* @param index KEY or VALUE
1888
* @return true if black (which is represented as a true boolean)
1890
private boolean isBlack(final int index) {
1891
return blackColor[index];
1897
* @param index KEY or VALUE
1899
* @return true if non-black
1901
private boolean isRed(final int index) {
1902
return !blackColor[index];
1906
* make this node black
1908
* @param index KEY or VALUE
1910
private void setBlack(final int index) {
1911
blackColor[index] = true;
1915
* make this node red
1917
* @param index KEY or VALUE
1919
private void setRed(final int index) {
1920
blackColor[index] = false;
1924
* make this node the same color as another
1926
* @param node the node whose color we're adopting
1927
* @param index KEY or VALUE
1929
private void copyColor(final Node node, final int index) {
1930
blackColor[index] = node.blackColor[index];
1933
/* ********** START implementation of Map.Entry ********** */
1936
* @return the key corresponding to this entry.
1938
public Object getKey() {
1943
* @return the value corresponding to this entry.
1945
public Object getValue() {
1950
* Optional operation that is not permitted in this
1955
* @return does not return
1957
* @exception UnsupportedOperationException
1959
public Object setValue(Object ignored)
1960
throws UnsupportedOperationException {
1961
throw new UnsupportedOperationException(
1962
"Map.Entry.setValue is not supported");
1966
* Compares the specified object with this entry for equality.
1967
* Returns true if the given object is also a map entry and
1968
* the two entries represent the same mapping.
1970
* @param o object to be compared for equality with this map
1972
* @return true if the specified object is equal to this map
1975
public boolean equals(Object o) {
1981
if (!(o instanceof Map.Entry)) {
1985
Map.Entry e = (Map.Entry) o;
1987
return data[KEY].equals(e.getKey())
1988
&& data[VALUE].equals(e.getValue());
1992
* @return the hash code value for this map entry.
1994
public int hashCode() {
1996
if (!calculatedHashCode) {
1997
hashcodeValue = data[KEY].hashCode()
1998
^ data[VALUE].hashCode();
1999
calculatedHashCode = true;
2002
return hashcodeValue;
2005
/* ********** END implementation of Map.Entry ********** */
2007
} // end public class DoubleOrderedMap