View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.bidimap;
18  
19  import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.KEY;
20  import static org.apache.commons.collections4.bidimap.TreeBidiMap.DataElement.VALUE;
21  
22  import java.io.IOException;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectOutputStream;
25  import java.io.Serializable;
26  import java.util.AbstractSet;
27  import java.util.ConcurrentModificationException;
28  import java.util.Iterator;
29  import java.util.Map;
30  import java.util.NoSuchElementException;
31  import java.util.Objects;
32  import java.util.Set;
33  import java.util.TreeMap;
34  
35  import org.apache.commons.collections4.KeyValue;
36  import org.apache.commons.collections4.MapIterator;
37  import org.apache.commons.collections4.OrderedBidiMap;
38  import org.apache.commons.collections4.OrderedIterator;
39  import org.apache.commons.collections4.OrderedMapIterator;
40  import org.apache.commons.collections4.iterators.EmptyOrderedMapIterator;
41  import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
42  
43  /**
44   * Red-Black tree-based implementation of BidiMap where all objects added
45   * implement the {@code Comparable} interface.
46   * <p>
47   * This class guarantees that the map will be in both ascending key order
48   * and ascending value order, sorted according to the natural order for
49   * the key's and value's classes.
50   * </p>
51   * <p>
52   * This Map is intended for applications that need to be able to look
53   * up a key-value pairing by either key or value, and need to do so
54   * with equal efficiency.
55   * </p>
56   * <p>
57   * While that goal could be accomplished by taking a pair of TreeMaps
58   * and redirecting requests to the appropriate TreeMap (for example,
59   * containsKey would be directed to the TreeMap that maps values to
60   * keys, containsValue would be directed to the TreeMap that maps keys
61   * to values), there are problems with that implementation.
62   * If the data contained in the TreeMaps is large, the cost of redundant
63   * storage becomes significant. The {@link DualTreeBidiMap} and
64   * {@link DualHashBidiMap} implementations use this approach.
65   * </p>
66   * <p>
67   * This solution keeps minimizes the data storage by holding data only once.
68   * The red-black algorithm is based on {@link TreeMap}, but has been modified
69   * to simultaneously map a tree node by key and by value. This doubles the
70   * cost of put operations (but so does using two TreeMaps), and nearly doubles
71   * the cost of remove operations (there is a savings in that the lookup of the
72   * node to be removed only has to be performed once). And since only one node
73   * contains the key and value, storage is significantly less than that
74   * required by two TreeMaps.
75   * </p>
76   * <p>
77   * The Map.Entry instances returned by the appropriate methods will
78   * not allow setValue() and will throw an
79   * UnsupportedOperationException on attempts to call that method.
80   * </p>
81   *
82   * @param <K> The type of the keys in this map
83   * @param <V> The type of the values in this map
84   * @since 3.0 (previously DoubleOrderedMap v2.0)
85   */
86  public class TreeBidiMap<K extends Comparable<K>, V extends Comparable<V>>
87      implements OrderedBidiMap<K, V>, Serializable {
88  
89      /**
90       * A view of this map.
91       */
92      abstract class AbstractView<E> extends AbstractSet<E> {
93  
94          /** Whether to return KEY or VALUE order. */
95          final DataElement orderType;
96  
97          /**
98           * Constructs a new instance.
99           *
100          * @param orderType  The KEY or VALUE int for the order
101          */
102         AbstractView(final DataElement orderType) {
103             this.orderType = orderType;
104         }
105 
106         @Override
107         public void clear() {
108             TreeBidiMap.this.clear();
109         }
110 
111         @Override
112         public int size() {
113             return TreeBidiMap.this.size();
114         }
115     }
116 
117     /**
118      * An iterator over the map.
119      */
120     abstract class AbstractViewIterator {
121 
122         /** Whether to return KEY or VALUE order. */
123         private final DataElement orderType;
124 
125         /** The last node returned by the iterator. */
126         Node<K, V> lastReturnedNode;
127 
128         /** The next node to be returned by the iterator. */
129         private Node<K, V> nextNode;
130 
131         /** The previous node in the sequence returned by the iterator. */
132         private Node<K, V> previousNode;
133 
134         /** The modification count. */
135         private int expectedModifications;
136 
137         /**
138          * Constructs a new instance.
139          *
140          * @param orderType  The KEY or VALUE int for the order
141          */
142         AbstractViewIterator(final DataElement orderType) {
143             this.orderType = orderType;
144             expectedModifications = modifications;
145             nextNode = leastNode(rootNode[orderType.ordinal()], orderType);
146             lastReturnedNode = null;
147             previousNode = null;
148         }
149 
150         public final boolean hasNext() {
151             return nextNode != null;
152         }
153 
154         public boolean hasPrevious() {
155             return previousNode != null;
156         }
157 
158         protected Node<K, V> navigateNext() {
159             if (nextNode == null) {
160                 throw new NoSuchElementException();
161             }
162             if (modifications != expectedModifications) {
163                 throw new ConcurrentModificationException();
164             }
165             lastReturnedNode = nextNode;
166             previousNode = nextNode;
167             nextNode = nextGreater(nextNode, orderType);
168             return lastReturnedNode;
169         }
170 
171         protected Node<K, V> navigatePrevious() {
172             if (previousNode == null) {
173                 throw new NoSuchElementException();
174             }
175             if (modifications != expectedModifications) {
176                 throw new ConcurrentModificationException();
177             }
178             nextNode = lastReturnedNode;
179             if (nextNode == null) {
180                 nextNode = nextGreater(previousNode, orderType);
181             }
182             lastReturnedNode = previousNode;
183             previousNode = nextSmaller(previousNode, orderType);
184             return lastReturnedNode;
185         }
186 
187         public final void remove() {
188             if (lastReturnedNode == null) {
189                 throw new IllegalStateException();
190             }
191             if (modifications != expectedModifications) {
192                 throw new ConcurrentModificationException();
193             }
194             doRedBlackDelete(lastReturnedNode);
195             expectedModifications++;
196             lastReturnedNode = null;
197             if (nextNode == null) {
198                 previousNode = greatestNode(rootNode[orderType.ordinal()], orderType);
199             } else {
200                 previousNode = nextSmaller(nextNode, orderType);
201             }
202         }
203     }
204 
205     enum DataElement {
206         KEY("key"), VALUE("value");
207 
208         private final String description;
209 
210         /**
211          * Creates a new TreeBidiMap.DataElement.
212          *
213          * @param description  The description for the element
214          */
215         DataElement(final String description) {
216             this.description = description;
217         }
218 
219         @Override
220         public String toString() {
221             return description;
222         }
223     }
224 
225     /**
226      * A view of this map.
227      */
228     final class EntryView extends AbstractView<Map.Entry<K, V>> {
229 
230         EntryView() {
231             super(KEY);
232         }
233 
234         @Override
235         public boolean contains(final Object obj) {
236             if (!(obj instanceof Map.Entry)) {
237                 return false;
238             }
239             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
240             final Object value = entry.getValue();
241             final Node<K, V> node = lookupKey(entry.getKey());
242             return node != null && Objects.equals(node.getValue(), value);
243         }
244 
245         @Override
246         public Iterator<Map.Entry<K, V>> iterator() {
247             return new ViewMapEntryIterator();
248         }
249 
250         @Override
251         public boolean remove(final Object obj) {
252             if (!(obj instanceof Map.Entry)) {
253                 return false;
254             }
255             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
256             final Object value = entry.getValue();
257             final Node<K, V> node = lookupKey(entry.getKey());
258             if (node != null && Objects.equals(node.getValue(), value)) {
259                 doRedBlackDelete(node);
260                 return true;
261             }
262             return false;
263         }
264     }
265 
266     /**
267      * The inverse map implementation.
268      */
269     final class Inverse implements OrderedBidiMap<V, K> {
270 
271         /** Store the keySet once created. */
272         private Set<V> inverseKeySet;
273 
274         /** Store the valuesSet once created. */
275         private Set<K> inverseValuesSet;
276 
277         /** Store the entrySet once created. */
278         private Set<Map.Entry<V, K>> inverseEntrySet;
279 
280         @Override
281         public void clear() {
282             TreeBidiMap.this.clear();
283         }
284 
285         @Override
286         public boolean containsKey(final Object key) {
287             return TreeBidiMap.this.containsValue(key);
288         }
289 
290         @Override
291         public boolean containsValue(final Object value) {
292             return TreeBidiMap.this.containsKey(value);
293         }
294 
295         @Override
296         public Set<Map.Entry<V, K>> entrySet() {
297             if (inverseEntrySet == null) {
298                 inverseEntrySet = new InverseEntryView();
299             }
300             return inverseEntrySet;
301         }
302 
303         @Override
304         public boolean equals(final Object obj) {
305             return TreeBidiMap.this.doEquals(obj, VALUE);
306         }
307 
308         @Override
309         public V firstKey() {
310             if (TreeBidiMap.this.nodeCount == 0) {
311                 throw new NoSuchElementException("Map is empty");
312             }
313             return leastNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue();
314         }
315 
316         @Override
317         public K get(final Object key) {
318             return TreeBidiMap.this.getKey(key);
319         }
320 
321         @Override
322         public V getKey(final Object value) {
323             return TreeBidiMap.this.get(value);
324         }
325 
326         @Override
327         public int hashCode() {
328             return TreeBidiMap.this.doHashCode(VALUE);
329         }
330 
331         @Override
332         public OrderedBidiMap<K, V> inverseBidiMap() {
333             return TreeBidiMap.this;
334         }
335 
336         @Override
337         public boolean isEmpty() {
338             return TreeBidiMap.this.isEmpty();
339         }
340 
341         @Override
342         public Set<V> keySet() {
343             if (inverseKeySet == null) {
344                 inverseKeySet = new ValueView(VALUE);
345             }
346             return inverseKeySet;
347         }
348 
349         @Override
350         public V lastKey() {
351             if (TreeBidiMap.this.nodeCount == 0) {
352                 throw new NoSuchElementException("Map is empty");
353             }
354             return greatestNode(TreeBidiMap.this.rootNode[VALUE.ordinal()], VALUE).getValue();
355         }
356 
357         @Override
358         public OrderedMapIterator<V, K> mapIterator() {
359             if (isEmpty()) {
360                 return EmptyOrderedMapIterator.<V, K>emptyOrderedMapIterator();
361             }
362             return new InverseViewMapIterator(VALUE);
363         }
364 
365         @Override
366         public V nextKey(final V key) {
367             checkKey(key);
368             final Node<K, V> node = nextGreater(TreeBidiMap.this.<V>lookup(key, VALUE), VALUE);
369             return node == null ? null : node.getValue();
370         }
371 
372         @Override
373         public V previousKey(final V key) {
374             checkKey(key);
375             final Node<K, V> node = TreeBidiMap.this.nextSmaller(TreeBidiMap.this.<V>lookup(key, VALUE), VALUE);
376             return node == null ? null : node.getValue();
377         }
378 
379         @Override
380         public K put(final V key, final K value) {
381             final K result = get(key);
382             TreeBidiMap.this.doPut(value, key);
383             return result;
384         }
385 
386         @Override
387         public void putAll(final Map<? extends V, ? extends K> map) {
388             for (final Map.Entry<? extends V, ? extends K> e : map.entrySet()) {
389                 put(e.getKey(), e.getValue());
390             }
391         }
392 
393         @Override
394         public K remove(final Object key) {
395             return TreeBidiMap.this.removeValue(key);
396         }
397 
398         @Override
399         public V removeValue(final Object value) {
400             return TreeBidiMap.this.remove(value);
401         }
402 
403         @Override
404         public int size() {
405             return TreeBidiMap.this.size();
406         }
407 
408         @Override
409         public String toString() {
410             return TreeBidiMap.this.doToString(VALUE);
411         }
412 
413         @Override
414         public Set<K> values() {
415             if (inverseValuesSet == null) {
416                 inverseValuesSet = new KeyView(VALUE);
417             }
418             return inverseValuesSet;
419         }
420     }
421 
422     /**
423      * A view of this map.
424      */
425     final class InverseEntryView extends AbstractView<Map.Entry<V, K>> {
426 
427         InverseEntryView() {
428             super(VALUE);
429         }
430 
431         @Override
432         public boolean contains(final Object obj) {
433             if (!(obj instanceof Map.Entry)) {
434                 return false;
435             }
436             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
437             final Object value = entry.getValue();
438             final Node<K, V> node = lookupValue(entry.getKey());
439             return node != null && Objects.equals(node.getKey(), value);
440         }
441 
442         @Override
443         public Iterator<Map.Entry<V, K>> iterator() {
444             return new InverseViewMapEntryIterator();
445         }
446 
447         @Override
448         public boolean remove(final Object obj) {
449             if (!(obj instanceof Map.Entry)) {
450                 return false;
451             }
452             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
453             final Object value = entry.getValue();
454             final Node<K, V> node = lookupValue(entry.getKey());
455             if (node != null && Objects.equals(node.getKey(), value)) {
456                 doRedBlackDelete(node);
457                 return true;
458             }
459             return false;
460         }
461     }
462 
463     /**
464      * An iterator over the inverse map entries.
465      */
466     final class InverseViewMapEntryIterator extends AbstractViewIterator implements OrderedIterator<Map.Entry<V, K>> {
467 
468         /**
469          * Constructs a new instance.
470          */
471         InverseViewMapEntryIterator() {
472             super(VALUE);
473         }
474 
475         private Map.Entry<V, K> createEntry(final Node<K, V> node) {
476             return new UnmodifiableMapEntry<>(node.getValue(), node.getKey());
477         }
478 
479         @Override
480         public Map.Entry<V, K> next() {
481             return createEntry(navigateNext());
482         }
483 
484         @Override
485         public Map.Entry<V, K> previous() {
486             return createEntry(navigatePrevious());
487         }
488     }
489 
490     /**
491      * An iterator over the map.
492      */
493     final class InverseViewMapIterator extends AbstractViewIterator implements OrderedMapIterator<V, K> {
494 
495         /**
496          * Creates a new TreeBidiMap.InverseViewMapIterator.
497          */
498         InverseViewMapIterator(final DataElement orderType) {
499             super(orderType);
500         }
501 
502         @Override
503         public V getKey() {
504             if (lastReturnedNode == null) {
505                 throw new IllegalStateException(
506                         "Iterator getKey() can only be called after next() and before remove()");
507             }
508             return lastReturnedNode.getValue();
509         }
510 
511         @Override
512         public K getValue() {
513             if (lastReturnedNode == null) {
514                 throw new IllegalStateException(
515                         "Iterator getValue() can only be called after next() and before remove()");
516             }
517             return lastReturnedNode.getKey();
518         }
519 
520         @Override
521         public V next() {
522             return navigateNext().getValue();
523         }
524 
525         @Override
526         public V previous() {
527             return navigatePrevious().getValue();
528         }
529 
530         /**
531          * Always throws {@link UnsupportedOperationException}.
532          *
533          * @param value Ignored.
534          * @throws UnsupportedOperationException Always thrown.
535          */
536         @Override
537         public K setValue(final K value) {
538             throw new UnsupportedOperationException();
539         }
540     }
541     final class KeyView extends AbstractView<K> {
542 
543         /**
544          * Creates a new TreeBidiMap.KeyView.
545          */
546         KeyView(final DataElement orderType) {
547             super(orderType);
548         }
549 
550         @Override
551         public boolean contains(final Object obj) {
552             checkNonNullComparable(obj, KEY);
553             return lookupKey(obj) != null;
554         }
555 
556         @Override
557         public Iterator<K> iterator() {
558             return new ViewMapIterator(orderType);
559         }
560 
561         @Override
562         public boolean remove(final Object o) {
563             return doRemoveKey(o) != null;
564         }
565 
566     }
567 
568     /**
569      * A node used to store the data.
570      */
571     static class Node<K extends Comparable<K>, V extends Comparable<V>> implements Map.Entry<K, V>, KeyValue<K, V> {
572 
573         private final K key;
574         private final V value;
575         private final Node<K, V>[] leftNode;
576         private final Node<K, V>[] rightNode;
577         private final Node<K, V>[] parentNode;
578         private final boolean[] blackColor;
579         private int hashCodeValue;
580         private boolean calculatedHashCode;
581 
582         /**
583          * Makes a new cell with given key and value, and with null
584          * links, and black (true) colors.
585          *
586          * @param key The key of this node
587          * @param value The value of this node
588          */
589         @SuppressWarnings("unchecked")
590         Node(final K key, final V value) {
591             this.key = key;
592             this.value = value;
593             leftNode = new Node[2];
594             rightNode = new Node[2];
595             parentNode = new Node[2];
596             blackColor = new boolean[] { true, true };
597             calculatedHashCode = false;
598         }
599 
600         /**
601          * Makes this node the same color as another.
602          *
603          * @param node  The node whose color we're adopting
604          * @param dataElement  either the {@link DataElement#KEY key}
605          *                     or the {@link DataElement#VALUE value}.
606          */
607         private void copyColor(final Node<K, V> node, final DataElement dataElement) {
608             blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()];
609         }
610 
611         /**
612          * Compares the specified object with this entry for equality.
613          * Returns true if the given object is also a map entry and
614          * the two entries represent the same mapping.
615          *
616          * @param obj  The object to be compared for equality with this entry.
617          * @return true if the specified object is equal to this entry.
618          */
619         @Override
620         public boolean equals(final Object obj) {
621             if (obj == this) {
622                 return true;
623             }
624             if (!(obj instanceof Map.Entry)) {
625                 return false;
626             }
627             final Map.Entry<?, ?> e = (Map.Entry<?, ?>) obj;
628             return Objects.equals(getKey(), e.getKey()) && Objects.equals(getValue(), e.getValue());
629         }
630 
631         private Object getData(final DataElement dataElement) {
632             switch (dataElement) {
633             case KEY:
634                 return getKey();
635             case VALUE:
636                 return getValue();
637             default:
638                 throw new IllegalArgumentException();
639             }
640         }
641 
642         /**
643          * Gets the key.
644          *
645          * @return The key corresponding to this entry.
646          */
647         @Override
648         public K getKey() {
649             return key;
650         }
651 
652         private Node<K, V> getLeft(final DataElement dataElement) {
653             return leftNode[dataElement.ordinal()];
654         }
655 
656         /**
657          * Gets the parent node.
658          *
659          * @param dataElement  either the {@link DataElement#KEY key}
660          *                     or the {@link DataElement#VALUE value}.
661          * @return The parent node, may be null
662          */
663         private Node<K, V> getParent(final DataElement dataElement) {
664             return parentNode[dataElement.ordinal()];
665         }
666 
667         private Node<K, V> getRight(final DataElement dataElement) {
668             return rightNode[dataElement.ordinal()];
669         }
670 
671         /**
672          * Gets the value.
673          *
674          * @return The value corresponding to this entry.
675          */
676         @Override
677         public V getValue() {
678             return value;
679         }
680 
681         /**
682          * @return The hash code value for this map entry.
683          */
684         @Override
685         public int hashCode() {
686             if (!calculatedHashCode) {
687                 hashCodeValue = getKey().hashCode() ^ getValue().hashCode();
688                 calculatedHashCode = true;
689             }
690             return hashCodeValue;
691         }
692 
693         /**
694          * Is this node black?
695          *
696          * @param dataElement  either the {@link DataElement#KEY key}
697          *                     or the {@link DataElement#VALUE value}.
698          * @return true if black (which is represented as a true boolean)
699          */
700         private boolean isBlack(final DataElement dataElement) {
701             return blackColor[dataElement.ordinal()];
702         }
703 
704         private boolean isLeftChild(final DataElement dataElement) {
705             return parentNode[dataElement.ordinal()] != null
706                     && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this;
707         }
708 
709         /**
710          * Is this node red?
711          *
712          * @param dataElement  either the {@link DataElement#KEY key}
713          *                     or the {@link DataElement#VALUE value}.
714          * @return true if non-black
715          */
716         private boolean isRed(final DataElement dataElement) {
717             return !blackColor[dataElement.ordinal()];
718         }
719 
720         private boolean isRightChild(final DataElement dataElement) {
721             return parentNode[dataElement.ordinal()] != null
722                     && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this;
723         }
724 
725         /**
726          * Makes this node black.
727          *
728          * @param dataElement  either the {@link DataElement#KEY key}
729          *                     or the {@link DataElement#VALUE value}.
730          */
731         private void setBlack(final DataElement dataElement) {
732             blackColor[dataElement.ordinal()] = true;
733         }
734 
735         private void setLeft(final Node<K, V> node, final DataElement dataElement) {
736             leftNode[dataElement.ordinal()] = node;
737         }
738 
739         /**
740          * Sets this node's parent node.
741          *
742          * @param node  The new parent node
743          * @param dataElement  either the {@link DataElement#KEY key}
744          *                     or the {@link DataElement#VALUE value}.
745          */
746         private void setParent(final Node<K, V> node, final DataElement dataElement) {
747             parentNode[dataElement.ordinal()] = node;
748         }
749 
750         /**
751          * Makes this node red.
752          *
753          * @param dataElement  either the {@link DataElement#KEY key}
754          *                     or the {@link DataElement#VALUE value}.
755          */
756         private void setRed(final DataElement dataElement) {
757             blackColor[dataElement.ordinal()] = false;
758         }
759 
760         private void setRight(final Node<K, V> node, final DataElement dataElement) {
761             rightNode[dataElement.ordinal()] = node;
762         }
763 
764         /**
765          * Optional operation that is not permitted in this implementation.
766          *
767          * @param ignored this parameter is ignored.
768          * @return does not return
769          * @throws UnsupportedOperationException always
770          */
771         @Override
772         public V setValue(final V ignored) throws UnsupportedOperationException {
773             throw new UnsupportedOperationException("Map.Entry.setValue is not supported");
774         }
775 
776         /**
777          * Exchanges colors with another node.
778          *
779          * @param node  The node to swap with
780          * @param dataElement  either the {@link DataElement#KEY key}
781          *                     or the {@link DataElement#VALUE value}.
782          */
783         private void swapColors(final Node<K, V> node, final DataElement dataElement) {
784             // Swap colors -- old hacker's trick
785             blackColor[dataElement.ordinal()]      ^= node.blackColor[dataElement.ordinal()];
786             node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()];
787             blackColor[dataElement.ordinal()]      ^= node.blackColor[dataElement.ordinal()];
788         }
789     }
790 
791     final class ValueView extends AbstractView<V> {
792 
793         /**
794          * Creates a new TreeBidiMap.ValueView.
795          */
796         ValueView(final DataElement orderType) {
797             super(orderType);
798         }
799 
800         @Override
801         public boolean contains(final Object obj) {
802             checkNonNullComparable(obj, VALUE);
803             return lookupValue(obj) != null;
804         }
805 
806         @Override
807         public Iterator<V> iterator() {
808             return new InverseViewMapIterator(orderType);
809         }
810 
811         @Override
812         public boolean remove(final Object o) {
813             return doRemoveValue(o) != null;
814         }
815 
816     }
817 
818     /**
819      * An iterator over the map entries.
820      */
821     final class ViewMapEntryIterator extends AbstractViewIterator implements OrderedIterator<Map.Entry<K, V>> {
822 
823         /**
824          * Constructs a new instance.
825          */
826         ViewMapEntryIterator() {
827             super(KEY);
828         }
829 
830         @Override
831         public Map.Entry<K, V> next() {
832             return navigateNext();
833         }
834 
835         @Override
836         public Map.Entry<K, V> previous() {
837             return navigatePrevious();
838         }
839     }
840 
841     /**
842      * An iterator over the map.
843      */
844     final class ViewMapIterator extends AbstractViewIterator implements OrderedMapIterator<K, V> {
845 
846         /**
847          * Constructs a new instance.
848          */
849         ViewMapIterator(final DataElement orderType) {
850             super(orderType);
851         }
852 
853         @Override
854         public K getKey() {
855             if (lastReturnedNode == null) {
856                 throw new IllegalStateException(
857                         "Iterator getKey() can only be called after next() and before remove()");
858             }
859             return lastReturnedNode.getKey();
860         }
861 
862         @Override
863         public V getValue() {
864             if (lastReturnedNode == null) {
865                 throw new IllegalStateException(
866                         "Iterator getValue() can only be called after next() and before remove()");
867             }
868             return lastReturnedNode.getValue();
869         }
870 
871         @Override
872         public K next() {
873             return navigateNext().getKey();
874         }
875 
876         @Override
877         public K previous() {
878             return navigatePrevious().getKey();
879         }
880 
881         /**
882          * Always throws {@link UnsupportedOperationException}.
883          *
884          * @param value Ignored.
885          * @throws UnsupportedOperationException Always thrown.
886          */
887         @Override
888         public V setValue(final V value) {
889             throw new UnsupportedOperationException();
890         }
891     }
892 
893     private static final long serialVersionUID = 721969328361807L;
894 
895     /**
896      * Checks a key for validity (non-null and implements Comparable)
897      *
898      * @param key The key to be checked
899      * @throws NullPointerException if key is null
900      * @throws ClassCastException if key is not Comparable
901      */
902     private static void checkKey(final Object key) {
903         checkNonNullComparable(key, KEY);
904     }
905 
906     /**
907      * Checks a key and a value for validity (non-null and implements
908      * Comparable)
909      *
910      * @param key The key to be checked
911      * @param value The value to be checked
912      * @throws NullPointerException if key or value is null
913      * @throws ClassCastException if key or value is not Comparable
914      */
915     private static void checkKeyAndValue(final Object key, final Object value) {
916         checkKey(key);
917         checkValue(value);
918     }
919 
920     /**
921      * Checks if an object is fit to be proper input ... has to be
922      * Comparable and non-null.
923      *
924      * @param obj The object being checked
925      * @param dataElement  either the {@link DataElement#KEY key}
926      *                     or the {@link DataElement#VALUE value}.
927      *
928      * @throws NullPointerException if o is null
929      * @throws ClassCastException if o is not Comparable
930      */
931     private static void checkNonNullComparable(final Object obj, final DataElement dataElement) {
932         Objects.requireNonNull(obj, Objects.toString(dataElement));
933         if (!(obj instanceof Comparable)) {
934             throw new ClassCastException(dataElement + " must be Comparable");
935         }
936     }
937 
938     /**
939      * Checks a value for validity (non-null and implements Comparable)
940      *
941      * @param value The value to be checked
942      * @throws NullPointerException if value is null
943      * @throws ClassCastException if value is not Comparable
944      */
945     private static void checkValue(final Object value) {
946         checkNonNullComparable(value, VALUE);
947     }
948 
949     /**
950      * Compares two objects.
951      *
952      * @param o1  The first object
953      * @param o2  The second object
954      * @return negative value if o1 &lt; o2; 0 if o1 == o2; positive
955      *         value if o1 &gt; o2
956      */
957     private static <T extends Comparable<T>> int compare(final T o1, final T o2) {
958         return o1.compareTo(o2);
959     }
960 
961     /**
962      * Is the specified black red? If the node does not exist, sure,
963      * it's black, thank you.
964      *
965      * @param node The node (may be null) in question
966      * @param dataElement  either the {@link DataElement#KEY key}
967      *                     or the {@link DataElement#VALUE value}.
968      */
969     private static boolean isBlack(final Node<?, ?> node, final DataElement dataElement) {
970         return node == null || node.isBlack(dataElement);
971     }
972 
973     /**
974      * Is the specified node red? If the node does not exist, no, it's
975      * black, thank you.
976      *
977      * @param node The node (may be null) in question
978      * @param dataElement  either the {@link DataElement#KEY key}
979      *                     or the {@link DataElement#VALUE value}.
980      */
981     private static boolean isRed(final Node<?, ?> node, final DataElement dataElement) {
982         return node != null && node.isRed(dataElement);
983     }
984 
985     /**
986      * Forces a node (if it exists) black.
987      *
988      * @param node The node (may be null) in question
989      * @param dataElement  either the {@link DataElement#KEY key}
990      *                     or the {@link DataElement#VALUE value}.
991      */
992     private static void makeBlack(final Node<?, ?> node, final DataElement dataElement) {
993         if (node != null) {
994             node.setBlack(dataElement);
995         }
996     }
997 
998     /**
999      * Forces a node (if it exists) red.
1000      *
1001      * @param node The node (may be null) in question
1002      * @param dataElement  either the {@link DataElement#KEY key}
1003      *                     or the {@link DataElement#VALUE value}.
1004      */
1005     private static void makeRed(final Node<?, ?> node, final DataElement dataElement) {
1006         if (node != null) {
1007             node.setRed(dataElement);
1008         }
1009     }
1010 
1011     private transient Node<K, V>[] rootNode;
1012 
1013     private transient int nodeCount;
1014 
1015     private transient int modifications;
1016 
1017     private transient Set<K> keySet;
1018 
1019     private transient Set<V> valuesSet;
1020 
1021     private transient Set<Map.Entry<K, V>> entrySet;
1022 
1023     private transient Inverse inverse;
1024 
1025     /**
1026      * Constructs a new empty TreeBidiMap.
1027      */
1028     @SuppressWarnings("unchecked")
1029     public TreeBidiMap() {
1030         rootNode = new Node[2];
1031     }
1032 
1033     /**
1034      * Constructs a new TreeBidiMap by copying an existing Map.
1035      *
1036      * @param map  The map to copy
1037      * @throws ClassCastException if the keys/values in the map are
1038      *  not Comparable or are not mutually comparable
1039      * @throws NullPointerException if any key or value in the map is null
1040      */
1041     public TreeBidiMap(final Map<? extends K, ? extends V> map) {
1042         this();
1043         putAll(map);
1044     }
1045 
1046     /**
1047      * Removes all mappings from this map.
1048      */
1049     @Override
1050     public void clear() {
1051         modify();
1052 
1053         nodeCount = 0;
1054         rootNode[KEY.ordinal()] = null;
1055         rootNode[VALUE.ordinal()] = null;
1056     }
1057 
1058     /**
1059      * Checks whether this map contains a mapping for the specified key.
1060      * <p>
1061      * The key must implement {@code Comparable}.
1062      *
1063      * @param key  key whose presence in this map is to be tested
1064      * @return true if this map contains a mapping for the specified key
1065      * @throws ClassCastException if the key is of an inappropriate type
1066      * @throws NullPointerException if the key is null
1067      */
1068     @Override
1069     public boolean containsKey(final Object key) {
1070         checkKey(key);
1071         return lookupKey(key) != null;
1072     }
1073 
1074     /**
1075      * Checks whether this map contains a mapping for the specified value.
1076      * <p>
1077      * The value must implement {@code Comparable}.
1078      *
1079      * @param value  value whose presence in this map is to be tested
1080      * @return true if this map contains a mapping for the specified value
1081      * @throws ClassCastException if the value is of an inappropriate type
1082      * @throws NullPointerException if the value is null
1083      */
1084     @Override
1085     public boolean containsValue(final Object value) {
1086         checkValue(value);
1087         return lookupValue(value) != null;
1088     }
1089 
1090     /**
1091      * Copies the color from one node to another, dealing with the fact
1092      * that one or both nodes may, in fact, be null.
1093      *
1094      * @param from The node whose color we're copying; may be null
1095      * @param to The node whose color we're changing; may be null
1096      * @param dataElement  either the {@link DataElement#KEY key}
1097      *                     or the {@link DataElement#VALUE value}.
1098      */
1099     private void copyColor(final Node<K, V> from, final Node<K, V> to, final DataElement dataElement) {
1100         if (to != null) {
1101             if (from == null) {
1102                 // by default, make it black
1103                 to.setBlack(dataElement);
1104             } else {
1105                 to.copyColor(from, dataElement);
1106             }
1107         }
1108     }
1109 
1110     /**
1111      * Compares for equals as per the API.
1112      *
1113      * @param obj  The object to compare to
1114      * @param dataElement  either the {@link DataElement#KEY key}
1115      *                     or the {@link DataElement#VALUE value}.
1116      * @return true if equal
1117      */
1118     private boolean doEquals(final Object obj, final DataElement dataElement) {
1119         if (obj == this) {
1120             return true;
1121         }
1122         if (!(obj instanceof Map)) {
1123             return false;
1124         }
1125         final Map<?, ?> other = (Map<?, ?>) obj;
1126         if (other.size() != size()) {
1127             return false;
1128         }
1129 
1130         if (nodeCount > 0) {
1131             try {
1132                 for (final MapIterator<?, ?> it = getMapIterator(dataElement); it.hasNext(); ) {
1133                     final Object key = it.next();
1134                     final Object value = it.getValue();
1135                     if (!value.equals(other.get(key))) {
1136                         return false;
1137                     }
1138                 }
1139             } catch (final ClassCastException | NullPointerException ex) {
1140                 return false;
1141             }
1142         }
1143         return true;
1144     }
1145 
1146     /**
1147      * Gets the hash code value for this map as per the API.
1148      *
1149      * @param dataElement  either the {@link DataElement#KEY key}
1150      *                     or the {@link DataElement#VALUE value}.
1151      * @return The hash code value for this map
1152      */
1153     private int doHashCode(final DataElement dataElement) {
1154         int total = 0;
1155         if (nodeCount > 0) {
1156             for (final MapIterator<?, ?> it = getMapIterator(dataElement); it.hasNext(); ) {
1157                 final Object key = it.next();
1158                 final Object value = it.getValue();
1159                 total += key.hashCode() ^ value.hashCode();
1160             }
1161         }
1162         return total;
1163     }
1164 
1165     /**
1166      * Puts logic.
1167      *
1168      * @param key  The key, always the main map key
1169      * @param value  The value, always the main map value
1170      */
1171     private void doPut(final K key, final V value) {
1172         checkKeyAndValue(key, value);
1173 
1174         // store previous and remove previous mappings
1175         doRemoveKey(key);
1176         doRemoveValue(value);
1177 
1178         Node<K, V> node = rootNode[KEY.ordinal()];
1179         if (node == null) {
1180             // map is empty
1181             final Node<K, V> root = new Node<>(key, value);
1182             rootNode[KEY.ordinal()] = root;
1183             rootNode[VALUE.ordinal()] = root;
1184             grow();
1185 
1186         } else {
1187             // add new mapping
1188             while (true) {
1189                 final int cmp = compare(key, node.getKey());
1190 
1191                 if (cmp == 0) {
1192                     // shouldn't happen
1193                     throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map");
1194                 }
1195                 if (cmp < 0) {
1196                     if (node.getLeft(KEY) == null) {
1197                         final Node<K, V> newNode = new Node<>(key, value);
1198 
1199                         insertValue(newNode);
1200                         node.setLeft(newNode, KEY);
1201                         newNode.setParent(node, KEY);
1202                         doRedBlackInsert(newNode, KEY);
1203                         grow();
1204 
1205                         break;
1206                     }
1207                     node = node.getLeft(KEY);
1208                 } else { // cmp > 0
1209                     if (node.getRight(KEY) == null) {
1210                         final Node<K, V> newNode = new Node<>(key, value);
1211 
1212                         insertValue(newNode);
1213                         node.setRight(newNode, KEY);
1214                         newNode.setParent(node, KEY);
1215                         doRedBlackInsert(newNode, KEY);
1216                         grow();
1217 
1218                         break;
1219                     }
1220                     node = node.getRight(KEY);
1221                 }
1222             }
1223         }
1224     }
1225 
1226     /**
1227      * Complicated red-black delete stuff. Based on Sun's TreeMap
1228      * implementation, though it's barely recognizable anymore.
1229      *
1230      * @param deletedNode The node to be deleted
1231      */
1232     private void doRedBlackDelete(final Node<K, V> deletedNode) {
1233         for (final DataElement dataElement : DataElement.values()) {
1234             // if deleted node has both left and children, swap with
1235             // the next greater node
1236             if (deletedNode.getLeft(dataElement) != null && deletedNode.getRight(dataElement) != null) {
1237                 swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement);
1238             }
1239             final Node<K, V> replacement = deletedNode.getLeft(dataElement) != null ? deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement);
1240             if (replacement != null) {
1241                 replacement.setParent(deletedNode.getParent(dataElement), dataElement);
1242                 if (deletedNode.getParent(dataElement) == null) {
1243                     rootNode[dataElement.ordinal()] = replacement;
1244                 } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) {
1245                     deletedNode.getParent(dataElement).setLeft(replacement, dataElement);
1246                 } else {
1247                     deletedNode.getParent(dataElement).setRight(replacement, dataElement);
1248                 }
1249                 deletedNode.setLeft(null, dataElement);
1250                 deletedNode.setRight(null, dataElement);
1251                 deletedNode.setParent(null, dataElement);
1252                 if (isBlack(deletedNode, dataElement)) {
1253                     doRedBlackDeleteFixup(replacement, dataElement);
1254                 }
1255             } else if (deletedNode.getParent(dataElement) == null) {
1256                 // replacement is null
1257                 // empty tree
1258                 rootNode[dataElement.ordinal()] = null;
1259             } else {
1260                 // deleted node had no children
1261                 if (isBlack(deletedNode, dataElement)) {
1262                     doRedBlackDeleteFixup(deletedNode, dataElement);
1263                 }
1264                 if (deletedNode.getParent(dataElement) != null) {
1265                     if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) {
1266                         deletedNode.getParent(dataElement).setLeft(null, dataElement);
1267                     } else {
1268                         deletedNode.getParent(dataElement).setRight(null, dataElement);
1269                     }
1270                     deletedNode.setParent(null, dataElement);
1271                 }
1272             }
1273         }
1274         shrink();
1275     }
1276 
1277     /**
1278      * Complicated red-black delete stuff. Based on Sun's TreeMap
1279      * implementation, though it's barely recognizable anymore. This
1280      * rebalances the tree (somewhat, as red-black trees are not
1281      * perfectly balanced -- perfect balancing takes longer)
1282      *
1283      * @param replacementNode The node being replaced
1284      * @param dataElement  The KEY or VALUE int
1285      */
1286     private void doRedBlackDeleteFixup(final Node<K, V> replacementNode, final DataElement dataElement) {
1287         Node<K, V> currentNode = replacementNode;
1288 
1289         while (currentNode != rootNode[dataElement.ordinal()] && isBlack(currentNode, dataElement)) {
1290             if (currentNode.isLeftChild(dataElement)) {
1291                 Node<K, V> siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement);
1292 
1293                 if (isRed(siblingNode, dataElement)) {
1294                     makeBlack(siblingNode, dataElement);
1295                     makeRed(getParent(currentNode, dataElement), dataElement);
1296                     rotateLeft(getParent(currentNode, dataElement), dataElement);
1297 
1298                     siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement);
1299                 }
1300 
1301                 if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)
1302                     && isBlack(getRightChild(siblingNode, dataElement), dataElement)) {
1303                     makeRed(siblingNode, dataElement);
1304 
1305                     currentNode = getParent(currentNode, dataElement);
1306                 } else {
1307                     if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) {
1308                         makeBlack(getLeftChild(siblingNode, dataElement), dataElement);
1309                         makeRed(siblingNode, dataElement);
1310                         rotateRight(siblingNode, dataElement);
1311 
1312                         siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement);
1313                     }
1314 
1315                     copyColor(getParent(currentNode, dataElement), siblingNode, dataElement);
1316                     makeBlack(getParent(currentNode, dataElement), dataElement);
1317                     makeBlack(getRightChild(siblingNode, dataElement), dataElement);
1318                     rotateLeft(getParent(currentNode, dataElement), dataElement);
1319 
1320                     currentNode = rootNode[dataElement.ordinal()];
1321                 }
1322             } else {
1323                 Node<K, V> siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement);
1324 
1325                 if (isRed(siblingNode, dataElement)) {
1326                     makeBlack(siblingNode, dataElement);
1327                     makeRed(getParent(currentNode, dataElement), dataElement);
1328                     rotateRight(getParent(currentNode, dataElement), dataElement);
1329 
1330                     siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement);
1331                 }
1332 
1333                 if (isBlack(getRightChild(siblingNode, dataElement), dataElement)
1334                     && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) {
1335                     makeRed(siblingNode, dataElement);
1336 
1337                     currentNode = getParent(currentNode, dataElement);
1338                 } else {
1339                     if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) {
1340                         makeBlack(getRightChild(siblingNode, dataElement), dataElement);
1341                         makeRed(siblingNode, dataElement);
1342                         rotateLeft(siblingNode, dataElement);
1343 
1344                         siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement);
1345                     }
1346 
1347                     copyColor(getParent(currentNode, dataElement), siblingNode, dataElement);
1348                     makeBlack(getParent(currentNode, dataElement), dataElement);
1349                     makeBlack(getLeftChild(siblingNode, dataElement), dataElement);
1350                     rotateRight(getParent(currentNode, dataElement), dataElement);
1351 
1352                     currentNode = rootNode[dataElement.ordinal()];
1353                 }
1354             }
1355         }
1356 
1357         makeBlack(currentNode, dataElement);
1358     }
1359 
1360     /**
1361      * Complicated red-black insert stuff. Based on Sun's TreeMap
1362      * implementation, though it's barely recognizable anymore.
1363      *
1364      * @param insertedNode The node to be inserted
1365      * @param dataElement  The KEY or VALUE int
1366      */
1367     private void doRedBlackInsert(final Node<K, V> insertedNode, final DataElement dataElement) {
1368         Node<K, V> currentNode = insertedNode;
1369         makeRed(currentNode, dataElement);
1370 
1371         while (currentNode != null
1372             && currentNode != rootNode[dataElement.ordinal()]
1373             && isRed(currentNode.getParent(dataElement), dataElement)) {
1374             if (currentNode.isLeftChild(dataElement)) {
1375                 final Node<K, V> y = getRightChild(getGrandParent(currentNode, dataElement), dataElement);
1376 
1377                 if (isRed(y, dataElement)) {
1378                     makeBlack(getParent(currentNode, dataElement), dataElement);
1379                     makeBlack(y, dataElement);
1380                     makeRed(getGrandParent(currentNode, dataElement), dataElement);
1381 
1382                     currentNode = getGrandParent(currentNode, dataElement);
1383                 } else {
1384                     //dead code?
1385                     if (currentNode.isRightChild(dataElement)) {
1386                         currentNode = getParent(currentNode, dataElement);
1387 
1388                         rotateLeft(currentNode, dataElement);
1389                     }
1390 
1391                     makeBlack(getParent(currentNode, dataElement), dataElement);
1392                     makeRed(getGrandParent(currentNode, dataElement), dataElement);
1393 
1394                     if (getGrandParent(currentNode, dataElement) != null) {
1395                         rotateRight(getGrandParent(currentNode, dataElement), dataElement);
1396                     }
1397                 }
1398             } else {
1399 
1400                 // just like clause above, except swap left for right
1401                 final Node<K, V> y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement);
1402 
1403                 if (isRed(y, dataElement)) {
1404                     makeBlack(getParent(currentNode, dataElement), dataElement);
1405                     makeBlack(y, dataElement);
1406                     makeRed(getGrandParent(currentNode, dataElement), dataElement);
1407 
1408                     currentNode = getGrandParent(currentNode, dataElement);
1409                 } else {
1410                     //dead code?
1411                     if (currentNode.isLeftChild(dataElement)) {
1412                         currentNode = getParent(currentNode, dataElement);
1413 
1414                         rotateRight(currentNode, dataElement);
1415                     }
1416 
1417                     makeBlack(getParent(currentNode, dataElement), dataElement);
1418                     makeRed(getGrandParent(currentNode, dataElement), dataElement);
1419 
1420                     if (getGrandParent(currentNode, dataElement) != null) {
1421                         rotateLeft(getGrandParent(currentNode, dataElement), dataElement);
1422                     }
1423                 }
1424             }
1425         }
1426 
1427         makeBlack(rootNode[dataElement.ordinal()], dataElement);
1428     }
1429 
1430     private V doRemoveKey(final Object key) {
1431         final Node<K, V> node = lookupKey(key);
1432         if (node == null) {
1433             return null;
1434         }
1435         doRedBlackDelete(node);
1436         return node.getValue();
1437     }
1438 
1439     private K doRemoveValue(final Object value) {
1440         final Node<K, V> node = lookupValue(value);
1441         if (node == null) {
1442             return null;
1443         }
1444         doRedBlackDelete(node);
1445         return node.getKey();
1446     }
1447 
1448     /**
1449      * Gets the string form of this map as per AbstractMap.
1450      *
1451      * @param dataElement  either the {@link DataElement#KEY key}
1452      *                     or the {@link DataElement#VALUE value}.
1453      * @return The string form of this map
1454      */
1455     private String doToString(final DataElement dataElement) {
1456         if (nodeCount == 0) {
1457             return "{}";
1458         }
1459         final StringBuilder buf = new StringBuilder(nodeCount * 32);
1460         buf.append('{');
1461         final MapIterator<?, ?> it = getMapIterator(dataElement);
1462         boolean hasNext = it.hasNext();
1463         while (hasNext) {
1464             final Object key = it.next();
1465             final Object value = it.getValue();
1466             buf.append(key == this ? "(this Map)" : key)
1467                 .append('=')
1468                 .append(value == this ? "(this Map)" : value);
1469 
1470             hasNext = it.hasNext();
1471             if (hasNext) {
1472                 buf.append(", ");
1473             }
1474         }
1475 
1476         buf.append('}');
1477         return buf.toString();
1478     }
1479 
1480     /**
1481      * Returns a set view of the entries contained in this map in key order.
1482      * For simple iteration through the map, the MapIterator is quicker.
1483      * <p>
1484      * The set is backed by the map, so changes to the map are reflected in
1485      * the set, and vice-versa. If the map is modified while an iteration over
1486      * the set is in progress, the results of the iteration are undefined.
1487      * <p>
1488      * The set supports element removal, which removes the corresponding mapping
1489      * from the map. It does not support the add or addAll operations.
1490      * The returned MapEntry objects do not support setValue.
1491      *
1492      * @return A set view of the values contained in this map.
1493      */
1494     @Override
1495     public Set<Map.Entry<K, V>> entrySet() {
1496         if (entrySet == null) {
1497             entrySet = new EntryView();
1498         }
1499         return entrySet;
1500     }
1501 
1502     /**
1503      * Compares for equals as per the API.
1504      *
1505      * @param obj  The object to compare to
1506      * @return true if equal
1507      */
1508     @Override
1509     public boolean equals(final Object obj) {
1510         return this.doEquals(obj, KEY);
1511     }
1512 
1513     /**
1514      * Gets the first (lowest) key currently in this map.
1515      *
1516      * @return The first (lowest) key currently in this sorted map
1517      * @throws NoSuchElementException if this map is empty
1518      */
1519     @Override
1520     public K firstKey() {
1521         if (nodeCount == 0) {
1522             throw new NoSuchElementException("Map is empty");
1523         }
1524         return leastNode(rootNode[KEY.ordinal()], KEY).getKey();
1525     }
1526 
1527     /**
1528      * Gets the value to which this map maps the specified key.
1529      * Returns null if the map contains no mapping for this key.
1530      * <p>
1531      * The key must implement {@code Comparable}.
1532      *
1533      * @param key  key whose associated value is to be returned
1534      * @return The value to which this map maps the specified key,
1535      *  or null if the map contains no mapping for this key
1536      * @throws ClassCastException if the key is of an inappropriate type
1537      * @throws NullPointerException if the key is null
1538      */
1539     @Override
1540     public V get(final Object key) {
1541         checkKey(key);
1542         final Node<K, V> node = lookupKey(key);
1543         return node == null ? null : node.getValue();
1544     }
1545 
1546     /**
1547      * Gets a node's grandparent. mind you, the node, its parent, or
1548      * its grandparent may not exist. No problem.
1549      *
1550      * @param node The node (may be null) in question
1551      * @param dataElement  either the {@link DataElement#KEY key}
1552      *                     or the {@link DataElement#VALUE value}.
1553      */
1554     private Node<K, V> getGrandParent(final Node<K, V> node, final DataElement dataElement) {
1555         return getParent(getParent(node, dataElement), dataElement);
1556     }
1557 
1558     /**
1559      * Gets the key to which this map maps the specified value.
1560      * Returns null if the map contains no mapping for this value.
1561      * <p>
1562      * The value must implement {@code Comparable}.
1563      *
1564      * @param value  value whose associated key is to be returned.
1565      * @return The key to which this map maps the specified value,
1566      *  or null if the map contains no mapping for this value.
1567      * @throws ClassCastException if the value is of an inappropriate type
1568      * @throws NullPointerException if the value is null
1569      */
1570     @Override
1571     public K getKey(final Object value) {
1572         checkValue(value);
1573         final Node<K, V> node = lookupValue(value);
1574         return node == null ? null : node.getKey();
1575     }
1576 
1577     /**
1578      * Gets a node's left child. mind you, the node may not exist. no
1579      * problem.
1580      *
1581      * @param node The node (may be null) in question
1582      * @param dataElement  either the {@link DataElement#KEY key}
1583      *                     or the {@link DataElement#VALUE value}.
1584      */
1585     private Node<K, V> getLeftChild(final Node<K, V> node, final DataElement dataElement) {
1586         return node == null ? null : node.getLeft(dataElement);
1587     }
1588 
1589     private MapIterator<?, ?> getMapIterator(final DataElement dataElement) {
1590         switch (dataElement) {
1591         case KEY:
1592             return new ViewMapIterator(KEY);
1593         case VALUE:
1594             return new InverseViewMapIterator(VALUE);
1595         default:
1596             throw new IllegalArgumentException();
1597         }
1598     }
1599 
1600     /**
1601      * Gets a node's parent. mind you, the node, or its parent, may not
1602      * exist. no problem.
1603      *
1604      * @param node The node (may be null) in question
1605      * @param dataElement  either the {@link DataElement#KEY key}
1606      *                     or the {@link DataElement#VALUE value}.
1607      */
1608     private Node<K, V> getParent(final Node<K, V> node, final DataElement dataElement) {
1609         return node == null ? null : node.getParent(dataElement);
1610     }
1611 
1612     /**
1613      * Gets a node's right child. mind you, the node may not exist. no
1614      * problem.
1615      *
1616      * @param node The node (may be null) in question
1617      * @param dataElement  either the {@link DataElement#KEY key}
1618      *                     or the {@link DataElement#VALUE value}.
1619      */
1620     private Node<K, V> getRightChild(final Node<K, V> node, final DataElement dataElement) {
1621         return node == null ? null : node.getRight(dataElement);
1622     }
1623 
1624     /**
1625      * Finds the greatest node from a given node.
1626      *
1627      * @param node  The node from which we will start searching
1628      * @param dataElement  either the {@link DataElement#KEY key}
1629      *                     or the {@link DataElement#VALUE value}.
1630      * @return The greatest node, from the specified node
1631      */
1632     private Node<K, V> greatestNode(final Node<K, V> node, final DataElement dataElement) {
1633         Node<K, V> rval = node;
1634         if (rval != null) {
1635             while (rval.getRight(dataElement) != null) {
1636                 rval = rval.getRight(dataElement);
1637             }
1638         }
1639         return rval;
1640     }
1641 
1642     /**
1643      * Bumps up the size and note that the map has changed.
1644      */
1645     private void grow() {
1646         modify();
1647         nodeCount++;
1648     }
1649 
1650     /**
1651      * Gets the hash code value for this map as per the API.
1652      *
1653      * @return The hash code value for this map
1654      */
1655     @Override
1656     public int hashCode() {
1657         return this.doHashCode(KEY);
1658     }
1659 
1660     /**
1661      * Inserts a node by its value.
1662      *
1663      * @param newNode The node to be inserted
1664      * @throws IllegalArgumentException if the node already exists
1665      *                                     in the value mapping
1666      */
1667     private void insertValue(final Node<K, V> newNode) throws IllegalArgumentException {
1668         Node<K, V> node = rootNode[VALUE.ordinal()];
1669 
1670         while (true) {
1671             final int cmp = compare(newNode.getValue(), node.getValue());
1672 
1673             if (cmp == 0) {
1674                 throw new IllegalArgumentException(
1675                     "Cannot store a duplicate value (\"" + newNode.getData(VALUE) + "\") in this Map");
1676             }
1677             if (cmp < 0) {
1678                 if (node.getLeft(VALUE) == null) {
1679                     node.setLeft(newNode, VALUE);
1680                     newNode.setParent(node, VALUE);
1681                     doRedBlackInsert(newNode, VALUE);
1682 
1683                     break;
1684                 }
1685                 node = node.getLeft(VALUE);
1686             } else { // cmp > 0
1687                 if (node.getRight(VALUE) == null) {
1688                     node.setRight(newNode, VALUE);
1689                     newNode.setParent(node, VALUE);
1690                     doRedBlackInsert(newNode, VALUE);
1691 
1692                     break;
1693                 }
1694                 node = node.getRight(VALUE);
1695             }
1696         }
1697     }
1698 
1699     /**
1700      * Gets the inverse map for comparison.
1701      *
1702      * @return The inverse map
1703      */
1704     @Override
1705     public OrderedBidiMap<V, K> inverseBidiMap() {
1706         if (inverse == null) {
1707             inverse = new Inverse();
1708         }
1709         return inverse;
1710     }
1711 
1712     /**
1713      * Checks whether the map is empty or not.
1714      *
1715      * @return true if the map is empty
1716      */
1717     @Override
1718     public boolean isEmpty() {
1719         return nodeCount == 0;
1720     }
1721 
1722     /**
1723      * Returns a set view of the keys contained in this map in key order.
1724      * <p>
1725      * The set is backed by the map, so changes to the map are reflected in
1726      * the set, and vice-versa. If the map is modified while an iteration over
1727      * the set is in progress, the results of the iteration are undefined.
1728      * <p>
1729      * The set supports element removal, which removes the corresponding mapping
1730      * from the map. It does not support the add or addAll operations.
1731      *
1732      * @return A set view of the keys contained in this map.
1733      */
1734     @Override
1735     public Set<K> keySet() {
1736         if (keySet == null) {
1737             keySet = new KeyView(KEY);
1738         }
1739         return keySet;
1740     }
1741 
1742     /**
1743      * Gets the last (highest) key currently in this map.
1744      *
1745      * @return The last (highest) key currently in this sorted map
1746      * @throws NoSuchElementException if this map is empty
1747      */
1748     @Override
1749     public K lastKey() {
1750         if (nodeCount == 0) {
1751             throw new NoSuchElementException("Map is empty");
1752         }
1753         return greatestNode(rootNode[KEY.ordinal()], KEY).getKey();
1754     }
1755 
1756     /**
1757      * Finds the least node from a given node.
1758      *
1759      * @param node  The node from which we will start searching
1760      * @param dataElement  either the {@link DataElement#KEY key}
1761      *                     or the {@link DataElement#VALUE value}.
1762      * @return The smallest node, from the specified node, in the
1763      *         specified mapping
1764      */
1765     private Node<K, V> leastNode(final Node<K, V> node, final DataElement dataElement) {
1766         Node<K, V> rval = node;
1767         if (rval != null) {
1768             while (rval.getLeft(dataElement) != null) {
1769                 rval = rval.getLeft(dataElement);
1770             }
1771         }
1772         return rval;
1773     }
1774 
1775     /**
1776      * Does the actual lookup of a piece of data.
1777      *
1778      * @param data The key or value to be looked up
1779      * @param dataElement  either the {@link DataElement#KEY key}
1780      *                     or the {@link DataElement#VALUE value}.
1781      * @return The desired Node, or null if there is no mapping of the
1782      *         specified data
1783      */
1784     @SuppressWarnings("unchecked")
1785     private <T extends Comparable<T>> Node<K, V> lookup(final Object data, final DataElement dataElement) {
1786         Node<K, V> rval = null;
1787         Node<K, V> node = rootNode[dataElement.ordinal()];
1788 
1789         while (node != null) {
1790             final int cmp = compare((T) data, (T) node.getData(dataElement));
1791             if (cmp == 0) {
1792                 rval = node;
1793                 break;
1794             }
1795             node = cmp < 0 ? node.getLeft(dataElement) : node.getRight(dataElement);
1796         }
1797 
1798         return rval;
1799     }
1800 
1801     private Node<K, V> lookupKey(final Object key) {
1802         return this.<K>lookup(key, KEY);
1803     }
1804 
1805     private Node<K, V> lookupValue(final Object value) {
1806         return this.<V>lookup(value, VALUE);
1807     }
1808 
1809     @Override
1810     public OrderedMapIterator<K, V> mapIterator() {
1811         if (isEmpty()) {
1812             return EmptyOrderedMapIterator.<K, V>emptyOrderedMapIterator();
1813         }
1814         return new ViewMapIterator(KEY);
1815     }
1816 
1817     /**
1818      * Increments the modification count -- used to check for
1819      * concurrent modification of the map through the map and through
1820      * an Iterator from one of its Set or Collection views.
1821      */
1822     private void modify() {
1823         modifications++;
1824     }
1825 
1826     /**
1827      * Gets the next larger node from the specified node.
1828      *
1829      * @param node The node to be searched from
1830      * @param dataElement  either the {@link DataElement#KEY key}
1831      *                     or the {@link DataElement#VALUE value}.
1832      * @return The specified node
1833      */
1834     private Node<K, V> nextGreater(final Node<K, V> node, final DataElement dataElement) {
1835         final Node<K, V> rval;
1836         if (node == null) {
1837             rval = null;
1838         } else if (node.getRight(dataElement) != null) {
1839             // everything to the node's right is larger. The least of
1840             // the right node's descendants is the next larger node
1841             rval = leastNode(node.getRight(dataElement), dataElement);
1842         } else {
1843             // traverse up our ancestry until we find an ancestor that
1844             // is null or one whose left child is our ancestor. If we
1845             // find a null, then this node IS the largest node in the
1846             // tree, and there is no greater node. Otherwise, we are
1847             // the largest node in the subtree on that ancestor's left
1848             // ... and that ancestor is the next greatest node
1849             Node<K, V> parent = node.getParent(dataElement);
1850             Node<K, V> child = node;
1851 
1852             while (parent != null && child == parent.getRight(dataElement)) {
1853                 child = parent;
1854                 parent = parent.getParent(dataElement);
1855             }
1856             rval = parent;
1857         }
1858         return rval;
1859     }
1860 
1861     /**
1862      * Gets the next key after the one specified.
1863      * <p>
1864      * The key must implement {@code Comparable}.
1865      *
1866      * @param key The key to search for next from
1867      * @return The next key, null if no match or at end
1868      */
1869     @Override
1870     public K nextKey(final K key) {
1871         checkKey(key);
1872         final Node<K, V> node = nextGreater(lookupKey(key), KEY);
1873         return node == null ? null : node.getKey();
1874     }
1875 
1876     /**
1877      * Gets the next smaller node from the specified node.
1878      *
1879      * @param node The node to be searched from
1880      * @param dataElement  either the {@link DataElement#KEY key}
1881      *                     or the {@link DataElement#VALUE value}.
1882      * @return The specified node
1883      */
1884     private Node<K, V> nextSmaller(final Node<K, V> node, final DataElement dataElement) {
1885         final Node<K, V> rval;
1886         if (node == null) {
1887             rval = null;
1888         } else if (node.getLeft(dataElement) != null) {
1889             // everything to the node's left is smaller. The greatest of
1890             // the left node's descendants is the next smaller node
1891             rval = greatestNode(node.getLeft(dataElement), dataElement);
1892         } else {
1893             // traverse up our ancestry until we find an ancestor that
1894             // is null or one whose right child is our ancestor. If we
1895             // find a null, then this node IS the largest node in the
1896             // tree, and there is no greater node. Otherwise, we are
1897             // the largest node in the subtree on that ancestor's right
1898             // ... and that ancestor is the next greatest node
1899             Node<K, V> parent = node.getParent(dataElement);
1900             Node<K, V> child = node;
1901 
1902             while (parent != null && child == parent.getLeft(dataElement)) {
1903                 child = parent;
1904                 parent = parent.getParent(dataElement);
1905             }
1906             rval = parent;
1907         }
1908         return rval;
1909     }
1910 
1911     /**
1912      * Gets the previous key before the one specified.
1913      * <p>
1914      * The key must implement {@code Comparable}.
1915      *
1916      * @param key The key to search for previous from
1917      * @return The previous key, null if no match or at start
1918      */
1919     @Override
1920     public K previousKey(final K key) {
1921         checkKey(key);
1922         final Node<K, V> node = nextSmaller(lookupKey(key), KEY);
1923         return node == null ? null : node.getKey();
1924     }
1925 
1926     /**
1927      * Puts the key-value pair into the map, replacing any previous pair.
1928      * <p>
1929      * When adding a key-value pair, the value may already exist in the map
1930      * against a different key. That mapping is removed, to ensure that the
1931      * value only occurs once in the inverse map.
1932      * <pre>
1933      *  BidiMap map1 = new TreeBidiMap();
1934      *  map.put("A","B");  // contains A mapped to B, as per Map
1935      *  map.put("A","C");  // contains A mapped to C, as per Map
1936      *
1937      *  BidiMap map2 = new TreeBidiMap();
1938      *  map.put("A","B");  // contains A mapped to B, as per Map
1939      *  map.put("C","B");  // contains C mapped to B, key A is removed
1940      * </pre>
1941      * <p>
1942      * Both key and value must implement {@code Comparable}.
1943      *
1944      * @param key  key with which the specified value is to be  associated
1945      * @param value  value to be associated with the specified key
1946      * @return The previous value for the key
1947      * @throws ClassCastException if the key is of an inappropriate type
1948      * @throws NullPointerException if the key is null
1949      */
1950     @Override
1951     public V put(final K key, final V value) {
1952         final V result = get(key);
1953         doPut(key, value);
1954         return result;
1955     }
1956 
1957     /**
1958      * Puts all the mappings from the specified map into this map.
1959      * <p>
1960      * All keys and values must implement {@code Comparable}.
1961      *
1962      * @param map  The map to copy from
1963      */
1964     @Override
1965     public void putAll(final Map<? extends K, ? extends V> map) {
1966         for (final Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
1967             put(e.getKey(), e.getValue());
1968         }
1969     }
1970 
1971     /**
1972      * Deserializes the content of the stream.
1973      *
1974      * @param stream The input stream
1975      * @throws IOException Thrown if an error occurs while reading from the stream
1976      * @throws ClassNotFoundException if an object read from the stream cannot be loaded
1977      */
1978     @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
1979     private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
1980         stream.defaultReadObject();
1981         rootNode = new Node[2];
1982         final int size = stream.readInt();
1983         for (int i = 0; i < size; i++) {
1984             final K k = (K) stream.readObject();
1985             final V v = (V) stream.readObject();
1986             put(k, v);
1987         }
1988     }
1989 
1990     /**
1991      * Removes the mapping for this key from this map if present.
1992      * <p>
1993      * The key must implement {@code Comparable}.
1994      *
1995      * @param key  key whose mapping is to be removed from the map.
1996      * @return previous value associated with specified key,
1997      *  or null if there was no mapping for key.
1998      * @throws ClassCastException if the key is of an inappropriate type
1999      * @throws NullPointerException if the key is null
2000      */
2001     @Override
2002     public V remove(final Object key) {
2003         return doRemoveKey(key);
2004     }
2005 
2006     /**
2007      * Removes the mapping for this value from this map if present.
2008      * <p>
2009      * The value must implement {@code Comparable}.
2010      *
2011      * @param value  value whose mapping is to be removed from the map
2012      * @return previous key associated with specified value,
2013      *  or null if there was no mapping for value.
2014      * @throws ClassCastException if the value is of an inappropriate type
2015      * @throws NullPointerException if the value is null
2016      */
2017     @Override
2018     public K removeValue(final Object value) {
2019         return doRemoveValue(value);
2020     }
2021 
2022     /**
2023      * Does a rotate left. standard fare in the world of balanced trees.
2024      *
2025      * @param node The node to be rotated
2026      * @param dataElement  either the {@link DataElement#KEY key}
2027      *                     or the {@link DataElement#VALUE value}.
2028      */
2029     private void rotateLeft(final Node<K, V> node, final DataElement dataElement) {
2030         final Node<K, V> rightChild = node.getRight(dataElement);
2031         node.setRight(rightChild.getLeft(dataElement), dataElement);
2032 
2033         if (rightChild.getLeft(dataElement) != null) {
2034             rightChild.getLeft(dataElement).setParent(node, dataElement);
2035         }
2036         rightChild.setParent(node.getParent(dataElement), dataElement);
2037 
2038         if (node.getParent(dataElement) == null) {
2039             // node was the root ... now its right child is the root
2040             rootNode[dataElement.ordinal()] = rightChild;
2041         } else if (node.getParent(dataElement).getLeft(dataElement) == node) {
2042             node.getParent(dataElement).setLeft(rightChild, dataElement);
2043         } else {
2044             node.getParent(dataElement).setRight(rightChild, dataElement);
2045         }
2046 
2047         rightChild.setLeft(node, dataElement);
2048         node.setParent(rightChild, dataElement);
2049     }
2050 
2051     /**
2052      * Does a rotate right. standard fare in the world of balanced trees.
2053      *
2054      * @param node The node to be rotated
2055      * @param dataElement  either the {@link DataElement#KEY key}
2056      *                     or the {@link DataElement#VALUE value}.
2057      */
2058     private void rotateRight(final Node<K, V> node, final DataElement dataElement) {
2059         final Node<K, V> leftChild = node.getLeft(dataElement);
2060         node.setLeft(leftChild.getRight(dataElement), dataElement);
2061         if (leftChild.getRight(dataElement) != null) {
2062             leftChild.getRight(dataElement).setParent(node, dataElement);
2063         }
2064         leftChild.setParent(node.getParent(dataElement), dataElement);
2065 
2066         if (node.getParent(dataElement) == null) {
2067             // node was the root ... now its left child is the root
2068             rootNode[dataElement.ordinal()] = leftChild;
2069         } else if (node.getParent(dataElement).getRight(dataElement) == node) {
2070             node.getParent(dataElement).setRight(leftChild, dataElement);
2071         } else {
2072             node.getParent(dataElement).setLeft(leftChild, dataElement);
2073         }
2074 
2075         leftChild.setRight(node, dataElement);
2076         node.setParent(leftChild, dataElement);
2077     }
2078 
2079     /**
2080      * Decrements the size and note that the map has changed.
2081      */
2082     private void shrink() {
2083         modify();
2084         nodeCount--;
2085     }
2086 
2087     /**
2088      * Returns the number of key-value mappings in this map.
2089      *
2090      * @return The number of key-value mappings in this map
2091      */
2092     @Override
2093     public int size() {
2094         return nodeCount;
2095     }
2096 
2097     /**
2098      * Swaps two nodes (except for their content), taking care of
2099      * special cases where one is the other's parent ... hey, it
2100      * happens.
2101      *
2102      * @param x one node
2103      * @param y another node
2104      * @param dataElement  The KEY or VALUE int
2105      */
2106     private void swapPosition(final Node<K, V> x, final Node<K, V> y, final DataElement dataElement) {
2107         // Save initial values.
2108         final Node<K, V> xFormerParent = x.getParent(dataElement);
2109         final Node<K, V> xFormerLeftChild = x.getLeft(dataElement);
2110         final Node<K, V> xFormerRightChild = x.getRight(dataElement);
2111         final Node<K, V> yFormerParent = y.getParent(dataElement);
2112         final Node<K, V> yFormerLeftChild = y.getLeft(dataElement);
2113         final Node<K, V> yFormerRightChild = y.getRight(dataElement);
2114         final boolean xWasLeftChild =
2115                 x.getParent(dataElement) != null && x == x.getParent(dataElement).getLeft(dataElement);
2116         final boolean yWasLeftChild =
2117                 y.getParent(dataElement) != null && y == y.getParent(dataElement).getLeft(dataElement);
2118 
2119         // Swap, handling special cases of one being the other's parent.
2120         if (x == yFormerParent) { // x was y's parent
2121             x.setParent(y, dataElement);
2122 
2123             if (yWasLeftChild) {
2124                 y.setLeft(x, dataElement);
2125                 y.setRight(xFormerRightChild, dataElement);
2126             } else {
2127                 y.setRight(x, dataElement);
2128                 y.setLeft(xFormerLeftChild, dataElement);
2129             }
2130         } else {
2131             x.setParent(yFormerParent, dataElement);
2132 
2133             if (yFormerParent != null) {
2134                 if (yWasLeftChild) {
2135                     yFormerParent.setLeft(x, dataElement);
2136                 } else {
2137                     yFormerParent.setRight(x, dataElement);
2138                 }
2139             }
2140 
2141             y.setLeft(xFormerLeftChild, dataElement);
2142             y.setRight(xFormerRightChild, dataElement);
2143         }
2144 
2145         if (y == xFormerParent) { // y was x's parent
2146             y.setParent(x, dataElement);
2147 
2148             if (xWasLeftChild) {
2149                 x.setLeft(y, dataElement);
2150                 x.setRight(yFormerRightChild, dataElement);
2151             } else {
2152                 x.setRight(y, dataElement);
2153                 x.setLeft(yFormerLeftChild, dataElement);
2154             }
2155         } else {
2156             y.setParent(xFormerParent, dataElement);
2157 
2158             if (xFormerParent != null) {
2159                 if (xWasLeftChild) {
2160                     xFormerParent.setLeft(y, dataElement);
2161                 } else {
2162                     xFormerParent.setRight(y, dataElement);
2163                 }
2164             }
2165 
2166             x.setLeft(yFormerLeftChild, dataElement);
2167             x.setRight(yFormerRightChild, dataElement);
2168         }
2169 
2170         // Fix children's parent pointers
2171         if (x.getLeft(dataElement) != null) {
2172             x.getLeft(dataElement).setParent(x, dataElement);
2173         }
2174 
2175         if (x.getRight(dataElement) != null) {
2176             x.getRight(dataElement).setParent(x, dataElement);
2177         }
2178 
2179         if (y.getLeft(dataElement) != null) {
2180             y.getLeft(dataElement).setParent(y, dataElement);
2181         }
2182 
2183         if (y.getRight(dataElement) != null) {
2184             y.getRight(dataElement).setParent(y, dataElement);
2185         }
2186 
2187         x.swapColors(y, dataElement);
2188 
2189         // Check if root changed
2190         if (rootNode[dataElement.ordinal()] == x) {
2191             rootNode[dataElement.ordinal()] = y;
2192         } else if (rootNode[dataElement.ordinal()] == y) {
2193             rootNode[dataElement.ordinal()] = x;
2194         }
2195     }
2196 
2197     /**
2198      * Returns a string version of this Map in standard format.
2199      *
2200      * @return A standard format string version of the map
2201      */
2202     @Override
2203     public String toString() {
2204         return this.doToString(KEY);
2205     }
2206 
2207     /**
2208      * Returns a set view of the values contained in this map in key order.
2209      * The returned object can be cast to a Set.
2210      * <p>
2211      * The set is backed by the map, so changes to the map are reflected in
2212      * the set, and vice-versa. If the map is modified while an iteration over
2213      * the set is in progress, the results of the iteration are undefined.
2214      * <p>
2215      * The set supports element removal, which removes the corresponding mapping
2216      * from the map. It does not support the add or addAll operations.
2217      *
2218      * @return A set view of the values contained in this map.
2219      */
2220     @Override
2221     public Set<V> values() {
2222         if (valuesSet == null) {
2223             valuesSet = new ValueView(KEY);
2224         }
2225         return valuesSet;
2226     }
2227 
2228     /**
2229      * Serializes this object to an ObjectOutputStream.
2230      *
2231      * @param out The target ObjectOutputStream.
2232      * @throws IOException thrown when an I/O errors occur writing to the target stream.
2233      */
2234     private void writeObject(final ObjectOutputStream out) throws IOException {
2235         out.defaultWriteObject();
2236         out.writeInt(this.size());
2237         for (final Entry<K, V> entry : entrySet()) {
2238             out.writeObject(entry.getKey());
2239             out.writeObject(entry.getValue());
2240         }
2241     }
2242 
2243 }