--- jsr166/src/main/java/util/TreeMap.java 2005/06/16 02:11:13 1.18 +++ jsr166/src/main/java/util/TreeMap.java 2006/04/19 15:07:14 1.28 @@ -1,12 +1,11 @@ /* * %W% %E% * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.util; -import java.util.*; // for javadoc /** * A Red-Black tree based {@link NavigableMap} implementation. @@ -31,19 +30,19 @@ import java.util.*; // for javadoc * is well-defined even if its ordering is inconsistent with equals; it * just fails to obey the general contract of the Map interface. * - *

Note that this implementation is not synchronized. If multiple - * threads access a map concurrently, and at least one of the threads modifies - * the map structurally, it must be synchronized externally. (A - * structural modification is any operation that adds or deletes one or more - * mappings; merely changing the value associated with an existing key is not - * a structural modification.) This is typically accomplished by - * synchronizing on some object that naturally encapsulates the map. If no - * such object exists, the map should be "wrapped" using the - * Collections.synchronizedMap method. This is best done at creation - * time, to prevent accidental unsynchronized access to the map: - *

- *     Map m = Collections.synchronizedMap(new TreeMap(...));
- * 
+ *

Note that this implementation is not synchronized. + * If multiple threads access a map concurrently, and at least one of the + * threads modifies the map structurally, it must be synchronized + * externally. (A structural modification is any operation that adds or + * deletes one or more mappings; merely changing the value associated + * with an existing key is not a structural modification.) This is + * typically accomplished by synchronizing on some object that naturally + * encapsulates the map. + * If no such object exists, the map should be "wrapped" using the + * {@link Collections#synchronizedSortedMap Collections.synchronizedSortedMap} + * method. This is best done at creation time, to prevent accidental + * unsynchronized access to the map:

+ *   SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
* *

The iterators returned by the iterator method of the collections * returned by all of this class's "collection view methods" are @@ -83,7 +82,6 @@ import java.util.*; // for javadoc * @see Comparable * @see Comparator * @see Collection - * @see Collections#synchronizedMap(Map) * @since 1.2 */ @@ -111,6 +109,15 @@ public class TreeMap */ private transient int modCount = 0; + /** + * A sentinel to indicate that an endpoint of a submap is not bounded. + * It is used to generate head maps, tail maps, and descending views + * of the entire backing map. The sentinel must be serializable, + * requiring a little class to express. + */ + private static class Unbounded implements java.io.Serializable {} + private static final Unbounded UNBOUNDED = new Unbounded(); + private void incrementSize() { modCount++; size++; } private void decrementSize() { modCount++; size--; } @@ -217,7 +224,8 @@ public class TreeMap * specified value. More formally, returns true if and only if * this map contains at least one mapping to a value v such * that (value==null ? v==null : value.equals(v)). This - * operation requires time linear in the map size. + * operation will probably require time linear in the map size for + * most implementations. * * @param value value whose presence in this map is to be tested * @return true if a mapping to value exists; @@ -250,16 +258,21 @@ public class TreeMap } /** - * Returns the value to which this map maps the specified key, or - * null if the map contains no mapping for the key. A return - * value of null does not necessarily indicate that the - * map contains no mapping for the key; it's also possible that the map - * explicitly maps the key to null. The {@link #containsKey} - * operation may be used to distinguish these two cases. - * - * @param key key whose associated value is to be returned - * @return the value to which this map maps the specified key, or - * null if the map contains no mapping for the key + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key} compares + * equal to {@code k} according to the map's ordering, then this + * method returns {@code v}; otherwise it returns {@code null}. + * (There can be at most one such mapping.) + * + *

A return value of {@code null} does not necessarily + * indicate that the map contains no mapping for the key; it's also + * possible that the map explicitly maps the key to {@code null}. + * The {@link #containsKey containsKey} operation may be used to + * distinguish these two cases. + * * @throws ClassCastException if the specified key cannot be compared * with the keys currently in the map * @throws NullPointerException if the specified key is null @@ -335,6 +348,8 @@ public class TreeMap // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); + if (key == null) + throw new NullPointerException(); Comparable k = (Comparable) key; Entry p = root; while (p != null) { @@ -520,7 +535,7 @@ public class TreeMap /** * Associates the specified value with the specified key in this map. - * If the map previously contained a mapping for this key, the old + * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated @@ -540,11 +555,12 @@ public class TreeMap Entry t = root; if (t == null) { - if (key == null) { - if (comparator == null) - throw new NullPointerException(); - comparator.compare(key, key); - } + // TBD +// if (key == null) { +// if (comparator == null) +// throw new NullPointerException(); +// comparator.compare(key, key); +// } incrementSize(); root = new Entry(key, value, null); return null; @@ -629,8 +645,8 @@ public class TreeMap clone.size = 0; clone.modCount = 0; clone.entrySet = null; - clone.descendingEntrySet = null; - clone.descendingKeySet = null; + clone.navigableKeySet = null; + clone.descendingMap = null; // Initialize clone with our mappings try { @@ -644,16 +660,25 @@ public class TreeMap // NavigableMap API methods + /** + * @since 1.6 + */ public Map.Entry firstEntry() { Entry e = getFirstEntry(); return (e == null)? null : new AbstractMap.SimpleImmutableEntry(e); } + /** + * @since 1.6 + */ public Map.Entry lastEntry() { Entry e = getLastEntry(); return (e == null)? null : new AbstractMap.SimpleImmutableEntry(e); } + /** + * @since 1.6 + */ public Map.Entry pollFirstEntry() { Entry p = getFirstEntry(); if (p == null) @@ -663,6 +688,9 @@ public class TreeMap return result; } + /** + * @since 1.6 + */ public Map.Entry pollLastEntry() { Entry p = getLastEntry(); if (p == null) @@ -677,6 +705,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public Map.Entry lowerEntry(K key) { Entry e = getLowerEntry(key); @@ -688,6 +717,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public K lowerKey(K key) { Entry e = getLowerEntry(key); @@ -699,6 +729,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public Map.Entry floorEntry(K key) { Entry e = getFloorEntry(key); @@ -710,6 +741,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public K floorKey(K key) { Entry e = getFloorEntry(key); @@ -721,6 +753,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public Map.Entry ceilingEntry(K key) { Entry e = getCeilingEntry(key); @@ -732,6 +765,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public K ceilingKey(K key) { Entry e = getCeilingEntry(key); @@ -743,6 +777,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public Map.Entry higherEntry(K key) { Entry e = getHigherEntry(key); @@ -754,6 +789,7 @@ public class TreeMap * @throws NullPointerException if the specified key is null * and this map uses natural ordering, or its comparator * does not permit null keys + * @since 1.6 */ public K higherKey(K key) { Entry e = getHigherEntry(key); @@ -768,8 +804,8 @@ public class TreeMap * there's no reason to create more than one. */ private transient Set> entrySet = null; - private transient Set> descendingEntrySet = null; - private transient Set descendingKeySet = null; + private transient KeySet navigableKeySet = null; + private transient NavigableMap descendingMap = null; /** * Returns a {@link Set} view of the keys contained in this map. @@ -786,32 +822,22 @@ public class TreeMap * operations. */ public Set keySet() { - Set ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet()); + return navigableKeySet(); } - class KeySet extends AbstractSet { - public Iterator iterator() { - return new KeyIterator(getFirstEntry()); - } - - public int size() { - return TreeMap.this.size(); - } - - public boolean contains(Object o) { - return containsKey(o); - } - - public boolean remove(Object o) { - int oldSize = size; - TreeMap.this.remove(o); - return size != oldSize; - } + /** + * @since 1.6 + */ + public NavigableSet navigableKeySet() { + NavigableSet nks = navigableKeySet; + return (nks != null) ? nks : (navigableKeySet = new KeySet(this)); + } - public void clear() { - TreeMap.this.clear(); - } + /** + * @since 1.6 + */ + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); } /** @@ -834,37 +860,6 @@ public class TreeMap return (vs != null) ? vs : (values = new Values()); } - class Values extends AbstractCollection { - public Iterator iterator() { - return new ValueIterator(getFirstEntry()); - } - - public int size() { - return TreeMap.this.size(); - } - - public boolean contains(Object o) { - for (Entry e = getFirstEntry(); e != null; e = successor(e)) - if (valEquals(e.getValue(), o)) - return true; - return false; - } - - public boolean remove(Object o) { - for (Entry e = getFirstEntry(); e != null; e = successor(e)) { - if (valEquals(e.getValue(), o)) { - deleteEntry(e); - return true; - } - } - return false; - } - - public void clear() { - TreeMap.this.clear(); - } - } - /** * Returns a {@link Set} view of the mappings contained in this map. * The set's iterator returns the entries in ascending key order. @@ -885,62 +880,14 @@ public class TreeMap return (es != null) ? es : (entrySet = new EntrySet()); } - class EntrySet extends AbstractSet> { - public Iterator> iterator() { - return new EntryIterator(getFirstEntry()); - } - - public boolean contains(Object o) { - if (!(o instanceof Map.Entry)) - return false; - Map.Entry entry = (Map.Entry) o; - V value = entry.getValue(); - Entry p = getEntry(entry.getKey()); - return p != null && valEquals(p.getValue(), value); - } - - public boolean remove(Object o) { - if (!(o instanceof Map.Entry)) - return false; - Map.Entry entry = (Map.Entry) o; - V value = entry.getValue(); - Entry p = getEntry(entry.getKey()); - if (p != null && valEquals(p.getValue(), value)) { - deleteEntry(p); - return true; - } - return false; - } - - public int size() { - return TreeMap.this.size(); - } - - public void clear() { - TreeMap.this.clear(); - } - } - - public Set> descendingEntrySet() { - Set> es = descendingEntrySet; - return (es != null) ? es : (descendingEntrySet = new DescendingEntrySet()); - } - - class DescendingEntrySet extends EntrySet { - public Iterator> iterator() { - return new DescendingEntryIterator(getLastEntry()); - } - } - - public Set descendingKeySet() { - Set ks = descendingKeySet; - return (ks != null) ? ks : (descendingKeySet = new DescendingKeySet()); - } - - class DescendingKeySet extends KeySet { - public Iterator iterator() { - return new DescendingKeyIterator(getLastEntry()); - } + /** + * @since 1.6 + */ + public NavigableMap descendingMap() { + NavigableMap km = descendingMap; + return (km != null) ? km : + (descendingMap = new DescendingSubMap((K)UNBOUNDED, 0, + (K)UNBOUNDED, 0)); } /** @@ -949,9 +896,12 @@ public class TreeMap * null and this map uses natural ordering, or its comparator * does not permit null keys * @throws IllegalArgumentException {@inheritDoc} + * @since 1.6 */ - public NavigableMap navigableSubMap(K fromKey, K toKey) { - return new SubMap(fromKey, toKey); + public NavigableMap navigableSubMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) { + return new AscendingSubMap(fromKey, excluded(fromInclusive), + toKey, excluded(toInclusive)); } /** @@ -960,9 +910,10 @@ public class TreeMap * and this map uses natural ordering, or its comparator * does not permit null keys * @throws IllegalArgumentException {@inheritDoc} + * @since 1.6 */ - public NavigableMap navigableHeadMap(K toKey) { - return new SubMap(toKey, true); + public NavigableMap navigableHeadMap(K toKey, boolean inclusive) { + return new AscendingSubMap((K)UNBOUNDED, 0, toKey, excluded(inclusive)); } /** @@ -971,17 +922,21 @@ public class TreeMap * and this map uses natural ordering, or its comparator * does not permit null keys * @throws IllegalArgumentException {@inheritDoc} + * @since 1.6 */ - public NavigableMap navigableTailMap(K fromKey) { - return new SubMap(fromKey, false); + public NavigableMap navigableTailMap(K fromKey, boolean inclusive) { + return new AscendingSubMap(fromKey, excluded(inclusive), (K)UNBOUNDED, 0); + } + + /** + * Translates a boolean "inclusive" value to the correct int value + * for the loExcluded or hiExcluded field. + */ + static int excluded(boolean inclusive) { + return inclusive ? 0 : 1; } /** - * Equivalent to {@link #navigableSubMap} but with a return type - * conforming to the SortedMap interface. - * - *

{@inheritDoc} - * * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if fromKey or toKey is * null and this map uses natural ordering, or its comparator @@ -989,15 +944,10 @@ public class TreeMap * @throws IllegalArgumentException {@inheritDoc} */ public SortedMap subMap(K fromKey, K toKey) { - return new SubMap(fromKey, toKey); + return navigableSubMap(fromKey, true, toKey, false); } /** - * Equivalent to {@link #navigableHeadMap} but with a return type - * conforming to the SortedMap interface. - * - *

{@inheritDoc} - * * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if toKey is null * and this map uses natural ordering, or its comparator @@ -1005,15 +955,10 @@ public class TreeMap * @throws IllegalArgumentException {@inheritDoc} */ public SortedMap headMap(K toKey) { - return new SubMap(toKey, true); + return navigableHeadMap(toKey, false); } /** - * Equivalent to {@link #navigableTailMap} but with a return type - * conforming to the SortedMap interface. - * - *

{@inheritDoc} - * * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if fromKey is null * and this map uses natural ordering, or its comparator @@ -1021,210 +966,283 @@ public class TreeMap * @throws IllegalArgumentException {@inheritDoc} */ public SortedMap tailMap(K fromKey) { - return new SubMap(fromKey, false); + return navigableTailMap(fromKey, true); } - private class SubMap - extends AbstractMap - implements NavigableMap, java.io.Serializable { - private static final long serialVersionUID = -6520786458950516097L; + // View class support - /** - * fromKey is significant only if fromStart is false. Similarly, - * toKey is significant only if toStart is false. - */ - private boolean fromStart = false, toEnd = false; - private K fromKey, toKey; + class Values extends AbstractCollection { + public Iterator iterator() { + return new ValueIterator(getFirstEntry()); + } - SubMap(K fromKey, K toKey) { - if (compare(fromKey, toKey) > 0) - throw new IllegalArgumentException("fromKey > toKey"); - this.fromKey = fromKey; - this.toKey = toKey; + public int size() { + return TreeMap.this.size(); } - SubMap(K key, boolean headMap) { - compare(key, key); // Type-check key + public boolean contains(Object o) { + for (Entry e = getFirstEntry(); e != null; e = successor(e)) + if (valEquals(e.getValue(), o)) + return true; + return false; + } - if (headMap) { - fromStart = true; - toKey = key; - } else { - toEnd = true; - fromKey = key; + public boolean remove(Object o) { + for (Entry e = getFirstEntry(); e != null; e = successor(e)) { + if (valEquals(e.getValue(), o)) { + deleteEntry(e); + return true; + } } + return false; } - SubMap(boolean fromStart, K fromKey, boolean toEnd, K toKey) { - this.fromStart = fromStart; - this.fromKey= fromKey; - this.toEnd = toEnd; - this.toKey = toKey; + public void clear() { + TreeMap.this.clear(); } + } - public boolean isEmpty() { - return entrySet().isEmpty(); + class EntrySet extends AbstractSet> { + public Iterator> iterator() { + return new EntryIterator(getFirstEntry()); } - public boolean containsKey(Object key) { - return inRange(key) && TreeMap.this.containsKey(key); + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry entry = (Map.Entry) o; + V value = entry.getValue(); + Entry p = getEntry(entry.getKey()); + return p != null && valEquals(p.getValue(), value); } - public V get(Object key) { - if (!inRange(key)) - return null; - return TreeMap.this.get(key); + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry entry = (Map.Entry) o; + V value = entry.getValue(); + Entry p = getEntry(entry.getKey()); + if (p != null && valEquals(p.getValue(), value)) { + deleteEntry(p); + return true; + } + return false; } - public V put(K key, V value) { - if (!inRange(key)) - throw new IllegalArgumentException("key out of range"); - return TreeMap.this.put(key, value); + public int size() { + return TreeMap.this.size(); } - public V remove(Object key) { - if (!inRange(key)) - return null; - return TreeMap.this.remove(key); + public void clear() { + TreeMap.this.clear(); } + } - public Comparator comparator() { - return comparator; + /* + * Unlike Values and EntrySet, the KeySet class is static, + * delegating to a NavigableMap to allow use by SubMaps, which + * outweighs the ugliness of needing type-tests for the following + * Iterator methods that are defined appropriately in main versus + * submap classes. + */ + + Iterator keyIterator() { + return new KeyIterator(getFirstEntry()); + } + + Iterator descendingKeyIterator() { + return new DescendingKeyIterator(getFirstEntry()); + } + + static final class KeySet extends AbstractSet implements NavigableSet { + private final NavigableMap m; + KeySet(NavigableMap map) { m = map; } + + public Iterator iterator() { + if (m instanceof TreeMap) + return ((TreeMap)m).keyIterator(); + else + return (Iterator)(((TreeMap.NavigableSubMap)m).keyIterator()); } - public K firstKey() { - TreeMap.Entry e = fromStart ? getFirstEntry() : getCeilingEntry(fromKey); - K first = key(e); - if (!toEnd && compare(first, toKey) >= 0) - throw new NoSuchElementException(); - return first; + public Iterator descendingIterator() { + if (m instanceof TreeMap) + return ((TreeMap)m).descendingKeyIterator(); + else + return (Iterator)(((TreeMap.NavigableSubMap)m).descendingKeyIterator()); } - public K lastKey() { - TreeMap.Entry e = toEnd ? getLastEntry() : getLowerEntry(toKey); - K last = key(e); - if (!fromStart && compare(last, fromKey) < 0) - throw new NoSuchElementException(); - return last; + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean contains(Object o) { return m.containsKey(o); } + public void clear() { m.clear(); } + public E lower(E e) { return m.lowerKey(e); } + public E floor(E e) { return m.floorKey(e); } + public E ceiling(E e) { return m.ceilingKey(e); } + public E higher(E e) { return m.higherKey(e); } + public E first() { return m.firstKey(); } + public E last() { return m.lastKey(); } + public Comparator comparator() { return m.comparator(); } + public E pollFirst() { + Map.Entry e = m.pollFirstEntry(); + return e == null? null : e.getKey(); + } + public E pollLast() { + Map.Entry e = m.pollLastEntry(); + return e == null? null : e.getKey(); + } + public boolean remove(Object o) { + int oldSize = size(); + m.remove(o); + return size() != oldSize; } + public NavigableSet navigableSubSet(E fromElement, + boolean fromInclusive, + E toElement, + boolean toInclusive) { + return new TreeSet + (m.navigableSubMap(fromElement, fromInclusive, + toElement, toInclusive)); + } + public NavigableSet navigableHeadSet(E toElement, boolean inclusive) { + return new TreeSet(m.navigableHeadMap(toElement, inclusive)); + } + public NavigableSet navigableTailSet(E fromElement, boolean inclusive) { + return new TreeSet(m.navigableTailMap(fromElement, inclusive)); + } + public SortedSet subSet(E fromElement, E toElement) { + return navigableSubSet(fromElement, true, toElement, false); + } + public SortedSet headSet(E toElement) { + return navigableHeadSet(toElement, false); + } + public SortedSet tailSet(E fromElement) { + return navigableTailSet(fromElement, true); + } + public NavigableSet descendingSet() { + return new TreeSet(m.descendingMap()); + } + } - public Map.Entry firstEntry() { - TreeMap.Entry e = fromStart ? - getFirstEntry() : getCeilingEntry(fromKey); - if (e == null || (!toEnd && compare(e.key, toKey) >= 0)) - return null; - return e; + // SubMaps + + abstract class NavigableSubMap extends AbstractMap + implements NavigableMap, java.io.Serializable { + + /** + * The low endpoint of this submap in absolute terms. For ascending + * submaps this will be the "first" endpoint; for descending submaps, + * the last. If there is no bound, this field is set to UNBOUNDED. + */ + K lo; + + /** + * Zero if the low endpoint is excluded from this submap, one if + * it's included. This field is unused if lo is UNBOUNDED. + */ + int loExcluded; + + /** + * The high endpoint of this submap in absolute terms. For ascending + * submaps this will be the "last" endpoint; for descending submaps, + * the first. If there is no bound, this field is set to UNBOUNDED. + */ + K hi; + + /** + * Zero if the high endpoint is excluded from this submap, one if + * it's included. This field is unused if hi is UNBOUNDED. + */ + int hiExcluded; + + NavigableSubMap(K lo, int loExcluded, K hi, int hiExcluded) { + if (lo != UNBOUNDED && hi != UNBOUNDED && compare(lo, hi) > 0) + throw new IllegalArgumentException("fromKey > toKey"); + this.lo = lo; + this.loExcluded = loExcluded; + this.hi = hi; + this.hiExcluded = hiExcluded; } - public Map.Entry lastEntry() { - TreeMap.Entry e = toEnd ? - getLastEntry() : getLowerEntry(toKey); - if (e == null || (!fromStart && compare(e.key, fromKey) < 0)) - return null; - return e; + public boolean isEmpty() { + return entrySet().isEmpty(); } - public Map.Entry pollFirstEntry() { - TreeMap.Entry e = fromStart ? - getFirstEntry() : getCeilingEntry(fromKey); - if (e == null || (!toEnd && compare(e.key, toKey) >= 0)) - return null; - Map.Entry result = new AbstractMap.SimpleImmutableEntry(e); - deleteEntry(e); - return result; + public boolean containsKey(Object key) { + return inRange(key) && TreeMap.this.containsKey(key); } - public Map.Entry pollLastEntry() { - TreeMap.Entry e = toEnd ? - getLastEntry() : getLowerEntry(toKey); - if (e == null || (!fromStart && compare(e.key, fromKey) < 0)) + public V get(Object key) { + if (!inRange(key)) return null; - Map.Entry result = new AbstractMap.SimpleImmutableEntry(e); - deleteEntry(e); - return result; + return TreeMap.this.get(key); + } + + public V put(K key, V value) { + if (!inRange(key)) + throw new IllegalArgumentException("key out of range"); + return TreeMap.this.put(key, value); } - private TreeMap.Entry subceiling(K key) { - TreeMap.Entry e = (!fromStart && compare(key, fromKey) < 0)? - getCeilingEntry(fromKey) : getCeilingEntry(key); - if (e == null || (!toEnd && compare(e.key, toKey) >= 0)) + public V remove(Object key) { + if (!inRange(key)) return null; - return e; + return TreeMap.this.remove(key); } public Map.Entry ceilingEntry(K key) { - TreeMap.Entry e = subceiling(key); + TreeMap.Entry e = subCeiling(key); return e == null? null : new AbstractMap.SimpleImmutableEntry(e); } public K ceilingKey(K key) { - TreeMap.Entry e = subceiling(key); + TreeMap.Entry e = subCeiling(key); return e == null? null : e.key; } - - private TreeMap.Entry subhigher(K key) { - TreeMap.Entry e = (!fromStart && compare(key, fromKey) < 0)? - getCeilingEntry(fromKey) : getHigherEntry(key); - if (e == null || (!toEnd && compare(e.key, toKey) >= 0)) - return null; - return e; - } - public Map.Entry higherEntry(K key) { - TreeMap.Entry e = subhigher(key); + TreeMap.Entry e = subHigher(key); return e == null? null : new AbstractMap.SimpleImmutableEntry(e); } public K higherKey(K key) { - TreeMap.Entry e = subhigher(key); + TreeMap.Entry e = subHigher(key); return e == null? null : e.key; } - private TreeMap.Entry subfloor(K key) { - TreeMap.Entry e = (!toEnd && compare(key, toKey) >= 0)? - getLowerEntry(toKey) : getFloorEntry(key); - if (e == null || (!fromStart && compare(e.key, fromKey) < 0)) - return null; - return e; - } - public Map.Entry floorEntry(K key) { - TreeMap.Entry e = subfloor(key); + TreeMap.Entry e = subFloor(key); return e == null? null : new AbstractMap.SimpleImmutableEntry(e); } public K floorKey(K key) { - TreeMap.Entry e = subfloor(key); + TreeMap.Entry e = subFloor(key); return e == null? null : e.key; } - private TreeMap.Entry sublower(K key) { - TreeMap.Entry e = (!toEnd && compare(key, toKey) >= 0)? - getLowerEntry(toKey) : getLowerEntry(key); - if (e == null || (!fromStart && compare(e.key, fromKey) < 0)) - return null; - return e; - } - public Map.Entry lowerEntry(K key) { - TreeMap.Entry e = sublower(key); + TreeMap.Entry e = subLower(key); return e == null? null : new AbstractMap.SimpleImmutableEntry(e); } public K lowerKey(K key) { - TreeMap.Entry e = sublower(key); + TreeMap.Entry e = subLower(key); return e == null? null : e.key; } - private transient Set> entrySet = null; + abstract Iterator keyIterator(); + abstract Iterator descendingKeyIterator(); - public Set> entrySet() { - Set> es = entrySet; - return (es != null)? es : (entrySet = new EntrySetView()); + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); } - private class EntrySetView extends AbstractSet> { + // Views + transient NavigableMap descendingMapView = null; + transient Set> entrySetView = null; + private transient NavigableSet navigableKeySetView = null; + + abstract class EntrySetView extends AbstractSet> { private transient int size = -1, sizeModCount; public int size() { @@ -1269,99 +1287,381 @@ public class TreeMap } return false; } + } - public Iterator> iterator() { - return new SubMapEntryIterator( - (fromStart ? getFirstEntry() : getCeilingEntry(fromKey)), - (toEnd ? null : getCeilingEntry(toKey))); - } + public NavigableSet navigableKeySet() { + NavigableSet nksv = navigableKeySetView; + return (nksv != null) ? nksv : + (navigableKeySetView = new TreeMap.KeySet(this)); } - private transient Set> descendingEntrySetView = null; - private transient Set descendingKeySetView = null; + public Set keySet() { + return navigableKeySet(); + } - public Set> descendingEntrySet() { - Set> es = descendingEntrySetView; - return (es != null) ? es : - (descendingEntrySetView = new DescendingEntrySetView()); - } - - public Set descendingKeySet() { - Set ks = descendingKeySetView; - return (ks != null) ? ks : - (descendingKeySetView = new DescendingKeySetView()); - } - - private class DescendingEntrySetView extends EntrySetView { - public Iterator> iterator() { - return new DescendingSubMapEntryIterator - ((toEnd ? getLastEntry() : getLowerEntry(toKey)), - (fromStart ? null : getLowerEntry(fromKey))); - } + public SortedMap subMap(K fromKey, K toKey) { + return navigableSubMap(fromKey, true, toKey, false); } - private class DescendingKeySetView extends AbstractSet { - public Iterator iterator() { - return new Iterator() { - private Iterator> i = descendingEntrySet().iterator(); - - public boolean hasNext() { return i.hasNext(); } - public K next() { return i.next().getKey(); } - public void remove() { i.remove(); } - }; - } + public SortedMap headMap(K toKey) { + return navigableHeadMap(toKey, false); + } - public int size() { - return SubMap.this.size(); - } + public SortedMap tailMap(K fromKey) { + return navigableTailMap(fromKey, true); + } - public boolean contains(Object k) { - return SubMap.this.containsKey(k); - } + /** Returns the lowest entry in this submap (absolute ordering) */ + TreeMap.Entry loEntry() { + TreeMap.Entry result = + ((lo == UNBOUNDED) ? getFirstEntry() : + (loExcluded == 0) ? getCeilingEntry(lo) : getHigherEntry(lo)); + return (result == null || tooHigh(result.key)) ? null : result; + } + + /** Returns the highest key in this submap (absolute ordering) */ + TreeMap.Entry hiEntry() { + TreeMap.Entry result = + ((hi == UNBOUNDED) ? getLastEntry() : + (hiExcluded == 0) ? getFloorEntry(hi) : getLowerEntry(hi)); + return (result == null || tooLow(result.key)) ? null : result; + } + + /** Polls the lowest entry in this submap (absolute ordering) */ + Map.Entry pollLoEntry() { + TreeMap.Entry e = + ((lo == UNBOUNDED) ? getFirstEntry() : + (loExcluded == 0) ? getCeilingEntry(lo) : getHigherEntry(lo)); + if (e == null || tooHigh(e.key)) + return null; + Map.Entry result = new AbstractMap.SimpleImmutableEntry(e); + deleteEntry(e); + return result; } - public NavigableMap navigableSubMap(K fromKey, K toKey) { - if (!inRange2(fromKey)) + /** Polls the highest key in this submap (absolute ordering) */ + Map.Entry pollHiEntry() { + TreeMap.Entry e = + ((hi == UNBOUNDED) ? getLastEntry() : + (hiExcluded == 0) ? getFloorEntry(hi) : getLowerEntry(hi)); + if (e == null || tooLow(e.key)) + return null; + Map.Entry result = new AbstractMap.SimpleImmutableEntry(e); + deleteEntry(e); + return result; + } + + // The following four definitions are correct only for + // ascending submaps. They are overridden in DescendingSubMap. + // They are defined in the base class because the definitions + // in DescendingSubMap rely on those for AscendingSubMap. + + /** + * Returns the entry corresponding to the ceiling of the specified + * key from the perspective of this submap, or null if the submap + * contains no such entry. + */ + TreeMap.Entry subCeiling(K key) { + if (tooLow(key)) + return loEntry(); + TreeMap.Entry e = getCeilingEntry(key); + return (e == null || tooHigh(e.key)) ? null : e; + } + + /** + * Returns the entry corresponding to the higher of the specified + * key from the perspective of this submap, or null if the submap + * contains no such entry. + */ + TreeMap.Entry subHigher(K key) { + if (tooLow(key)) + return loEntry(); + TreeMap.Entry e = getHigherEntry(key); + return (e == null || tooHigh(e.key)) ? null : e; + } + + /** + * Returns the entry corresponding to the floor of the specified + * key from the perspective of this submap, or null if the submap + * contains no such entry. + */ + TreeMap.Entry subFloor(K key) { + if (tooHigh(key)) + return hiEntry(); + TreeMap.Entry e = getFloorEntry(key); + return (e == null || tooLow(e.key)) ? null : e; + } + + /** + * Returns the entry corresponding to the lower of the specified + * key from the perspective of this submap, or null if the submap + * contains no such entry. + */ + TreeMap.Entry subLower(K key) { + if (tooHigh(key)) + return hiEntry(); + TreeMap.Entry e = getLowerEntry(key); + return (e == null || tooLow(e.key)) ? null : e; + } + + boolean inRange(Object key) { + return (lo == UNBOUNDED || compare(key, lo) >= loExcluded) + && (hi == UNBOUNDED || compare(hi, key) >= hiExcluded); + } + + boolean inClosedRange(Object key) { + return (lo == UNBOUNDED || compare(key, lo) >= 0) + && (hi == UNBOUNDED || compare(hi, key) >= 0); + } + + boolean inRange(Object key, boolean inclusive) { + return inclusive ? inRange(key) : inClosedRange(key); + } + + boolean tooLow(K key) { + return lo != UNBOUNDED && compare(key, lo) < loExcluded; + } + + boolean tooHigh(K key) { + return hi != UNBOUNDED && compare(hi, key) < hiExcluded; + } + } + + class AscendingSubMap extends NavigableSubMap { + private static final long serialVersionUID = 912986545866124060L; + + AscendingSubMap(K lo, int loExcluded, K hi, int hiExcluded) { + super(lo, loExcluded, hi, hiExcluded); + } + + public Comparator comparator() { + return comparator; + } + + public NavigableMap navigableSubMap( + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + if (!inRange(fromKey, fromInclusive)) throw new IllegalArgumentException("fromKey out of range"); - if (!inRange2(toKey)) + if (!inRange(toKey, toInclusive)) throw new IllegalArgumentException("toKey out of range"); - return new SubMap(fromKey, toKey); + return new AscendingSubMap(fromKey, excluded(fromInclusive), + toKey, excluded(toInclusive)); } - public NavigableMap navigableHeadMap(K toKey) { - if (!inRange2(toKey)) + public NavigableMap navigableHeadMap(K toKey, boolean inclusive) { + if (!inClosedRange(toKey)) throw new IllegalArgumentException("toKey out of range"); - return new SubMap(fromStart, fromKey, false, toKey); + return new AscendingSubMap(lo, loExcluded, + toKey, excluded(inclusive)); } - public NavigableMap navigableTailMap(K fromKey) { - if (!inRange2(fromKey)) + public NavigableMap navigableTailMap(K fromKey, boolean inclusive){ + if (!inRange(fromKey, inclusive)) throw new IllegalArgumentException("fromKey out of range"); - return new SubMap(false, fromKey, toEnd, toKey); + return new AscendingSubMap(fromKey, excluded(inclusive), + hi, hiExcluded); } - public SortedMap subMap(K fromKey, K toKey) { - return navigableSubMap(fromKey, toKey); + Iterator keyIterator() { + return new SubMapKeyIterator + (loEntry(), + hi == UNBOUNDED ? null : + hiExcluded == 1 ? getCeilingEntry(hi) : + getHigherEntry(hi)); + } + + Iterator descendingKeyIterator() { + return new DescendingSubMapKeyIterator + (hiEntry(), + lo == UNBOUNDED ? null : + loExcluded == 1 ? getFloorEntry(lo) : + getLowerEntry(lo)); } - public SortedMap headMap(K toKey) { - return navigableHeadMap(toKey); + public Set> entrySet() { + Set> es = entrySetView; + if (es != null) + return es; + return entrySetView = new NavigableSubMap.EntrySetView() { + public Iterator> iterator() { + return new SubMapEntryIterator(loEntry(), + hi == UNBOUNDED ? null : + hiExcluded == 1 ? getCeilingEntry(hi) : + getHigherEntry(hi)); + } + }; } - public SortedMap tailMap(K fromKey) { - return navigableTailMap(fromKey); + public K firstKey() { + return key(loEntry()); + } + + public K lastKey() { + return key(hiEntry()); + } + + public Map.Entry firstEntry() { + return loEntry(); + } + + public Map.Entry lastEntry() { + return hiEntry(); + } + + public Map.Entry pollFirstEntry() { + return pollLoEntry(); + } + + public Map.Entry pollLastEntry() { + return pollHiEntry(); + } + + public NavigableMap descendingMap() { + NavigableMap m = descendingMapView; + return (m != null) ? m : + (descendingMapView = + new DescendingSubMap(lo, loExcluded, hi, hiExcluded)); + } + } + + class DescendingSubMap extends NavigableSubMap { + private static final long serialVersionUID = 912986545866120460L; + DescendingSubMap(K lo, int loExcluded, K hi, int hiExcluded) { + super(lo, loExcluded, hi, hiExcluded); + } + + private final Comparator reverseComparator = + Collections.reverseOrder(comparator); + + public Comparator comparator() { + return reverseComparator; } - private boolean inRange(Object key) { - return (fromStart || compare(key, fromKey) >= 0) && - (toEnd || compare(key, toKey) < 0); + public NavigableMap navigableSubMap( + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + if (!inRange(fromKey, fromInclusive)) + throw new IllegalArgumentException("fromKey out of range"); + if (!inRange(toKey, toInclusive)) + throw new IllegalArgumentException("toKey out of range"); + return new DescendingSubMap(toKey, excluded(toInclusive), + fromKey, excluded(fromInclusive)); + } + + public NavigableMap navigableHeadMap(K toKey, boolean inclusive) { + if (!inRange(toKey, inclusive)) + throw new IllegalArgumentException("toKey out of range"); + return new DescendingSubMap(toKey, inclusive ? 0:1, hi, hiExcluded); + } + + public NavigableMap navigableTailMap(K fromKey, boolean inclusive){ + if (!inRange(fromKey, inclusive)) + throw new IllegalArgumentException("fromKey out of range"); + return new DescendingSubMap(lo, loExcluded, + fromKey, excluded(inclusive)); + } + + Iterator keyIterator() { + return new DescendingSubMapKeyIterator + (hiEntry(), + lo == UNBOUNDED ? null : + loExcluded == 1 ? getFloorEntry(lo) : + getLowerEntry(lo)); + } + + Iterator descendingKeyIterator() { + return new SubMapKeyIterator + (loEntry(), + hi == UNBOUNDED ? null : + hiExcluded == 1 ? getCeilingEntry(hi) : + getHigherEntry(hi)); + } + + public Set> entrySet() { + Set> es = entrySetView; + if (es != null) + return es; + return entrySetView = new NavigableSubMap.EntrySetView() { + public Iterator> iterator() { + return new DescendingSubMapEntryIterator(hiEntry(), + lo == UNBOUNDED ? null : + loExcluded == 1 ? getFloorEntry(lo) : + getLowerEntry(lo)); + } + }; + } + + public K firstKey() { + return key(hiEntry()); + } + + public K lastKey() { + return key(loEntry()); + } + + public Map.Entry firstEntry() { + return hiEntry(); + } + + public Map.Entry lastEntry() { + return loEntry(); + } + + public Map.Entry pollFirstEntry() { + return pollHiEntry(); + } + + public Map.Entry pollLastEntry() { + return pollLoEntry(); + } + + public NavigableMap descendingMap() { + NavigableMap m = descendingMapView; + return (m != null) ? m : + (descendingMapView = + new AscendingSubMap(lo, loExcluded, hi, hiExcluded)); + } + + @Override TreeMap.Entry subCeiling(K key) { + return super.subFloor(key); + } + + @Override TreeMap.Entry subHigher(K key) { + return super.subLower(key); } - // This form allows the high endpoint (as well as all legit keys) - private boolean inRange2(Object key) { - return (fromStart || compare(key, fromKey) >= 0) && - (toEnd || compare(key, toKey) <= 0); + @Override TreeMap.Entry subFloor(K key) { + return super.subCeiling(key); } + + @Override TreeMap.Entry subLower(K key) { + return super.subHigher(key); + } + } + + /** + * This class exists solely for the sake of serialization + * compatibility with previous releases of TreeMap that did not + * support NavigableMap. It translates an old-version SubMap into + * a new-version AscendingSubMap. This class is never otherwise + * used. + */ + private class SubMap extends AbstractMap + implements SortedMap, java.io.Serializable { + private static final long serialVersionUID = -6520786458950516097L; + private boolean fromStart = false, toEnd = false; + private K fromKey, toKey; + private Object readResolve() { + return new AscendingSubMap + (fromStart? ((K)UNBOUNDED) : fromKey, 0, + toEnd? ((K)UNBOUNDED) : toKey, 1); + } + public Set> entrySet() { throw new UnsupportedOperationException(); } + public K lastKey() { throw new UnsupportedOperationException(); } + public K firstKey() { throw new UnsupportedOperationException(); } + public SortedMap subMap(K fromKey, K toKey) { throw new UnsupportedOperationException(); } + public SortedMap headMap(K toKey) { throw new UnsupportedOperationException(); } + public SortedMap tailMap(K fromKey) { throw new UnsupportedOperationException(); } + public Comparator comparator() { throw new UnsupportedOperationException(); } } /** @@ -1376,11 +1676,11 @@ public class TreeMap next = first; } - public boolean hasNext() { + public final boolean hasNext() { return next != null; } - Entry nextEntry() { + final Entry nextEntry() { if (next == null) throw new NoSuchElementException(); if (modCount != expectedModCount) @@ -1390,6 +1690,16 @@ public class TreeMap return lastReturned; } + final Entry prevEntry() { + if (next == null) + throw new NoSuchElementException(); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + lastReturned = next; + next = predecessor(next); + return lastReturned; + } + public void remove() { if (lastReturned == null) throw new IllegalStateException(); @@ -1403,7 +1713,7 @@ public class TreeMap } } - class EntryIterator extends PrivateEntryIterator> { + final class EntryIterator extends PrivateEntryIterator> { EntryIterator(Entry first) { super(first); } @@ -1412,7 +1722,16 @@ public class TreeMap } } - class KeyIterator extends PrivateEntryIterator { + final class ValueIterator extends PrivateEntryIterator { + ValueIterator(Entry first) { + super(first); + } + public V next() { + return nextEntry().value; + } + } + + final class KeyIterator extends PrivateEntryIterator { KeyIterator(Entry first) { super(first); } @@ -1421,46 +1740,46 @@ public class TreeMap } } - class ValueIterator extends PrivateEntryIterator { - ValueIterator(Entry first) { + final class DescendingKeyIterator extends PrivateEntryIterator { + DescendingKeyIterator(Entry first) { super(first); } - public V next() { - return nextEntry().value; + public K next() { + return prevEntry().key; } } - class SubMapEntryIterator extends PrivateEntryIterator> { - private final K firstExcludedKey; + /** + * Iterators for SubMaps + */ + abstract class SubMapIterator implements Iterator { + int expectedModCount = TreeMap.this.modCount; + Entry lastReturned = null; + Entry next; + final K firstExcludedKey; - SubMapEntryIterator(Entry first, Entry firstExcluded) { - super(first); - firstExcludedKey = (firstExcluded == null - ? null + SubMapIterator(Entry first, Entry firstExcluded) { + next = first; + firstExcludedKey = (firstExcluded == null ? null : firstExcluded.key); } - public boolean hasNext() { + public final boolean hasNext() { return next != null && next.key != firstExcludedKey; } - public Map.Entry next() { + final Entry nextEntry() { if (next == null || next.key == firstExcludedKey) throw new NoSuchElementException(); - return nextEntry(); - } - } - - /** - * Base for Descending Iterators. - */ - abstract class DescendingPrivateEntryIterator extends PrivateEntryIterator { - DescendingPrivateEntryIterator(Entry first) { - super(first); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + lastReturned = next; + next = successor(next); + return lastReturned; } - Entry nextEntry() { - if (next == null) + final Entry prevEntry() { + if (next == null || next.key == firstExcludedKey) throw new NoSuchElementException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); @@ -1468,47 +1787,55 @@ public class TreeMap next = predecessor(next); return lastReturned; } + + public void remove() { + if (lastReturned == null) + throw new IllegalStateException(); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + if (lastReturned.left != null && lastReturned.right != null) + next = lastReturned; + deleteEntry(lastReturned); + expectedModCount++; + lastReturned = null; + } } - class DescendingEntryIterator extends DescendingPrivateEntryIterator> { - DescendingEntryIterator(Entry first) { - super(first); + final class SubMapEntryIterator extends SubMapIterator> { + SubMapEntryIterator(Entry first, Entry firstExcluded) { + super(first, firstExcluded); } public Map.Entry next() { return nextEntry(); } } - class DescendingKeyIterator extends DescendingPrivateEntryIterator { - DescendingKeyIterator(Entry first) { - super(first); + final class SubMapKeyIterator extends SubMapIterator { + SubMapKeyIterator(Entry first, Entry firstExcluded) { + super(first, firstExcluded); } public K next() { return nextEntry().key; } } - - class DescendingSubMapEntryIterator extends DescendingPrivateEntryIterator> { - private final K lastExcludedKey; - + final class DescendingSubMapEntryIterator extends SubMapIterator> { DescendingSubMapEntryIterator(Entry last, Entry lastExcluded) { - super(last); - lastExcludedKey = (lastExcluded == null - ? null - : lastExcluded.key); - } - - public boolean hasNext() { - return next != null && next.key != lastExcludedKey; + super(last, lastExcluded); } public Map.Entry next() { - if (next == null || next.key == lastExcludedKey) - throw new NoSuchElementException(); - return nextEntry(); + return prevEntry(); } + } + final class DescendingSubMapKeyIterator extends SubMapIterator { + DescendingSubMapKeyIterator(Entry last, Entry lastExcluded) { + super(last, lastExcluded); + } + public K next() { + return prevEntry().key; + } } /** @@ -1923,8 +2250,6 @@ public class TreeMap } } - - /** * Reconstitute the TreeMap instance from a stream (i.e., * deserialize it). @@ -1986,20 +2311,18 @@ public class TreeMap * @throws ClassNotFoundException propagated from readObject. * This cannot occur if str is null. */ - private - void buildFromSorted(int size, Iterator it, - java.io.ObjectInputStream str, - V defaultVal) + private void buildFromSorted(int size, Iterator it, + java.io.ObjectInputStream str, + V defaultVal) throws java.io.IOException, ClassNotFoundException { this.size = size; - root = - buildFromSorted(0, 0, size-1, computeRedLevel(size), - it, str, defaultVal); + root = buildFromSorted(0, 0, size-1, computeRedLevel(size), + it, str, defaultVal); } /** * Recursive "helper method" that does the real work of the - * of the previous method. Identically named parameters have + * previous method. Identically named parameters have * identical definitions. Additional parameters are documented below. * It is assumed that the comparator and size fields of the TreeMap are * already set prior to calling this method. (It ignores both fields.)