package cz.cuni.mff.peckam.java.origamist.unused.utils; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; /** * A red-black tree implementation. * * Inspired by the Sun JRE's implementation. * * @author Martin Pecka */ public class RedBlackTree<K, V> extends AbstractMap<K, V> implements SortedMap<K, V>, Cloneable, Serializable { /** */ private static final long serialVersionUID = 919286545866124006L; /** The comparator to be used for comparing the keys. */ protected final Comparator<? super K> comparator; /** The root of the tree. */ protected transient Entry root = null; /** Number of elements of the tree. */ protected transient int size = 0; /** Number of modifications, used for detecting concurrent modifications while using iterators. */ protected transient int modCount = 0; /** The factory object for creating new entries. */ protected EntryFactory entryFactory = new EntryFactory(); /** * Colors of the tree's nodes. * * @author Martin Pecka */ protected enum Color { RED, BLACK }; /** * Construct a new red-black tree with the default comparator. The default comparator needs all values inserted into * or deleted from the tree to implement {@link Comparable}<K>. If an element doesn't implement it, an insert * or delete method will end up throwing a {@link ClassCastException}. */ public RedBlackTree() { comparator = getDefaultComparator(); } /** * Construct a new red-black tree using the given comparator. The comparator must be able to compare every two * "valid" keys, and must throw a {@link ClassCastException} if an "invalid" key is to be compared. * * @param comparator The comparator to use for comparing this tree's element's keys. */ public RedBlackTree(Comparator<? super K> comparator) { this.comparator = comparator; } /** * Create a new red-black tree with entries from the given map. Use the default comparator to compare the keys. The * keys must implement {@link Comparable}<K>. * * @param m The map to take entries from. * * @throws ClassCastException If a key from the given map doesn't implement the {@link Comparable}<K> * interface. * @throws NullPointerException If <code>m == null</code> or if the map contains a <code>null</code> key and the * comparator doesn't support it. */ public RedBlackTree(Map<? extends K, ? extends V> m) { this(); putAll(m); } /** * Create a new red-black tree with entries from the given sorted map. The map's comparator is used. This is an * effective (linear time) algorithm. * * @param m The sorted map to take entries from. */ public RedBlackTree(SortedMap<K, ? extends V> m) { this(m.comparator()); buildFromSorted(m.size(), m.entrySet().iterator()); } /** * @return The number of entries in this tree. */ @Override public int size() { return size; } /** * Test if the tree contains an entry with the given key. * * Runs in logarithmic time (or linear to the tree's height). * * @throws ClassCastException If the specified key cannot be compared with the keys currently in the map * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @SuppressWarnings("unchecked") @Override public boolean containsKey(Object key) { return getEntry((K) key) != null; } /** * Test if the tree contains an entry with the given value. * * Runs in time linear to the size of the tree (ineffective). */ @SuppressWarnings("unchecked") @Override public boolean containsValue(Object value) { if (root == null) return false; return getPathForValue((V) value).size() > 0; } /** * Return the value associated to the given key. * * Runs in logarithmic time (or linear to the tree's height). * * @throws ClassCastException if the specified key cannot be compared with the keys currently in the map. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @Override public V get(Object key) { @SuppressWarnings("unchecked") Entry e = getEntry((K) key); return (e == null ? null : e.value); } /** * @return The comparator used for comparing keys. */ @Override public Comparator<? super K> comparator() { return comparator; } /** * Return the lowest key in the tree. * * Runs in logarithmic time (or linear to the tree's height). * * @throws NoSuchElementException {@inheritDoc} */ @Override public K firstKey() { Entry e = getFirstEntry(); if (e == null) throw new NoSuchElementException("No first key was found."); return e.getKey(); } /** * Return the highest key in the tree. * * Runs in logarithmic time (or linear to the tree's height). * * @throws NoSuchElementException {@inheritDoc} */ public K lastKey() { Entry e = getLastEntry(); if (e == null) throw new NoSuchElementException("No last key was found."); return e.getKey(); } /** * Puts all entries from the given map into this tree. * * If the provided map is a {@link SortedMap} and the tree is empty, a linear-time algorithm is used, othwerwise * this operations takes O(n*log n) time. * * @throws ClassCastException If a key or value has invalid class. * @throws NullPointerException If the specified map is <code>null</code> or the specified map contains a * <code>null</code> key and this tree does not permit <code>null</code> keys. */ public void putAll(Map<? extends K, ? extends V> map) { if (size == 0 && map.size() != 0 && map instanceof SortedMap<?, ?>) { Comparator<?> c = ((SortedMap<? extends K, ? extends V>) map).comparator(); if (comparator.equals(c)) { ++modCount; buildFromSorted(map.size(), map.entrySet().iterator()); return; } } super.putAll(map); } /** * Return the entry for the given key. If the key is not found, return <code>null</code>. * * Runs in logarithmic time (or linear to the tree's height). * * @param key The key to find the entry for. * @return The entry for the given key. If the key is not found, return <code>null</code>. * * @throws NullPointerException If key is <code>null</code> and this tree does not permit <code>null</code> keys. */ protected Entry getEntry(K key) { Entry p = root; while (p != null) { int cmp = comparator.compare(key, p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } return null; } /** * Returns the first entry in the tree (according to the comparator). * * @return The entry with the lowest key. <code>null</code> if the tree is empty. */ protected Entry getFirstEntry() { Entry p = root; if (p != null) while (p.left != null) p = p.left; return p; } /** * Returns the last entry in the tree (according to the comparator). * * @return The entry with the highest key. <code>null</code> if the tree is empty. */ protected Entry getLastEntry() { Entry p = root; if (p != null) while (p.right != null) p = p.right; return p; } /** * Return the path to the given key. If this tree doesn't contain the given key, the path will end with the entry * the key would be a child of. * * Runs in logarithmic time (or linear to the tree's height). * * @param key The key to find path for. * @return Return the path to the given key. If this tree doesn't contain the given key, the path will end with the * entry the key would be a child of. */ protected TreePath getPath(K key) { TreePath path = entryFactory.createTreePath(); Entry p = root; while (p != null) { path.addLast(p); int cmp = comparator.compare(key, p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else break; } return path; } /** * Return the path to the given value. If this tree doesn't contain the given value, empty path will be returned. * * Runs in time linear to the size of this tree (ineffective). * * @param value The value to find path for. * @return The path to the given value. If this tree doesn't contain the given value, empty path will be returned. */ protected TreePath getPathForValue(V value) { TreePath path = entryFactory.createTreePath(); path.add(root); if (searchPathForValue(value, path)) { return path; } else { return entryFactory.createTreePath(); } } /** * Return whether the subtree defined by the last entry of the given path contains the given value. If it contains, * <code>path</code> will contain the path to the value's entry, otherwise it will be left unchanged. * * @param value The value to find path for. * @param path The path that defines the subtree to search within. * @return Return whether the subtree defined by the last entry of the given path contains the given value. */ protected boolean searchPathForValue(V value, TreePath path) { Entry p = path.getLast(); if (p.value.equals(value)) { return true; } else { if (p.left != null) { path.addLast(p.left); if (searchPathForValue(value, path)) { return true; } path.removeLast(); } if (p.right != null) { path.addLast(p.right); if (searchPathForValue(value, path)) { return true; } path.removeLast(); } } return false; } /** * Return an unmodifiable view of the entry for the given key. * * @param key The key to find the entry for. * @return The entry for the given key. If the key is not found, return <code>null</code>. * * @throws NullPointerException If key is <code>null</code> and this tree does not permit <code>null</code> keys. */ public Map.Entry<K, V> getEntryForKey(K key) { return exportEntry(getEntry(key)); } /** * Put the key and value in the tree. * * Runs in logarithmic time (or linear to the tree's height). * * @throws ClassCastException If the specified key cannot be compared with the keys currently in the map. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @Override public V put(K key, V value) { if (root == null) { comparator.compare(key, key); // throws NPE if the comparator doesn't support null root = entryFactory.createEntry(key, value); size = 1; modCount++; return null; } TreePath path = getPath(key); // if we found the key, just change its associated value if (path.endsWithKey(key)) return path.getLast().setValue(value); int cmp = comparator.compare(key, path.getLast().getKey()); // now we have the new entry's parent as the last item on the path Entry e = entryFactory.createEntry(key, value); if (cmp < 0) path.getLast().left = e; else path.getLast().right = e; path.addLast(e); repairTreeAfterInsert(path); size++; modCount++; return null; } /** * Removes the entry with the given key, if it exists in the tree. * * Runs in logarithmic time (or linear to the tree's height). * * @throws ClassCastException if the specified key cannot be compared with the keys currently in the map. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @Override public V remove(Object key) { @SuppressWarnings("unchecked") K k = (K) key; TreePath path = getPath(k); if (!path.endsWithKey(k)) return null; V oldValue = path.getLast().value; deleteLastPathEntry(path); return oldValue; } @Override public void clear() { modCount++; size = 0; root = null; } /** * Repair the tree structure after insert. * * The algorithm is taken from http://en.wikipedia.org/wiki/Red-black_tree#Insertion . The first call to insert1() * from the wiki page is already done in the put() method. All calls to methods insertI() are using tail-recursion, * so no additional space on stack is needed to perform the repair. * * @param path The path to the newly added entry. */ protected void repairTreeAfterInsert(RedBlackTree<K, V>.TreePath path) { path.getLast().color = Color.RED; insert2(path); } private void insert1(RedBlackTree<K, V>.TreePath path) { if (path.size() == 1) path.getLast().color = Color.BLACK; else insert2(path); } private void insert2(RedBlackTree<K, V>.TreePath path) { if (colorOf(path.getLast(1)) == Color.BLACK) return; insert3(path); } private void insert3(RedBlackTree<K, V>.TreePath path) { Entry uncle = leftOf(path.getLast(2)); if (uncle == path.getLast(1)) uncle = rightOf(path.getLast(2)); if (colorOf(uncle) == Color.RED) { path.getLast(1).color = Color.BLACK; uncle.color = Color.BLACK; path.getLast(2).color = Color.RED; path.removeLast(); path.removeLast(); insert1(path); } else { insert4(path); } } private void insert4(RedBlackTree<K, V>.TreePath path) { Entry grand = path.getLast(2); if (path.getLast() == rightOf(path.getLast(1)) && path.getLast(1) == leftOf(grand)) { path.removeLast(); path.rotateLeft(); } else if (path.getLast() == leftOf(path.getLast(1)) && path.getLast(1) == rightOf(grand)) { path.removeLast(); path.rotateRight(); } insert5(path); } private void insert5(RedBlackTree<K, V>.TreePath path) { Entry grand = path.getLast(2); path.getLast(1).color = Color.BLACK; grand.color = Color.RED; if (leftOf(path.getLast(1)) == path.getLast() && path.getLast(1) == leftOf(grand)) { path.removeLast(); path.removeLast(); path.rotateRight(); } else { path.removeLast(); path.removeLast(); path.rotateLeft(); } } /** * Delete the path's last entry and repair the tree. The path will be returned in an undetermined state. * * The algorithm is taken from http://en.wikipedia.org/wiki/Red-black_tree#Removal . All calls to methods deleteI() * are using tail-recursion, so no additional space on stack is needed to perform the repair. * * @param path The path to the entry to be deleted. */ protected void deleteLastPathEntry(RedBlackTree<K, V>.TreePath path) { modCount++; size--; Entry p = path.getLast(); // If p has 2 children, copy successor's element to p and then make p point to successor. if (p.left != null && p.right != null) { path.moveToSuccesor(); // path will lead to a node in p's subtree, because p has a right child Entry s = path.getLast(); p.key = s.key; p.value = s.value; p = s; } // now we can be sure that p has at most one non-null child // Start fixup at replacement node, if it exists. Entry replacement = (p.left != null ? p.left : p.right); if (replacement != null) { if (path.size() == 1) root = replacement; else if (p == path.getLast(1).left) path.getLast(1).left = replacement; else path.getLast(1).right = replacement; path.removeLast(); path.addLast(replacement); // Fix replacement if (p.color == Color.BLACK) { if (replacement.color == Color.RED) replacement.color = Color.BLACK; else repairTreeAfterDeletion(path); } } else if (path.size() == 1) { // return if we are the only node. root = null; } else { // No children. Use self as phantom replacement and unlink. if (p.color == Color.BLACK) { repairTreeAfterDeletion(path); // p is surely still a leaf, so we can just remove it TreePath newPath = getPath(p.key); if (newPath.getLast().right != null) { // getPath just finds a path to the first occurence of the key, but we want to remove its second // occurence newPath.addLast(newPath.getLast().right); while (newPath.getLast().left != null) { newPath.addLast(newPath.getLast().left); } } if (newPath.size() > 1) { if (p == newPath.getLast(1).left) newPath.getLast(1).left = null; else newPath.getLast(1).right = null; } } else { if (p == path.getLast(1).left) { path.getLast(1).left = null; } else { path.getLast(1).right = null; } path.removeLast(); } } } /** * Repair the tree structure after deletion. * * @param path Path points to the entry used as replacement (or the deleted entry itself if it has no children). * After the repair is complete, path leads to the entry with the same key as it led before calling this * procedure. */ protected void repairTreeAfterDeletion(RedBlackTree<K, V>.TreePath path) { delete1(path); } private void delete1(RedBlackTree<K, V>.TreePath path) { if (path.size() > 1) delete2(path); else root = path.getLast(); } private void delete2(RedBlackTree<K, V>.TreePath path) { Entry sibling = leftOf(path.getLast(1)); if (sibling == path.getLast()) sibling = rightOf(path.getLast(1)); if (colorOf(sibling) == Color.RED) { path.getLast(1).color = Color.RED; sibling.color = Color.BLACK; if (path.getLast() == leftOf(path.getLast(1))) { path.removeLast(); path.rotateLeft(); path.addLast(path.getLast().left); } else { path.removeLast(); path.rotateRight(); path.addLast(path.getLast().right); } } delete3(path); } private void delete3(RedBlackTree<K, V>.TreePath path) { Entry sibling = leftOf(path.getLast(1)); if (sibling == path.getLast()) sibling = rightOf(path.getLast(1)); if (path.getLast(1).color == Color.BLACK && colorOf(sibling) == Color.BLACK && colorOf(sibling.left) == Color.BLACK && colorOf(sibling.right) == Color.BLACK) { sibling.color = Color.RED; path.removeLast(); delete1(path); } else { delete4(path); } } private void delete4(RedBlackTree<K, V>.TreePath path) { Entry sibling = leftOf(path.getLast(1)); if (sibling == path.getLast()) sibling = rightOf(path.getLast(1)); if (path.getLast(1).color == Color.RED && colorOf(sibling) == Color.BLACK && colorOf(sibling.left) == Color.BLACK && colorOf(sibling.right) == Color.BLACK) { sibling.color = Color.RED; path.getLast(1).color = Color.BLACK; } else { delete5(path); } } private void delete5(RedBlackTree<K, V>.TreePath path) { Entry sibling = leftOf(path.getLast(1)); if (sibling == path.getLast()) sibling = rightOf(path.getLast(1)); if (sibling.color == Color.BLACK) { if (path.getLast() == leftOf(path.getLast(1)) && colorOf(sibling.left) == Color.RED && colorOf(sibling.right) == Color.BLACK) { sibling.color = Color.RED; sibling.left.color = Color.BLACK; path.removeLast(); path.addLast(path.getLast().right); path.rotateRight(); path.removeLast(); path.removeLast(); path.addLast(path.getLast().left); } else if (path.getLast() == rightOf(path.getLast(1)) && colorOf(sibling.left) == Color.BLACK && colorOf(sibling.right) == Color.RED) { sibling.color = Color.RED; sibling.right.color = Color.BLACK; path.removeLast(); path.addLast(path.getLast().left); path.rotateLeft(); path.removeLast(); path.removeLast(); path.addLast(path.getLast().right); } } delete6(path); } private void delete6(RedBlackTree<K, V>.TreePath path) { Entry sibling = leftOf(path.getLast(1)); if (sibling == path.getLast()) sibling = rightOf(path.getLast(1)); sibling.color = path.getLast(1).color; path.getLast(1).color = Color.BLACK; if (path.getLast() == path.getLast(1).left) { sibling.right.color = Color.BLACK; path.removeLast(); path.rotateLeft(); } else { sibling.left.color = Color.BLACK; path.removeLast(); path.rotateRight(); } } /** * Linear time tree building algorithm from sorted data. * * It is assumed that the comparator of the tree is already set prior to calling this method. * * @param size The size of the built tree. * @param it New entries are created from entries from this iterator. */ protected void buildFromSorted(int size, Iterator<?> it) { this.size = size; root = buildFromSorted(0, 0, size - 1, computeRedLevel(size), it); } /** * Find the level down to which to assign all nodes BLACK. This is the last `full' level of the complete binary tree * produced by buildTree. The remaining nodes are colored RED. (This makes a `nice' set of color assignments wrt * future insertions.) This level number is computed by finding the number of splits needed to reach the zeroeth * node. (The answer is ~lg(N), but in any case must be computed by same quick O(lg(N)) loop.) */ protected int computeRedLevel(int sz) { int level = 0; for (int m = sz - 1; m >= 0; m = m / 2 - 1) level++; return level; } /** * Recursive "helper method" that does the real work of the previous method. * * It is assumed that the comparator and size fields of the RedBlackTree are already set prior to calling this * method. (It ignores both fields.) * * @param level The current level of tree. Initial call should be 0. * @param lo The first element index of this subtree. Initial should be 0. * @param hi The last element index of this subtree. Initial should be size-1. * @param redLevel The level at which nodes should be red. Must be equal to computeRedLevel for tree of this size. * @param it New entries are created from entries from this iterator. */ private final Entry buildFromSorted(int level, int lo, int hi, int redLevel, Iterator<?> it) { /* * Strategy: The root is the middlemost element. To get to it, we * have to first recursively construct the entire left subtree, * so as to grab all of its elements. We can then proceed with right * subtree. * * The lo and hi arguments are the minimum and maximum * indices to pull out of the iterator or stream for current subtree. * They are not actually indexed, we just proceed sequentially, * ensuring that items are extracted in corresponding order. */ if (hi < lo) return null; int mid = (lo + hi) / 2; Entry left = null; if (lo < mid) left = buildFromSorted(level + 1, lo, mid - 1, redLevel, it); K key; V value; @SuppressWarnings("unchecked") Map.Entry<K, V> entry = (java.util.Map.Entry<K, V>) it.next(); key = entry.getKey(); value = entry.getValue(); Entry middle = entryFactory.createEntry(key, value); // color nodes in non-full bottommost level red if (level == redLevel) middle.color = Color.RED; if (left != null) { middle.left = left; } if (mid < hi) { Entry right = buildFromSorted(level + 1, mid + 1, hi, redLevel, it); middle.right = right; } return middle; } /** * Returns a shallow copy of this <code>RedBlackTree</code> instance. (The keys and * values themselves are not cloned.) * * @return A shallow copy of this tree. */ @SuppressWarnings("unchecked") @Override public RedBlackTree<K, V> clone() { RedBlackTree<K, V> clone = null; try { clone = (RedBlackTree<K, V>) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(); } clone.root = null; clone.size = 0; clone.modCount = 0; clone.entrySet = null; clone.keySet = null; clone.buildFromSorted(size, entrySet().iterator()); return clone; } /** * A view on a part of this tree. * * @author Martin Pecka */ protected class SubMap extends AbstractMap<K, V> implements SortedMap<K, V> { /** Bounds. */ protected K from, to; /** Are the bounds inclusive? */ protected boolean fromIncl, toIncl; /** Ignore bounds? */ protected boolean fromStart, toEnd; /** * Create a submap defined by the arguments. * * @param fromStart If true, ignore the lower bound, the submap will be down-unbounded. * @param from The lower bound. * @param fromIncl If true, the lower bound is treated as inclusive. * @param toEnd If true, ignore the upper bound, the submap will be up-unbounded. * @param to The upper bound. * @param toIncl If true, the upper bound is treated as inclusive. * * @throws IllegalArgumentException If from > to and the bounds aren't ignored. */ public SubMap(boolean fromStart, K from, boolean fromIncl, boolean toEnd, K to, boolean toIncl) { if (!fromStart && !toEnd) { if (comparator.compare(from, to) > 0) throw new IllegalArgumentException("Cannot construct submap with from > to."); } else { if (!fromStart) comparator.compare(from, from); // type check if (!toEnd) comparator.compare(to, to); // type check } this.from = from; this.to = to; this.fromIncl = fromIncl; this.toIncl = toIncl; this.fromStart = fromStart; this.toEnd = toEnd; } @Override public int size() { return (fromStart && toEnd) ? RedBlackTree.this.size() : entrySet().size(); } @Override public boolean isEmpty() { return (fromStart && toEnd) ? RedBlackTree.this.isEmpty() : entrySet().isEmpty(); } /** * Returns true if the given key is in the range this submap works on. * * @param key The key to compare. * @return true if the given key is in the range this submap works on. * * @throws NullPointerException If the key is <code>null</code> and the iterator doesn't handle * <code>null</code> values. * @throws ClassCastException If key cannot be cast to the type of keys in the tree. */ protected boolean keyValid(Object key) { @SuppressWarnings("unchecked") K k = (K) key; comparator.compare(k, k); // check type return (fromStart || comparator.compare(from, k) < (fromIncl ? 1 : 0)) && (toEnd || comparator.compare(k, to) < (toIncl ? 1 : 0)); } @Override public boolean containsKey(Object key) { return keyValid(key) && RedBlackTree.this.containsKey(key); } @Override public boolean containsValue(Object value) { @SuppressWarnings("unchecked") TreePath path = getPathForValue((V) value); if (path.size() == 0) return false; return containsKey(path.getLast().getKey()); } @Override public V get(Object key) { if (!keyValid(key)) return null; return RedBlackTree.this.get(key); } @Override public V put(K key, V value) { if (!keyValid(key)) throw new IllegalArgumentException("The given key is out of range."); return null; } @Override public V remove(Object key) { return keyValid(key) ? RedBlackTree.this.remove(key) : null; } @Override public void putAll(Map<? extends K, ? extends V> m) { if (fromStart && toEnd) { RedBlackTree.this.putAll(m); } else { for (Entry<? extends K, ? extends V> e : m.entrySet()) { put(e.getKey(), e.getValue()); } } } @Override public void clear() { if (fromStart && toEnd) { RedBlackTree.this.clear(); } else { entrySet().clear(); } } @Override public Comparator<? super K> comparator() { return comparator; } @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { if (!keyValid(fromKey)) throw new IllegalArgumentException( "Cannot construct submap with lower bound outside the containing submap."); if (!keyValid(toKey)) throw new IllegalArgumentException( "Cannot construct submap with upper bound outside the containing submap."); return RedBlackTree.this.subMap(fromKey, toKey); } @Override public SortedMap<K, V> headMap(K toKey) { if (!keyValid(toKey)) throw new IllegalArgumentException( "Cannot construct submap with upper bound outside the containing submap."); if (fromStart) { return RedBlackTree.this.headMap(toKey); } else { return subMap(from, toKey); } } @Override public SortedMap<K, V> tailMap(K fromKey) { if (!keyValid(fromKey)) throw new IllegalArgumentException( "Cannot construct submap with lower bound outside the containing submap."); if (toEnd) { return RedBlackTree.this.tailMap(fromKey); } else { return subMap(fromKey, to); } } @Override public K firstKey() { if (fromStart) { return RedBlackTree.this.firstKey(); } else { TreePath path = getPath(from); while (path.size() > 0 && comparator.compare(path.getLast().getKey(), from) < (fromIncl ? 0 : 1)) path.moveToSuccesor(); if (path.size() > 0) { return path.getLast().getKey(); } else { return null; } } } @Override public K lastKey() { if (toEnd) { return RedBlackTree.this.lastKey(); } else { TreePath path = getPath(to); while (path.size() > 0 && comparator.compare(to, path.getLast().getKey()) < (toIncl ? 0 : 1)) path.moveToPredecessor(); if (path.size() > 0) { return path.getLast().getKey(); } else { return null; } } } protected SortedSet<K> keySet = null; protected class SubMapKeySet extends AbstractSet<K> implements SortedSet<K> { @Override public Iterator<K> iterator() { RedBlackTree<K, V>.Entry first = fromStart ? getFirstEntry() : getEntry(firstKey()); return new KeyIterator(first) { @Override public boolean hasNext() { return super.hasNext() && keyValid(pathToNext.getLast().getKey()); } }; } @Override public int size() { return SubMap.this.size(); } @Override public boolean remove(Object o) { int oldSize = SubMap.this.size(); SubMap.this.remove(o); return oldSize != SubMap.this.size(); } @Override public boolean contains(Object o) { return SubMap.this.containsKey(o); } @Override public Comparator<? super K> comparator() { return comparator; } @Override public SortedSet<K> subSet(K fromElement, K toElement) { return new SubMap(false, fromElement, true, false, toElement, false).keySet(); } @Override public SortedSet<K> headSet(K toElement) { return new SubMap(true, null, true, false, toElement, false).keySet(); } @Override public SortedSet<K> tailSet(K fromElement) { return new SubMap(false, fromElement, true, true, null, false).keySet(); } @Override public K first() { return SubMap.this.firstKey(); } @Override public K last() { return SubMap.this.lastKey(); } } @Override public SortedSet<K> keySet() { if (keySet == null) keySet = new SubMapKeySet(); return keySet; } protected class SubMapValues extends AbstractCollection<V> { @Override public Iterator<V> iterator() { RedBlackTree<K, V>.Entry first = fromStart ? getFirstEntry() : getEntry(firstKey()); return new ValueIterator(first) { @Override public boolean hasNext() { return super.hasNext() && keyValid(pathToNext.getLast().getKey()); } }; } @Override public int size() { return SubMap.this.size(); } } @Override public Collection<V> values() { if (values == null) values = new SubMapValues(); return values; } protected Set<Map.Entry<K, V>> entrySet = null; protected class SubMapEntrySet extends AbstractSet<Map.Entry<K, V>> { @Override public Iterator<Map.Entry<K, V>> iterator() { RedBlackTree<K, V>.Entry first = fromStart ? getFirstEntry() : getEntry(firstKey()); return new EntryIterator(first) { @Override public boolean hasNext() { return super.hasNext() && keyValid(pathToNext.getLast().getKey()); } }; } /** Cache modcount in the times we last computed submap size. */ private int sizeModCount = -1; /** */ private int cachedSize = 0; @Override public int size() { if (sizeModCount == modCount) { return cachedSize; } sizeModCount = modCount; TreePath path = getPath(SubMap.this.firstKey()); K last = SubMap.this.lastKey(); cachedSize = 0; while (path.size() > 0 && comparator.compare(path.getLast().getKey(), last) <= 0) { cachedSize++; path.moveToSuccesor(); } return cachedSize; } @Override public boolean remove(Object o) { @SuppressWarnings("unchecked") Map.Entry<K, V> e = (Map.Entry<K, V>) o; TreePath path = getPath(e.getKey()); if (!path.endsWithKey(e.getKey())) return false; if (!valEquals(path.getLast().getValue(), e.getValue())) return false; int oldSize = SubMap.this.size(); SubMap.this.remove(e.getKey()); return oldSize != SubMap.this.size(); } @Override public boolean contains(Object o) { @SuppressWarnings("unchecked") Map.Entry<K, V> e = (Map.Entry<K, V>) o; if (!keyValid(e.getKey())) return false; Map.Entry<K, V> treeE = getEntryForKey(e.getKey()); return treeE != null && valEquals(treeE.getValue(), e.getValue()); } } @Override public Set<Map.Entry<K, V>> entrySet() { if (entrySet == null) entrySet = new SubMapEntrySet(); return entrySet; } } /** * @throws ClassCastException If the key cannot be compared to the keys in the tree. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. * @throws IllegalArgumentException If <code>fromKey > toKey</code>. */ @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { return new SubMap(false, fromKey, true, false, toKey, false); } /** * @throws ClassCastException If the key cannot be compared to the keys in the tree. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @Override public SortedMap<K, V> headMap(K toKey) { return new SubMap(true, null, true, false, toKey, false); } /** * @throws ClassCastException If the key cannot be compared to the keys in the tree. * @throws NullPointerException If the specified key is <code>null</code> and this tree's comparator does not permit * <code>null</code> keys. */ @Override public SortedMap<K, V> tailMap(K fromKey) { return new SubMap(false, fromKey, true, true, null, false); } /** * The view of the values of this tree. * * @author Martin Pecka */ class Values extends AbstractCollection<V> { @Override public Iterator<V> iterator() { return new ValueIterator(getFirstEntry()); } @Override public int size() { return RedBlackTree.this.size(); } @Override public boolean contains(Object o) { return RedBlackTree.this.containsValue(o); } @Override public boolean remove(Object o) { @SuppressWarnings("unchecked") TreePath path = getPathForValue((V) o); if (path.size() > 0) { deleteLastPathEntry(path); return true; } return false; } @Override public void clear() { RedBlackTree.this.clear(); } } /** * A set of views on the entries of the tree. * * @author Martin Pecka */ class EntrySet extends AbstractSet<Map.Entry<K, V>> { @Override public Iterator<Map.Entry<K, V>> iterator() { return new EntryIterator(getFirstEntry()); } @Override public boolean contains(Object o) { if (!(o instanceof Map.Entry<?, ?>)) return false; @SuppressWarnings("unchecked") Map.Entry<K, V> entry = (Map.Entry<K, V>) o; Entry p = getEntry(entry.getKey()); return p != null && valEquals(p.getValue(), entry.getValue()); } @Override public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; @SuppressWarnings("unchecked") Map.Entry<K, V> entry = (Map.Entry<K, V>) o; TreePath path = getPath(entry.getKey()); if (path.endsWithKey(entry.getKey()) && valEquals(path.getLast().getValue(), entry.getValue())) { deleteLastPathEntry(path); return true; } return false; } @Override public int size() { return RedBlackTree.this.size(); } @Override public void clear() { RedBlackTree.this.clear(); } } /** * The view of keys in this tree. * * @author Martin Pecka */ protected class KeySet extends AbstractSet<K> implements SortedSet<K> { @Override public Iterator<K> iterator() { return keyIterator(); } public Iterator<K> descendingIterator() { return descendingKeyIterator(); } @Override public int size() { return RedBlackTree.this.size(); } @Override public K first() { return firstKey(); } @Override public K last() { return lastKey(); } @Override public Comparator<? super K> comparator() { return RedBlackTree.this.comparator(); } @Override public boolean remove(Object o) { int oldSize = size(); RedBlackTree.this.remove(o); return size() != oldSize; } @Override public SortedSet<K> subSet(K fromElement, K toElement) { return new SubMap(false, fromElement, true, false, toElement, false).keySet(); } @Override public SortedSet<K> headSet(K toElement) { return new SubMap(true, null, true, false, toElement, false).keySet(); } @Override public SortedSet<K> tailSet(K fromElement) { return new SubMap(false, fromElement, true, true, null, false).keySet(); } } private Set<K> keySet = null; @Override public Set<K> keySet() { if (keySet == null) keySet = new KeySet(); return keySet; } private Collection<V> values = null; @Override public Collection<V> values() { if (values == null) values = new Values(); return values; } /** A view of the tree's entries. */ private transient EntrySet entrySet = null; @Override public Set<Map.Entry<K, V>> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } /** * The base for all iterators. */ protected abstract class BaseEntryIterator<T> implements Iterator<T> { /** The path to the last returned entry. */ protected TreePath pathToLastReturned; /** The path to "next" entry (if prevEntry() was called, this contains the path to the previous entry). */ protected TreePath pathToNext; /** * Modification counter state in the time of this iterator's creation - serves for revealing concurrent * modification. */ protected int expectedModCount; /** Set to true in each remove() call and to false in each nextEntry() and prevEntry(). */ protected boolean usedRemove = false; /** * Create the iterator with <code>first</code> as the first entry to be returned by next(). * * @param first The first entry to return. Can be <code>null</code> (in that case the iterator will be empty). */ public BaseEntryIterator(Entry first) { expectedModCount = modCount; pathToLastReturned = null; if (first != null) pathToNext = getPath(first.getKey()); else pathToNext = entryFactory.createTreePath(); } @Override public boolean hasNext() { return pathToNext.size() > 0; } /** * @return The next entry. */ protected Entry nextEntry() { if (pathToNext.size() == 0) throw new NoSuchElementException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); usedRemove = false; pathToLastReturned = pathToNext; pathToNext = entryFactory.createTreePath(); pathToNext.addAll(pathToLastReturned); pathToNext.moveToSuccesor(); return pathToLastReturned.getLast(); } /** * @return The previous entry. */ protected Entry prevEntry() { if (pathToNext.size() == 0) throw new NoSuchElementException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); usedRemove = false; pathToLastReturned = pathToNext; pathToNext = entryFactory.createTreePath(); pathToNext.addAll(pathToLastReturned); pathToNext.moveToPredecessor(); return pathToLastReturned.getLast(); } @Override public void remove() { if (pathToLastReturned == null || usedRemove) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); deleteLastPathEntry(pathToLastReturned); // the path to the next node may have changed pathToNext = getPath(pathToNext.getLast().getKey()); expectedModCount = modCount; usedRemove = true; } } /** * Iterator over entries. * * @author Martin Pecka */ protected class EntryIterator extends BaseEntryIterator<Map.Entry<K, V>> { /** * Create the iterator over entries beginning with first. * * @param first The first entry to start with. */ public EntryIterator(Entry first) { super(first); } @Override public Map.Entry<K, V> next() { return nextEntry(); } } /** * Iterator over values (but sorted by keys). * * @author Martin Pecka */ protected class ValueIterator extends BaseEntryIterator<V> { /** * Create the iterator over values beginning with <code>first</code> as the first entry. * * @param first The first entry to start with. */ public ValueIterator(Entry first) { super(first); } @Override public V next() { return nextEntry().value; } } /** * Iterator over keys. * * @author Martin Pecka */ protected class KeyIterator extends BaseEntryIterator<K> { /** * Create the iterator over keys beginning with <code>first</code> as the first entry. * * @param first The first entry to start with. */ public KeyIterator(Entry first) { super(first); } @Override public K next() { return nextEntry().key; } } /** * Iterator over keys in reverse order. * * @author Martin Pecka */ protected class DescendingKeyIterator extends BaseEntryIterator<K> { /** * Create the iterator over keys beginning with <code>first</code> as the first entry. * * @param first The first entry to start with. */ public DescendingKeyIterator(Entry first) { super(first); } @Override public K next() { return prevEntry().key; } } /** * @return The iterator over keys in ascending order. */ public Iterator<K> keyIterator() { return new KeyIterator(getFirstEntry()); } /** * @return The iterator over keys in descending order. */ public Iterator<K> descendingKeyIterator() { return new DescendingKeyIterator(getLastEntry()); } /** * Test two values for equality. Differs from o1.equals(o2) only in that it copes with <tt>null</tt> o1 properly. */ protected final static boolean valEquals(Object o1, Object o2) { return (o1 == null ? o2 == null : o1.equals(o2)); } /** * Return SimpleImmutableEntry for entry, or <code>null</code> if <code>null</code>. */ protected static <K, V> Map.Entry<K, V> exportEntry(RedBlackTree<K, V>.Entry e) { return e == null ? null : new AbstractMap.SimpleImmutableEntry<K, V>(e); } /** * Return the color of the given entry, or Black, if it is <code>null</code>. * * @param p The entry to get color of. * @return The color of the given entry, or Black, if it is <code>null</code>. */ protected static <K, V> Color colorOf(RedBlackTree<K, V>.Entry p) { return (p == null ? Color.BLACK : p.color); } /** * Set p's color to c, if p isn't <code>null</code>. * * @param p The entry to set color for. * @param c The color to set. */ protected static <K, V> void setColor(RedBlackTree<K, V>.Entry p, Color c) { if (p != null) p.color = c; } /** * Return the left child of p, or <code>null</code> if p is <code>null</code>. * * @param p The entry to get left child of. * @return The left child of p, or <code>null</code> if p is <code>null</code>. */ protected static <K, V> RedBlackTree<K, V>.Entry leftOf(RedBlackTree<K, V>.Entry p) { return (p == null) ? null : p.left; } /** * Return the right child of p, or <code>null</code> if p is <code>null</code>. * * @param p The entry to get right child of. * @return The right child of p, or <code>null</code> if p is <code>null</code>. */ protected static <K, V> RedBlackTree<K, V>.Entry rightOf(RedBlackTree<K, V>.Entry p) { return (p == null) ? null : p.right; } /** * Save the state of the tree's instance to a stream. * * @param s The stream to write in. * * @serialData The <i>size</i> of the RedBlackTree (the number of key-value mappings) is emitted (int), followed by * the key (Object) and value (Object) for each key-value mapping represented by the RedBlackTree. The * key-value mappings are emitted in key-order. */ private void writeObject(ObjectOutputStream s) throws IOException { // Write out the Comparator and any hidden stuff s.defaultWriteObject(); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) for (Iterator<Map.Entry<K, V>> i = entrySet().iterator(); i.hasNext();) { Map.Entry<K, V> e = i.next(); s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } /** * Reconstitute the tree's instance from a stream. * * @param s The stream to read from. */ @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the Comparator and any hidden stuff s.defaultReadObject(); // Read in size int size = s.readInt(); List<RedBlackTree<K, V>.Entry> data = new LinkedList<RedBlackTree<K, V>.Entry>(); for (int i = 0; i < size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); data.add(entryFactory.createEntry(key, value)); } buildFromSorted(size, data.iterator()); } /** * @return The default comparator to be used if no other comparator is provided. */ protected Comparator<? super K> getDefaultComparator() { return new Comparator<K>() { @SuppressWarnings("unchecked") @Override public int compare(K o1, K o2) throws ClassCastException { Comparable<K> o1c = (Comparable<K>) o1; return o1c.compareTo(o2); } }; } /** * A path to a tree's entry, the very first element is the tree's root. * * @author Martin Pecka */ protected class TreePath extends LinkedList<Entry> { /** */ private static final long serialVersionUID = -5735382496269541155L; /** * Returns true if this path ends with an entry with the given key. * * @param key The key to check. * @return true if this path ends with an entry with the given key. */ public boolean endsWithKey(K key) { if (size() == 0) return false; K k = getLast().key; return (key == null ? k == null : key.equals(k)); } /** * Return the index-th last entry from the path. Indexed from 0. If it doesn't exist, return <code>null</code>. * * @param index The index from the end of the path. * @return The index-th last entry from the path. Indexed from 0. If it doesn't exist, return <code>null</code>. */ public Entry getLast(int index) { int i = size() - index - 1; if (i < 0) return null; return get(i); } /** * Make this path lead to it's current last entry's successor. Empty the path if the current last entry is the * last entry in the tree. * * @return The successor, or <code>null</code> if it doesn't exist. */ protected Entry moveToSuccesor() { if (size() == 0) return null; if (getLast().right != null) { addLast(getLast().right); while (getLast().left != null) { addLast(getLast().left); } } else { while (size() > 0 && getLast(1) != null && getLast() == getLast(1).right) { removeLast(); } removeLast(); } return getLast(0); } /** * Make this path lead to it's current last entry's predecessor. Empty the path if the current last entry is the * first entry in the tree. * * @return The predecessor, or <code>null</code> if it doesn't exist. */ protected Entry moveToPredecessor() { if (size() == 0) return null; if (getLast().left != null) { addLast(getLast().left); while (getLast().right != null) { addLast(getLast().right); } } else { while (size() > 0 && getLast(1) != null && getLast() == getLast(1).left) { removeLast(); } removeLast(); } return getLast(0); } /** * Rotate left the last path's entry and repair the path so that it is the new path to its previous last entry. */ public void rotateLeft() { if (size() > 0) { Entry p = getLast(), q = p.right; p.right = q.left; // substitute p with q in the path removeLast(); addLast(q); if (size() == 1) root = q; else if (getLast(1).left == p) getLast(1).left = q; else getLast(1).right = q; q.left = p; addLast(p); } } /** * Rotate right the last path's entry and repair the path so that it is the new path to its previous last entry. */ public void rotateRight() { if (size() > 0) { Entry p = getLast(), q = p.left; p.left = q.right; // substitute p with q in the path removeLast(); addLast(q); if (size() == 1) root = q; else if (getLast(1).left == p) getLast(1).left = q; else getLast(1).right = q; q.right = p; addLast(p); } } } /** * Entry in the Tree. */ protected class Entry implements Map.Entry<K, V> { K key; V value; Entry left = null; Entry right = null; Color color = Color.BLACK; /** * Make a new entry with given key and value, and with <tt>null</tt> child links, and BLACK color. */ Entry(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } /** * @return The color of this entry (either Color.BLACK or Color.RED). */ public Color getColor() { return color; } /** * @return The left child. */ public Entry getLeft() { return left; } /** * @return The right child. */ public Entry getRight() { return right; } @Override public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; return valEquals(key, e.getKey()) && valEquals(value, e.getValue()); } @Override public int hashCode() { int keyHash = (key == null ? 0 : key.hashCode()); int valueHash = (value == null ? 0 : value.hashCode()); return keyHash ^ valueHash; } @Override public String toString() { return key + "=" + value; } } public String treeKeysToString() { StringBuilder result = new StringBuilder(); class EntryOrNil { RedBlackTree<K, V>.Entry entry = null; public EntryOrNil(RedBlackTree<K, V>.Entry e) { entry = e; } @Override public String toString() { if (entry == null) return "NIL"; else return (entry.color == Color.BLACK ? "(B)" : "(R)") + (entry.key == null ? "null" : entry.key.toString()); } } EntryOrNil e = new EntryOrNil(root); if (e.entry != null) { Queue<EntryOrNil> currentLevel = new LinkedList<EntryOrNil>(); Queue<EntryOrNil> nextLevel = new LinkedList<EntryOrNil>(); currentLevel.add(e); while (!currentLevel.isEmpty()) { while (!currentLevel.isEmpty()) { EntryOrNil cur = currentLevel.remove(); result.append(cur + " "); if (cur.entry == null) continue; e = new EntryOrNil(cur.entry.left); nextLevel.add(e); e = new EntryOrNil(cur.entry.right); nextLevel.add(e); } while (!nextLevel.isEmpty()) { currentLevel.add(nextLevel.remove()); } result.append("\n"); } } return result.toString(); } /** * A factory for creating new entries and tree paths. * * @author Martin Pecka */ protected class EntryFactory { /** * Create a new entry with black color and no children. * * @param key The key of the new entry. * @param value The value of the new entry. * @return The new entry. */ public Entry createEntry(K key, V value) { return new Entry(key, value); } /** * @return An empty tree path. */ public TreePath createTreePath() { return new TreePath(); } } }