| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.qpid.protonj2.engine.util; |
| |
| import java.util.AbstractCollection; |
| import java.util.AbstractMap; |
| import java.util.AbstractSet; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.ConcurrentModificationException; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.NavigableMap; |
| import java.util.NavigableSet; |
| import java.util.NoSuchElementException; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.SortedMap; |
| import java.util.SortedSet; |
| import java.util.function.BiConsumer; |
| import java.util.function.BiFunction; |
| import java.util.function.Consumer; |
| |
| import org.apache.qpid.protonj2.types.UnsignedInteger; |
| |
| /** |
| * Map class that is implemented using a Splay Tree and uses primitive integers as the keys |
| * for the specified value type. |
| * |
| * The splay tree is a specialized form of a binary search tree that is self balancing and |
| * provides faster access in general to frequently used items. The splay tree serves well |
| * as an LRU cache of sorts where 80 percent of the accessed elements comes from 20 percent |
| * of the overall load in the {@link Map}. The best case access time is generally O(long n) |
| * however it can be Theta(n) in a very worst case scenario. |
| * |
| * @param <E> The type stored in the map entries |
| */ |
| public class SplayMap<E> implements NavigableMap<UnsignedInteger, E> { |
| |
| protected static final Comparator<UnsignedInteger> COMPARATOR = new UnsignedComparator(); |
| protected static final Comparator<UnsignedInteger> REVERSE_COMPARATOR = Collections.reverseOrder(COMPARATOR); |
| |
| protected final RingQueue<SplayedEntry<E>> entryPool = new RingQueue<>(64); |
| |
| /** |
| * Root node which can be null if the tree has no elements (size == 0) |
| */ |
| protected SplayedEntry<E> root; |
| |
| /** |
| * Current size of the splayed map tree. |
| */ |
| protected int size; |
| |
| protected int modCount; |
| |
| @Override |
| public int size() { |
| return size; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return size == 0; |
| } |
| |
| /** |
| * Gets the value of the element stored in the {@link Map} with the key (treated as an |
| * unsigned integer for comparison. |
| * |
| * As a side effect of calling this method the tree that comprises the Map can be modified |
| * to bring up the found key or the last accessed key if the key given is not in the {@link Map}. |
| * For entries at the root of the tree that match the given search key the method returns |
| * immediately without modifying the {@link Map}. |
| * |
| * @param key |
| * the integer key value to search for in the {@link SplayMap}. |
| * |
| * @return the value stored for the given key if found or null if not in the {@link Map}. |
| */ |
| public E get(int key) { |
| if (root == null) { |
| return null; |
| } else if (root.key == key) { |
| return root.value; |
| } else { |
| root = splay(root, key); |
| |
| if (root.key == key) { |
| return root.value; |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * Gets the value of the element stored in the {@link Map} with the key (treated as an |
| * unsigned integer for comparison. |
| * |
| * As a side effect of calling this method the tree that comprises the Map can be modified |
| * to bring up the found key or the last accessed key if the key given is not in the {@link Map}. |
| * For entries at the root of the tree that match the given search key the method returns |
| * immediately without modifying the {@link Map}. |
| * |
| * @param key |
| * the integer key value to search for in the {@link SplayMap}. |
| * @param defaultValue |
| * the default value to return if the key is not stored in this {@link Map}. |
| * |
| * @return the value stored for the given key if found the default value if not in the {@link Map}. |
| */ |
| public E getOrDefault(int key, E defaultValue) { |
| E result = get(key); |
| if (result == null && root != null && root.key == key) { |
| return null; |
| } |
| |
| return defaultValue; |
| } |
| |
| /** |
| * Puts the value into the in the {@link Map} at the entry specified by the given key (treated as an |
| * unsigned integer for comparison. |
| * |
| * As a side effect of calling this method the tree that comprises the Map can be modified |
| * to bring up the found key or the last accessed key if the key given is not in the {@link Map}. |
| * For entries at the root of the tree that match the given search key the method returns |
| * immediately without modifying the {@link Map}. |
| * |
| * @param key |
| * the integer key value to search for and or insert in the {@link SplayMap}. |
| * @param value |
| * the value to assign to the entry accessed via the given key. |
| * |
| * @return the previous value stored for the given key if found or null if not in the {@link Map}. |
| */ |
| public E put(int key, E value) { |
| E oldValue = null; |
| |
| if (root == null) { |
| root = entryPool.poll(SplayMap::createEntry).initialize(key, value); |
| } else { |
| root = splay(root, key); |
| if (root.key == key) { |
| oldValue = root.value; |
| root.value = value; |
| } else { |
| final SplayedEntry<E> node = entryPool.poll(SplayMap::createEntry).initialize(key, value); |
| |
| if (compare(key, root.key) < 0) { |
| shiftRootRightOf(node); |
| } else { |
| shiftRootLeftOf(node); |
| } |
| } |
| } |
| |
| if (oldValue == null) { |
| entryAdded(root); |
| size++; |
| } |
| modCount++; |
| |
| return oldValue; |
| } |
| |
| /** |
| * If the specified key is not already associated with a value associates it with the given value and |
| * returns null, otherwise returns the current value. |
| * |
| * As a side effect of calling this method the tree that comprises the Map can be modified |
| * to bring up the found key or the last accessed key if the key given is not in the {@link Map}. |
| * For entries at the root of the tree that match the given search key the method returns |
| * immediately without modifying the {@link Map}. |
| * |
| * @param key |
| * the integer key value to search for and or insert in the {@link SplayMap}. |
| * @param value |
| * the value to assign to the entry accessed via the given key. |
| * |
| * @return the previous value associated with the given key or null if none was present. |
| */ |
| public E putIfAbsent(int key, E value) { |
| if (root == null) { |
| root = entryPool.poll(SplayMap::createEntry).initialize(key, value); |
| } else { |
| root = splay(root, key); |
| if (root.key == key) { |
| return root.value; |
| } else { |
| final SplayedEntry<E> node = entryPool.poll(SplayMap::createEntry).initialize(key, value); |
| |
| if (compare(key, root.key) < 0) { |
| shiftRootRightOf(node); |
| } else { |
| shiftRootLeftOf(node); |
| } |
| } |
| } |
| |
| entryAdded(root); |
| size++; |
| modCount++; |
| |
| return null; |
| } |
| |
| private void shiftRootRightOf(SplayedEntry<E> newRoot) { |
| newRoot.right = root; |
| newRoot.left = root.left; |
| if (newRoot.left != null) { |
| newRoot.left.parent = newRoot; |
| } |
| root.left = null; |
| root.parent = newRoot; |
| root = newRoot; |
| } |
| |
| private void shiftRootLeftOf(SplayedEntry<E> newRoot) { |
| newRoot.left = root; |
| newRoot.right = root.right; |
| if (newRoot.right != null) { |
| newRoot.right.parent = newRoot; |
| } |
| root.right = null; |
| root.parent = newRoot; |
| root = newRoot; |
| } |
| |
| /** |
| * Removes the mapping for the {@link UnsignedInteger} key from this map if it is present |
| * and returns the value to which this map previously associated the key, or null if the |
| * map contained no mapping for the key. |
| * |
| * @param key |
| * The {@link UnsignedInteger} key whose value will be removed from the {@link SplayMap}. |
| * |
| * @return the value that was removed if one was present in the {@link Map}. |
| */ |
| public E remove(UnsignedInteger key) { |
| return remove(key.intValue()); |
| } |
| |
| /** |
| * Removes the mapping for the primitive <code>int</code> key from this map if it is present |
| * and returns the value to which this map previously associated the key, or null if the |
| * map contained no mapping for the key. The integer value is treated as an unsigned int |
| * internally. |
| * |
| * @param key |
| * The {@link UnsignedInteger} key whose value will be removed from the {@link SplayMap}. |
| * |
| * @return the value that was removed if one was present in the {@link Map}. |
| */ |
| public E remove(int key) { |
| if (root == null) { |
| return null; |
| } |
| |
| root = splay(root, key); |
| if (root.key != key) { |
| return null; |
| } |
| |
| final E removed = root.value; |
| |
| // We splayed on the key and matched it so the root |
| // will now be the node matching that key. |
| delete(root); |
| |
| return removed; |
| } |
| |
| /** |
| * Searches the map using the given primitive integer key value which will be treated |
| * internally as an unsigned value when comparing against keys in the mapping. |
| * |
| * @param key |
| * The key which will be searched for in this mapping. |
| * |
| * @return <code>true</code> if the key mapping is found within this {@link Map}. |
| */ |
| public boolean containsKey(int key) { |
| if (root == null) { |
| return false; |
| } |
| |
| root = splay(root, key); |
| if (root.key == key) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //----- Map interface implementation |
| |
| @Override |
| public E put(UnsignedInteger key, E value) { |
| return put(key.intValue(), value); |
| } |
| |
| @Override |
| public E putIfAbsent(UnsignedInteger key, E value) { |
| return putIfAbsent(key.intValue(), value); |
| } |
| |
| @Override |
| public E get(Object key) { |
| return get(Number.class.cast(key).intValue()); |
| } |
| |
| @Override |
| public E getOrDefault(Object key, E defaultValue) { |
| return getOrDefault(Number.class.cast(key).intValue(), defaultValue); |
| } |
| |
| @Override |
| public E remove(Object key) { |
| return remove(Number.class.cast(key).intValue()); |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| Number numericKey = (Number) key; |
| return containsKey(numericKey.intValue()); |
| } |
| |
| @Override |
| public void clear() { |
| root = null; |
| size = 0; |
| } |
| |
| @Override |
| public void putAll(Map<? extends UnsignedInteger, ? extends E> source) { |
| for (Entry<? extends UnsignedInteger, ? extends E> entry : source.entrySet()) { |
| put(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| if (entry.valueEquals(value)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int hash = 0; |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| hash += entry.hashCode(); |
| } |
| return hash; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof Map)) { |
| return false; |
| } |
| |
| Map<?,?> m = (Map<?,?>) o; |
| if (m.size() != size()) { |
| return false; |
| } |
| |
| try { |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| UnsignedInteger key = entry.getKey(); |
| E value = entry.getValue(); |
| if (value == null) { |
| if (!(m.get(key) == null && m.containsKey(key))) { |
| return false; |
| } |
| } else { |
| if (!value.equals(m.get(key))) { |
| return false; |
| } |
| } |
| } |
| } catch (ClassCastException | NullPointerException ignored) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Once requested we will create an store a single instance to a collection |
| // with no state for each of the key, values ,entries types and the descending |
| // Map view. Since the types are stateless the trivial race on create is not |
| // important to the eventual outcome of having a cached instance. |
| |
| protected NavigableSet<UnsignedInteger> keySet; |
| protected Collection<E> values; |
| protected Set<Entry<UnsignedInteger, E>> entrySet; |
| protected NavigableMap<UnsignedInteger, E> descendingMapView; |
| |
| @Override |
| public Set<UnsignedInteger> keySet() { |
| return navigableKeySet(); |
| } |
| |
| @Override |
| public Collection<E> values() { |
| if (values == null) { |
| values = new SplayMapValues(); |
| } |
| return values; |
| } |
| |
| @Override |
| public Set<Entry<UnsignedInteger, E>> entrySet() { |
| if (entrySet == null) { |
| entrySet = new SplayMapEntrySet(); |
| } |
| return entrySet; |
| } |
| |
| @Override |
| public void forEach(BiConsumer<? super UnsignedInteger, ? super E> action) { |
| Objects.requireNonNull(action); |
| |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| action.accept(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| /** |
| * A specialized forEach implementation that accepts a {@link Consumer} function that will |
| * be called for each value in the {@link SplayMap}. This method can save overhead as it does not |
| * need to box the primitive key values into an object for the call to the provided function. |
| * Unless otherwise specified by the implementing class, actions are performed in the order of entry |
| * set iteration (if an iteration order is specified.) |
| * |
| * @param action |
| * The action to be performed for each of the values in the {@link SplayMap}. |
| */ |
| public void forEach(Consumer<? super E> action) { |
| Objects.requireNonNull(action); |
| |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| action.accept(entry.getValue()); |
| } |
| } |
| |
| @Override |
| public void replaceAll(BiFunction<? super UnsignedInteger, ? super E, ? extends E> function) { |
| Objects.requireNonNull(function, "The replacement function parameter cannot be null"); |
| |
| final int initialModCount = modCount; |
| |
| for (SplayedEntry<E> entry = firstEntry(root); entry != null; entry = successor(entry)) { |
| entry.value = function.apply(entry.getKey(), entry.value); |
| } |
| |
| if (modCount != initialModCount) { |
| throw new ConcurrentModificationException(); |
| } |
| } |
| |
| @Override |
| public boolean remove(Object key, Object value) { |
| Number numericKey = (Number) key; |
| return remove(numericKey.intValue(), value); |
| } |
| |
| /** |
| * Removes the entry for the specified primitive int (treated as unsigned) key only if it is |
| * currently mapped to the specified value in the {@link Map}. |
| * |
| * @param key |
| * The key whose value will be removed if matched. |
| * @param value |
| * The value that must be contained in the mapping for the remove to be performed. |
| * |
| * @return <code>true</code> if an entry was removed from the {@link Map} |
| */ |
| public boolean remove(int key, Object value) { |
| root = splay(root, key); |
| if (root == null || root.key != key || !Objects.equals(root.value, value)) { |
| return false; |
| } else { |
| delete(root); |
| return true; |
| } |
| } |
| |
| @Override |
| public boolean replace(UnsignedInteger key, E oldValue, E newValue) { |
| return replace(key.intValue(), oldValue, newValue); |
| } |
| |
| /** |
| * Replaces the entry for the specified primitive int (treated as unsigned) key only if it is |
| * currently mapped to the specified value in the {@link Map} with the new value provided. |
| * |
| * @param key |
| * The key whose value will be removed if matched. |
| * @param oldValue |
| * The old value that must be contained in the mapping for the replace to be performed. |
| * @param newValue |
| * The value that will replace the old value mapped to the given key if one existed.. |
| * |
| * @return <code>true</code> if an entry was replaced in the {@link Map} |
| */ |
| public boolean replace(int key, E oldValue, E newValue) { |
| root = splay(root, key); |
| if (root == null || root.key != key || !Objects.equals(root.value, oldValue)) { |
| return false; |
| } else { |
| root.setValue(newValue); |
| return true; |
| } |
| } |
| |
| @Override |
| public E replace(UnsignedInteger key, E value) { |
| return replace(key.intValue(), value); |
| } |
| |
| /** |
| * Replaces the entry for the specified primitive int (treated as unsigned) key only if it is |
| * currently mapped to the a value in the {@link Map} with the new value provided. |
| * |
| * @param key |
| * The key whose value will be removed if matched. |
| * @param value |
| * The value that will replace the old value mapped to the given key if one existed.. |
| * |
| * @return <code>true</code> if an entry was replaced in the {@link Map} |
| */ |
| public E replace(int key, E value) { |
| root = splay(root, key); |
| if (root == null || root.key != key || root.value == null) { |
| return null; |
| } else { |
| return root.setValue(value); |
| } |
| } |
| |
| //----- Extension points |
| |
| protected void entryAdded(SplayedEntry<E> newEntry) { |
| // Nothing to do in the base class implementation. |
| } |
| |
| protected void entryDeleted(SplayedEntry<E> deletedEntry) { |
| // Nothing to do in the base class implementation. |
| } |
| |
| //----- Internal Implementation |
| |
| /* |
| * Rotations of tree elements form the basis of search and balance operations |
| * within the tree during put, get and remove type operations. |
| * |
| * y x |
| * / \ Zig (Right Rotation) / \ |
| * x T3 – - – - – - – - - -> T1 y |
| * / \ < - - - - - - - - - / \ |
| * T1 T2 Zag (Left Rotation) T2 T3 |
| * |
| */ |
| |
| private static <E> SplayedEntry<E> rightRotate(SplayedEntry<E> node) { |
| SplayedEntry<E> rotated = node.left; |
| node.left = rotated.right; |
| rotated.right = node; |
| |
| // Reset the parent values for adjusted nodes. |
| rotated.parent = node.parent; |
| node.parent = rotated; |
| if (node.left != null) { |
| node.left.parent = node; |
| } |
| |
| return rotated; |
| } |
| |
| private static <E> SplayedEntry<E> leftRotate(SplayedEntry<E> node) { |
| SplayedEntry<E> rotated = node.right; |
| node.right = rotated.left; |
| rotated.left = node; |
| |
| // Reset the parent values for adjusted nodes. |
| rotated.parent = node.parent; |
| node.parent = rotated; |
| if (node.right != null) { |
| node.right.parent = node; |
| } |
| |
| return rotated; |
| } |
| |
| /* |
| * The requested key if present is brought to the root of the tree. If it is not |
| * present then the last accessed element (nearest match) will be brought to the root |
| * as it is likely it will be the next accessed or one of the neighboring nodes which |
| * reduces the search time for that cluster. |
| */ |
| private static <E> SplayedEntry<E> splay(SplayedEntry<E> root, int key) { |
| if (root == null || root.key == key) { |
| return root; |
| } |
| |
| SplayedEntry<E> lessThanKeyRoot = null; |
| SplayedEntry<E> lessThanKeyNode = null; |
| SplayedEntry<E> greaterThanKeyRoot = null; |
| SplayedEntry<E> greaterThanKeyNode = null; |
| |
| while (true) { |
| if (compare(key, root.key) < 0) { |
| // Entry must be to the left of the current node so we bring that up |
| // and then work from there to see if we can find the key |
| if (root.left != null && compare(key, root.left.key) < 0) { |
| root = rightRotate(root); |
| } |
| |
| // Is there nowhere else to go, if so we are done. |
| if (root.left == null) { |
| break; |
| } |
| |
| // Haven't found it yet but we now know the current element is greater |
| // than the element we are looking for so it goes to the right tree. |
| if (greaterThanKeyRoot == null) { |
| greaterThanKeyRoot = greaterThanKeyNode = root; |
| } else { |
| greaterThanKeyNode.left = root; |
| greaterThanKeyNode.left.parent = greaterThanKeyNode; |
| greaterThanKeyNode = root; |
| } |
| |
| root = root.left; |
| root.parent = null; |
| } else if (compare(key, root.key) > 0) { |
| // Entry must be to the right of the current node so we bring that up |
| // and then work from there to see if we can find the key |
| if (root.right != null && compare(key, root.right.key) > 0) { |
| root = leftRotate(root); |
| } |
| |
| // Is there nowhere else to go, if so we are done. |
| if (root.right == null) { |
| break; |
| } |
| |
| // Haven't found it yet but we now know the current element is less |
| // than the element we are looking for so it goes to the left tree. |
| if (lessThanKeyRoot == null) { |
| lessThanKeyRoot = lessThanKeyNode = root; |
| } else { |
| lessThanKeyNode.right = root; |
| lessThanKeyNode.right.parent = lessThanKeyNode; |
| lessThanKeyNode = root; |
| } |
| |
| root = root.right; |
| root.parent = null; |
| } else { |
| break; // Found it |
| } |
| } |
| |
| // Reassemble the tree from the left, right and middle the assembled nodes in the |
| // left and right should have their last element either nulled out or linked to the |
| // remaining items middle tree |
| if (lessThanKeyRoot == null) { |
| lessThanKeyRoot = root.left; |
| } else { |
| lessThanKeyNode.right = root.left; |
| if (lessThanKeyNode.right != null) { |
| lessThanKeyNode.right.parent = lessThanKeyNode; |
| } |
| } |
| |
| if (greaterThanKeyRoot == null) { |
| greaterThanKeyRoot = root.right; |
| } else { |
| greaterThanKeyNode.left = root.right; |
| if (greaterThanKeyNode.left != null) { |
| greaterThanKeyNode.left.parent = greaterThanKeyNode; |
| } |
| } |
| |
| // The found or last accessed element is now rooted to the splayed |
| // left and right trees and returned as the new tree. |
| root.left = lessThanKeyRoot; |
| if (root.left != null) { |
| root.left.parent = root; |
| } |
| root.right = greaterThanKeyRoot; |
| if (root.right != null) { |
| root.right.parent = root; |
| } |
| |
| return root; |
| } |
| |
| protected void delete(SplayedEntry<E> node) { |
| final SplayedEntry<E> grandparent = node.parent; |
| SplayedEntry<E> replacement = node.right; |
| |
| if (node.left != null) { |
| replacement = splay(node.left, node.key); |
| replacement.right = node.right; |
| if (replacement.right != null) { |
| replacement.right.parent = replacement; |
| } |
| } |
| |
| if (replacement != null) { |
| replacement.parent = grandparent; |
| } |
| |
| if (grandparent != null) { |
| if (grandparent.left == node) { |
| grandparent.left = replacement; |
| } else { |
| grandparent.right = replacement; |
| } |
| } else { |
| root = replacement; |
| } |
| |
| // Clear node before moving to cache |
| node.left = node.right = node.parent = null; |
| entryPool.offer(node); |
| |
| entryDeleted(node); |
| |
| size--; |
| modCount++; |
| } |
| |
| private SplayedEntry<E> findEntry(int key) { |
| if (root == null) { |
| return null; |
| } else if (root.key == key) { |
| return root; |
| } else { |
| root = splay(root, key); |
| |
| if (root.key == key) { |
| return root; |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| private static <E> SplayedEntry<E> firstEntry(SplayedEntry<E> node) { |
| SplayedEntry<E> firstEntry = node; |
| if (firstEntry != null) { |
| while (firstEntry.left != null) { |
| firstEntry = firstEntry.left; |
| } |
| } |
| |
| return firstEntry; |
| } |
| |
| private static <E> SplayedEntry<E> lastEntry(SplayedEntry<E> node) { |
| SplayedEntry<E> lastEntry = node; |
| if (lastEntry != null) { |
| while (lastEntry.right != null) { |
| lastEntry = lastEntry.right; |
| } |
| } |
| |
| return lastEntry; |
| } |
| |
| private static <E> SplayedEntry<E> successor(SplayedEntry<E> node) { |
| if (node == null) { |
| return null; |
| } else if (node.right != null) { |
| // Walk to bottom of tree from this node's right child. |
| SplayedEntry<E> result = node.right; |
| while (result.left != null) { |
| result = result.left; |
| } |
| |
| return result; |
| } else { |
| SplayedEntry<E> parent = node.parent; |
| SplayedEntry<E> child = node; |
| while (parent != null && child == parent.right) { |
| child = parent; |
| parent = parent.parent; |
| } |
| |
| return parent; |
| } |
| } |
| |
| private static <E> SplayedEntry<E> predecessor(SplayedEntry<E> node) { |
| if (node == null) { |
| return null; |
| } else if (node.left != null) { |
| // Walk to bottom of tree from this node's left child. |
| SplayedEntry<E> result = node.left; |
| while (result.right != null) { |
| result = result.right; |
| } |
| |
| return result; |
| } else { |
| SplayedEntry<E> parent = node.parent; |
| SplayedEntry<E> child = node; |
| while (parent != null && child == parent.left) { |
| child = parent; |
| parent = parent.parent; |
| } |
| |
| return parent; |
| } |
| } |
| |
| /** |
| * Unsigned comparison of two integer values |
| * |
| * @param lhs |
| * the left hand side value for comparison. |
| * @param rhs |
| * the right hand side value for comparison. |
| * |
| * @return a negative integer, zero, or a positive integer as the first argument is less than, |
| * equal to, or greater than the second. |
| */ |
| private static int compare(int lhs, int rhs) { |
| return Integer.compareUnsigned(lhs, rhs); |
| } |
| |
| private static <E> SplayedEntry<E> createEntry() { |
| return new SplayedEntry<>(); |
| } |
| |
| //----- Map Iterator implementation for EntrySet, KeySet and Values collections |
| |
| // Base class iterator that can be used for the collections returned from the Map |
| private abstract class SplayMapIterator<T> implements Iterator<T> { |
| |
| protected SplayedEntry<E> nextNode; |
| protected SplayedEntry<E> lastReturned; |
| |
| protected int expectedModCount; |
| |
| public SplayMapIterator(SplayedEntry<E> startAt) { |
| this.nextNode = startAt; |
| this.expectedModCount = SplayMap.this.modCount; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return nextNode != null; |
| } |
| |
| protected SplayedEntry<E> nextNode() { |
| final SplayedEntry<E> entry = nextNode; |
| |
| if (nextNode == null) { |
| throw new NoSuchElementException(); |
| } |
| if (expectedModCount != SplayMap.this.modCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| nextNode = successor(nextNode); |
| lastReturned = entry; |
| |
| return lastReturned; |
| } |
| |
| protected SplayedEntry<E> previousNode() { |
| final SplayedEntry<E> entry = nextNode; |
| |
| if (nextNode == null) { |
| throw new NoSuchElementException(); |
| } |
| if (expectedModCount != SplayMap.this.modCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| nextNode = predecessor(nextNode); |
| lastReturned = entry; |
| |
| return lastReturned; |
| } |
| |
| @Override |
| public void remove() { |
| if (lastReturned == null) { |
| throw new IllegalStateException(); |
| } |
| if (modCount != expectedModCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| delete(lastReturned); |
| |
| expectedModCount = modCount; |
| lastReturned = null; |
| } |
| } |
| |
| private class SplayMapEntryIterator extends SplayMapIterator<Entry<UnsignedInteger, E>> { |
| |
| public SplayMapEntryIterator(SplayedEntry<E> startAt) { |
| super(startAt); |
| } |
| |
| @Override |
| public Entry<UnsignedInteger, E> next() { |
| return nextNode(); |
| } |
| } |
| |
| private class SplayMapKeyIterator extends SplayMapIterator<UnsignedInteger> { |
| |
| public SplayMapKeyIterator(SplayedEntry<E> startAt) { |
| super(startAt); |
| } |
| |
| @Override |
| public UnsignedInteger next() { |
| return nextNode().getKey(); |
| } |
| } |
| |
| final class ReverseSplayMapKeyIterator extends SplayMapIterator<UnsignedInteger> { |
| public ReverseSplayMapKeyIterator(SplayedEntry<E> startAt) { |
| super(startAt); |
| } |
| |
| @Override |
| public UnsignedInteger next() { |
| return previousNode().getKey(); |
| } |
| } |
| |
| private class SplayMapValueIterator extends SplayMapIterator<E> { |
| |
| public SplayMapValueIterator(SplayedEntry<E> startAt) { |
| super(startAt); |
| } |
| |
| @Override |
| public E next() { |
| return nextNode().getValue(); |
| } |
| } |
| |
| //----- Splay Map Collection types |
| |
| private final class SplayMapValues extends AbstractCollection<E> { |
| |
| @Override |
| public Iterator<E> iterator() { |
| return new SplayMapValueIterator(firstEntry(root)); |
| } |
| |
| @Override |
| public int size() { |
| return SplayMap.this.size; |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| return SplayMap.this.containsValue(o); |
| } |
| |
| @Override |
| public boolean remove(Object target) { |
| for (SplayedEntry<E> e = firstEntry(root); e != null; e = successor(e)) { |
| if (e.valueEquals(target)) { |
| delete(e); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void clear() { |
| SplayMap.this.clear(); |
| } |
| } |
| |
| protected class SplayMapKeySet extends AbstractSet<UnsignedInteger> implements NavigableSet<UnsignedInteger> { |
| |
| @Override |
| public Iterator<UnsignedInteger> iterator() { |
| return new SplayMapKeyIterator(firstEntry(root)); |
| } |
| |
| @Override |
| public int size() { |
| return SplayMap.this.size; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return SplayMap.this.size == 0; |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| return SplayMap.this.containsKey(o); |
| } |
| |
| @Override |
| public boolean remove(Object target) { |
| for (SplayedEntry<E> e = firstEntry(root); e != null; e = successor(e)) { |
| if (e.keyEquals(target)) { |
| delete(e); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean retainAll(Collection<?> c) { |
| Objects.requireNonNull(c); |
| boolean modified = false; |
| |
| for (SplayedEntry<E> e = firstEntry(root); e != null;) { |
| if (c.contains(e.getKey())) { |
| e = successor(e); |
| } else { |
| final SplayedEntry<E> target = e; |
| e = successor(e); |
| |
| delete(target); |
| modified = true; |
| } |
| } |
| |
| return modified; |
| } |
| |
| @Override |
| public Object[] toArray() { |
| Object[] result = new Object[size()]; |
| int i = 0; |
| |
| for (SplayedEntry<E> e = firstEntry(root); e != null; e = successor(e), ++i) { |
| result[i] = e.getKey(); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public void clear() { |
| SplayMap.this.clear(); |
| } |
| |
| @Override |
| public Comparator<? super UnsignedInteger> comparator() { |
| return SplayMap.COMPARATOR; |
| } |
| |
| @Override |
| public UnsignedInteger first() { |
| return firstKey(); |
| } |
| |
| @Override |
| public UnsignedInteger last() { |
| return lastKey(); |
| } |
| |
| @Override |
| public UnsignedInteger lower(UnsignedInteger key) { |
| return lowerKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger floor(UnsignedInteger key) { |
| return floorKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger ceiling(UnsignedInteger key) { |
| return ceilingKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger higher(UnsignedInteger key) { |
| return higherKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger pollFirst() { |
| Map.Entry<UnsignedInteger, ?> first = pollFirstEntry(); |
| return first == null ? null : first.getKey(); |
| } |
| |
| @Override |
| public UnsignedInteger pollLast() { |
| Map.Entry<UnsignedInteger, ?> first = pollLastEntry(); |
| return first == null ? null : first.getKey(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> descendingSet() { |
| return descendingMap().navigableKeySet(); |
| } |
| |
| @Override |
| public Iterator<UnsignedInteger> descendingIterator() { |
| return descendingMap().keySet().iterator(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> subSet(UnsignedInteger fromElement, boolean fromInclusive, UnsignedInteger toElement, boolean toInclusive) { |
| return new AscendingSubMap<>(SplayMap.this, |
| false, fromElement.intValue(), fromInclusive, |
| false, toElement.intValue(), toInclusive).navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> headSet(UnsignedInteger toElement, boolean inclusive) { |
| return new AscendingSubMap<>(SplayMap.this, |
| true, 0, true, false, toElement.intValue(), inclusive).navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> tailSet(UnsignedInteger fromElement, boolean inclusive) { |
| return new AscendingSubMap<>(SplayMap.this, |
| false, fromElement.intValue(), inclusive, true, 0, true).navigableKeySet(); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> subSet(UnsignedInteger fromElement, UnsignedInteger toElement) { |
| return subSet(fromElement, true, toElement, true); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> headSet(UnsignedInteger toElement) { |
| return headSet(toElement, false); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> tailSet(UnsignedInteger fromElement) { |
| return tailSet(fromElement, false); |
| } |
| } |
| |
| private final class SplayMapEntrySet extends AbstractSet<Entry<UnsignedInteger, E>> { |
| |
| @Override |
| public Iterator<Entry<UnsignedInteger, E>> iterator() { |
| return new SplayMapEntryIterator(firstEntry(root)); |
| } |
| |
| @Override |
| public int size() { |
| return SplayMap.this.size; |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| if ((o instanceof Map.Entry) && SplayMap.this.root != null) { |
| for (SplayedEntry<E> e = firstEntry(root); e != null; e = successor(e)) { |
| if (e.equals(o)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public boolean remove(Object target) { |
| if (!(target instanceof Entry)) { |
| throw new IllegalArgumentException("value provided is not an Entry type."); |
| } |
| |
| for (SplayedEntry<E> e = firstEntry(root); e != null; e = successor(e)) { |
| if (e.equals(target)) { |
| delete(e); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void clear() { |
| SplayMap.this.clear(); |
| } |
| } |
| |
| //----- Map Entry node for the Splay Map |
| |
| protected static final class SplayedEntry<E> implements Map.Entry<UnsignedInteger, E>{ |
| |
| SplayedEntry<E> left; |
| SplayedEntry<E> right; |
| SplayedEntry<E> parent; |
| |
| int key; |
| E value; |
| |
| // Insertion order chain used by LinkedSplayMap |
| SplayedEntry<E> linkNext; |
| SplayedEntry<E> linkPrev; |
| |
| public SplayedEntry() { |
| initialize(key, value); |
| } |
| |
| public SplayedEntry<E> initialize(int key, E value) { |
| this.key = key; |
| this.value = value; |
| // Node is circular list to start. |
| this.linkNext = this; |
| this.linkPrev = this; |
| |
| return this; |
| } |
| |
| public int getIntKey() { |
| return key; |
| } |
| |
| @Override |
| public UnsignedInteger getKey() { |
| return UnsignedInteger.valueOf(key); |
| } |
| |
| @Override |
| public E getValue() { |
| return value; |
| } |
| |
| @Override |
| public E setValue(E value) { |
| E oldValue = this.value; |
| this.value = value; |
| return oldValue; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof Map.Entry)) { |
| return false; |
| } |
| |
| Map.Entry<?,?> e = (Map.Entry<?,?>)o; |
| |
| return keyEquals(e.getKey()) && valueEquals(e.getValue()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return key ^ (value == null ? 0 : value.hashCode()); |
| } |
| |
| @Override |
| public String toString() { |
| return "Node:{" + key + "," + value + "}"; |
| } |
| |
| boolean keyEquals(Object other) { |
| if (!(other instanceof Number)) { |
| return false; |
| } |
| |
| return key == ((Number) other).intValue(); |
| } |
| |
| boolean valueEquals(Object other) { |
| return value != null ? value.equals(other) : other == null; |
| } |
| } |
| |
| /** |
| * An immutable {@link Map} entry that can be used when exposing raw entry mappings |
| * via the {@link Map} API. |
| */ |
| public static class ImmutableSplayMapEntry<E> implements Map.Entry<UnsignedInteger, E> { |
| |
| private final SplayedEntry<E> entry; |
| |
| /** |
| * Create a new immutable {@link Map} entry. |
| * |
| * @param entry |
| * The inner {@link Map} entry that is wrapped. |
| */ |
| public ImmutableSplayMapEntry(SplayedEntry<E> entry) { |
| this.entry = entry; |
| } |
| |
| @Override |
| public UnsignedInteger getKey() { |
| return entry.getKey(); |
| } |
| |
| /** |
| * @return the primitive integer view of the unsigned key. |
| */ |
| public int getPrimitiveKey() { |
| return entry.getIntKey(); |
| } |
| |
| @Override |
| public E getValue() { |
| return entry.getValue(); |
| } |
| |
| @Override |
| public E setValue(E value) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| protected static <V> ImmutableSplayMapEntry<V> export(SplayedEntry<V> entry) { |
| return entry == null ? null : new ImmutableSplayMapEntry<>(entry); |
| } |
| |
| //----- Unsigned Integer comparator for Navigable Maps |
| |
| private static final class UnsignedComparator implements Comparator<UnsignedInteger> { |
| |
| @Override |
| public int compare(UnsignedInteger uint1, UnsignedInteger uint2) { |
| return uint1.compareTo(uint2); |
| } |
| } |
| |
| protected static Comparator<? super UnsignedInteger> reverseComparator() { |
| return REVERSE_COMPARATOR; |
| } |
| |
| //----- Navigable and Sorted Map implementation methods |
| |
| @Override |
| public Comparator<? super UnsignedInteger> comparator() { |
| return COMPARATOR; |
| } |
| |
| @Override |
| public UnsignedInteger firstKey() { |
| return isEmpty() ? null : firstEntry(root).getKey(); |
| } |
| |
| @Override |
| public UnsignedInteger lastKey() { |
| return isEmpty() ? null : lastEntry(root).getKey(); |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> firstEntry() { |
| return export(firstEntry(root)); |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> lastEntry() { |
| return export(lastEntry(root)); |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> pollFirstEntry() { |
| SplayedEntry<E> firstEntry = firstEntry(root); |
| if (firstEntry != null) { |
| delete(firstEntry); |
| } |
| return export(firstEntry); |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> pollLastEntry() { |
| SplayedEntry<E> lastEntry = lastEntry(root); |
| if (lastEntry != null) { |
| delete(lastEntry); |
| } |
| return export(lastEntry); |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> lowerEntry(UnsignedInteger key) { |
| return export(splayToLowerEntry(key.intValue())); |
| } |
| |
| @Override |
| public UnsignedInteger lowerKey(UnsignedInteger key) { |
| final SplayedEntry<E> result = splayToLowerEntry(key.intValue()); |
| |
| return result == null ? null : result.getKey(); |
| } |
| |
| /** |
| * Splay to a key-value mapping associated with the greatest key strictly less than the given |
| * key, or null if there is no such key. |
| * |
| * @param key |
| * The key whose next lower entry match is being queried. |
| * |
| * @return the next lower entry or null if no keys lower than the given value. |
| */ |
| private SplayedEntry<E> splayToLowerEntry(int key) { |
| root = splay(root, key); |
| |
| while (root != null) { |
| if (compare(root.getIntKey(), key) >= 0) { |
| SplayedEntry<E> pred = predecessor(root); |
| if (pred != null) { |
| root = splay(root, pred.key); |
| } else { |
| return null; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| return root; |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> higherEntry(UnsignedInteger key) { |
| return export(splayToHigherEntry(key.intValue())); |
| } |
| |
| @Override |
| public UnsignedInteger higherKey(UnsignedInteger key) { |
| final SplayedEntry<E> result = splayToHigherEntry(key.intValue()); |
| |
| return result == null ? null : result.getKey(); |
| } |
| |
| /** |
| * Splay to a key-value mapping associated with the least key strictly greater than the given |
| * key, or null if there is no such key. |
| * |
| * @param key |
| * The key whose next higher entry match is being queried. |
| * |
| * @return the next highest entry or null if no keys higher than the given value. |
| */ |
| private SplayedEntry<E> splayToHigherEntry(int key) { |
| root = splay(root, key); |
| |
| while (root != null) { |
| if (compare(root.getIntKey(), key) <= 0) { |
| SplayedEntry<E> succ = successor(root); |
| if (succ != null) { |
| root = splay(root, succ.key); |
| } else { |
| return null; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| return root; |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> floorEntry(UnsignedInteger key) { |
| return export(splayToFloorEntry(key.intValue())); |
| } |
| |
| @Override |
| public UnsignedInteger floorKey(UnsignedInteger key) { |
| final SplayedEntry<E> result = splayToFloorEntry(key.intValue()); |
| |
| return result == null ? null : result.getKey(); |
| } |
| |
| /** |
| * Splay to a key-value mapping associated with the greatest key less than or equal to |
| * the given key, or null if there is no such key. |
| * |
| * @param key |
| * The key whose floor entry match is being queried. |
| * |
| * @return the entry or next lowest entry or null if no keys less then or equal to the given value. |
| */ |
| private SplayedEntry<E> splayToFloorEntry(int key) { |
| root = splay(root, key); |
| |
| while (root != null) { |
| if (compare(root.getIntKey(), key) > 0) { |
| SplayedEntry<E> pred = predecessor(root); |
| if (pred != null) { |
| root = splay(root, pred.key); |
| } else { |
| return null; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| return root; |
| } |
| |
| @Override |
| public ImmutableSplayMapEntry<E> ceilingEntry(UnsignedInteger key) { |
| return export(splayToCeilingEntry(key.intValue())); |
| } |
| |
| @Override |
| public UnsignedInteger ceilingKey(UnsignedInteger key) { |
| final SplayedEntry<E> result = splayToCeilingEntry(key.intValue()); |
| |
| return result == null ? null : result.getKey(); |
| } |
| |
| /** |
| * Splay to a key-value mapping associated with the least key greater than or equal |
| * to the given key, or null if there is no such key. |
| * |
| * @param key |
| * The key whose ceiling entry match is being queried. |
| * |
| * @return the entry or next highest entry or null if no keys higher than or equal to the given value. |
| */ |
| private SplayedEntry<E> splayToCeilingEntry(int key) { |
| root = splay(root, key); |
| |
| while (root != null) { |
| if (compare(root.getIntKey(), key) < 0) { |
| SplayedEntry<E> succ = successor(root); |
| if (succ != null) { |
| root = splay(root, succ.key); |
| } else { |
| return null; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| return root; |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, E> descendingMap() { |
| return (descendingMapView != null) ? descendingMapView : |
| (descendingMapView = new DescendingSubMap<>(this, |
| true, 0, true, |
| true, UnsignedInteger.MAX_VALUE.intValue(), true)); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> navigableKeySet() { |
| if (keySet == null) { |
| keySet = new SplayMapKeySet(); |
| } |
| return keySet; |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> descendingKeySet() { |
| return descendingMap().navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, E> subMap(UnsignedInteger fromKey, boolean fromInclusive, UnsignedInteger toKey, boolean toInclusive) { |
| return new AscendingSubMap<>(this, false, fromKey.intValue(), fromInclusive, false, toKey.intValue(), toInclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, E> headMap(UnsignedInteger toKey, boolean inclusive) { |
| return new AscendingSubMap<>(this, true, 0, true, false, toKey.intValue(), inclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, E> tailMap(UnsignedInteger fromKey, boolean inclusive) { |
| return new AscendingSubMap<>(this, false, fromKey.intValue(), inclusive, true, UnsignedInteger.MAX_VALUE.intValue(), true); |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> subMap(UnsignedInteger fromKey, UnsignedInteger toKey) { |
| return subMap(fromKey, true, toKey, false); |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> headMap(UnsignedInteger toKey) { |
| return headMap(toKey, false); |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> tailMap(UnsignedInteger fromKey) { |
| return tailMap(fromKey, true); |
| } |
| |
| /** |
| * Gets a key-value mapping associated with the given key or its successor. |
| * This method does not splay the tree so it will not bring the result to the root. |
| * |
| * @param key |
| * The key to search for in the mappings |
| * |
| * @return the entry that matches the search criteria or null if no valid match. |
| */ |
| private SplayedEntry<E> getCeilingEntry(int key) { |
| SplayedEntry<E> result = this.root; |
| |
| while (result != null) { |
| final int comparison = SplayMap.compare(key, result.key); |
| if (comparison < 0) { |
| // search key is less than current go left to get a smaller value |
| if (result.left != null) { |
| result = result.left; |
| } else { |
| return result; // nothing smaller exists |
| } |
| } else if (comparison > 0) { |
| // search key is greater than current go right to get a bigger one |
| // or go back up to the root of this branch |
| if (result.right != null) { |
| result = result.right; |
| } else { |
| SplayedEntry<E> parent = result.parent; |
| SplayedEntry<E> current = result; |
| while (parent != null && current == parent.right) { |
| current = parent; |
| parent = parent.parent; |
| } |
| return parent; |
| } |
| } else { |
| return result; // Found it. |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Gets a key-value mapping associated with the given key or its predecessor. |
| * This method does not splay the tree so it will not bring the result to the root. |
| * |
| * @param key |
| * The key to search for in the mappings |
| * |
| * @return the entry that matches the search criteria or null if no valid match. |
| */ |
| private SplayedEntry<E> getFloorEntry(int key) { |
| SplayedEntry<E> result = this.root; |
| |
| while (result != null) { |
| final int comparison = SplayMap.compare(key, result.key); |
| if (comparison > 0) { |
| // search key is greater than current go right to get a bigger value |
| if (result.right != null) { |
| result = result.right; |
| } else { |
| return result; // nothing bigger exists |
| } |
| } else if (comparison < 0) { |
| // search key is less than current go left to get a smaller one |
| // or go back up to the root of this branch |
| if (result.left != null) { |
| result = result.left; |
| } else { |
| SplayedEntry<E> parent = result.parent; |
| SplayedEntry<E> current = result; |
| while (parent != null && current == parent.left) { |
| current = parent; |
| parent = parent.parent; |
| } |
| return parent; |
| } |
| } else { |
| return result; // Found it. |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Gets a key-value mapping associated with the next entry higher than the given |
| * key or null if no entries exists with a higher key value. This method does not |
| * splay the tree so it will not bring the result to the root. |
| * |
| * @param key |
| * The key to search for in the mappings |
| * |
| * @return the entry that matches the search criteria or null if no valid match. |
| */ |
| private SplayedEntry<E> getHigherEntry(int key) { |
| SplayedEntry<E> result = this.root; |
| |
| while (result != null) { |
| final int comparison = SplayMap.compare(key, result.key); |
| if (comparison < 0) { |
| // search key is less than current go left to get a smaller value |
| if (result.left != null) { |
| result = result.left; |
| } else { |
| return result; // nothing smaller exists |
| } |
| } else { |
| // search key is greater than current go right to get a bigger one |
| // or go back up to the root of this branch |
| if (result.right != null) { |
| result = result.right; |
| } else { |
| SplayedEntry<E> parent = result.parent; |
| SplayedEntry<E> current = result; |
| while (parent != null && current == parent.right) { |
| current = parent; |
| parent = parent.parent; |
| } |
| return parent; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Gets a key-value mapping associated with next smallest entry below the given key |
| * or null if no smaller entries exist. This method does not splay the tree so it |
| * will not bring the result to the root. |
| * |
| * @param key |
| * The key to search for in the mappings |
| * |
| * @return the entry that matches the search criteria or null if no valid match. |
| */ |
| private SplayedEntry<E> getLowerEntry(int key) { |
| SplayedEntry<E> result = this.root; |
| |
| while (result != null) { |
| final int comparison = SplayMap.compare(key, result.key); |
| if (comparison > 0) { |
| // search key is greater than current go right to get a bigger value |
| if (result.right != null) { |
| result = result.right; |
| } else { |
| return result; // nothing bigger exists |
| } |
| } else { |
| // search key is less than current go left to get a smaller one |
| // or go back up to the root of this branch |
| if (result.left != null) { |
| result = result.left; |
| } else { |
| SplayedEntry<E> parent = result.parent; |
| SplayedEntry<E> current = result; |
| while (parent != null && current == parent.left) { |
| current = parent; |
| parent = parent.parent; |
| } |
| return parent; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| protected static class NavigableSubMapKeySet extends AbstractSet<UnsignedInteger> implements NavigableSet<UnsignedInteger> { |
| |
| private final NavigableSubMap<?> backingMap; |
| |
| public NavigableSubMapKeySet(NavigableSubMap<?> backingMap) { |
| this.backingMap = backingMap; |
| } |
| |
| @Override |
| public Iterator<UnsignedInteger> iterator() { |
| return backingMap.keyIterator(); |
| } |
| |
| @Override |
| public Iterator<UnsignedInteger> descendingIterator() { |
| return ((NavigableSubMap<?>)backingMap.descendingMap()).descendingKeyIterator(); |
| } |
| |
| @Override |
| public int size() { |
| return backingMap.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return backingMap.isEmpty(); |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| return backingMap.containsKey(o); |
| } |
| |
| @Override |
| public boolean remove(Object target) { |
| int oldSize = backingMap.size(); |
| backingMap.remove(target); |
| return oldSize != backingMap.size(); |
| } |
| |
| @Override |
| public void clear() { |
| backingMap.clear(); |
| } |
| |
| @Override |
| public Comparator<? super UnsignedInteger> comparator() { |
| return backingMap.comparator(); |
| } |
| |
| @Override |
| public UnsignedInteger first() { |
| return backingMap.firstKey(); |
| } |
| |
| @Override |
| public UnsignedInteger last() { |
| return backingMap.lastKey(); |
| } |
| |
| @Override |
| public UnsignedInteger lower(UnsignedInteger key) { |
| return backingMap.lowerKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger floor(UnsignedInteger key) { |
| return backingMap.floorKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger ceiling(UnsignedInteger key) { |
| return backingMap.ceilingKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger higher(UnsignedInteger key) { |
| return backingMap.higherKey(key); |
| } |
| |
| @Override |
| public UnsignedInteger pollFirst() { |
| Map.Entry<UnsignedInteger, ?> first = backingMap.pollFirstEntry(); |
| return first == null ? null : first.getKey(); |
| } |
| |
| @Override |
| public UnsignedInteger pollLast() { |
| Map.Entry<UnsignedInteger, ?> first = backingMap.pollLastEntry(); |
| return first == null ? null : first.getKey(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> descendingSet() { |
| return backingMap.descendingMap().navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> subSet(UnsignedInteger fromElement, boolean fromInclusive, UnsignedInteger toElement, boolean toInclusive) { |
| return backingMap.subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> headSet(UnsignedInteger toElement, boolean inclusive) { |
| return backingMap.headMap(toElement, inclusive).navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> tailSet(UnsignedInteger fromElement, boolean inclusive) { |
| return backingMap.tailMap(fromElement, inclusive).navigableKeySet(); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> subSet(UnsignedInteger fromElement, UnsignedInteger toElement) { |
| return subSet(fromElement, true, toElement, true); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> headSet(UnsignedInteger toElement) { |
| return headSet(toElement, false); |
| } |
| |
| @Override |
| public SortedSet<UnsignedInteger> tailSet(UnsignedInteger fromElement) { |
| return tailSet(fromElement, false); |
| } |
| } |
| |
| /** |
| * Utility base class for the {@link SplayMap} that allows access to a sub region of the |
| * backing map. |
| * <p> |
| * If the from start directive is <code>true</code> then the map ignores the start key |
| * and the start key inclusive directive and assigns the start key as zero and the |
| * start key inclusive value to true. |
| * <p> |
| * If the to end directive is <code>true</code> then the map ignores the end key |
| * and the end key inclusive directive and assigns the end key to {@link UnsignedInteger#MAX_VALUE} |
| * and the end key inclusive flag to true. |
| * |
| * @param <E> The value type for this {@link NavigableSubMap} |
| */ |
| private abstract static class NavigableSubMap<E> extends AbstractMap<UnsignedInteger, E> implements NavigableMap<UnsignedInteger, E> { |
| |
| protected final SplayMap<E> backingMap; |
| |
| final int startKey; |
| final int endKey; |
| final boolean fromStart; |
| final boolean toEnd; |
| final boolean startInclusive; |
| final boolean endInclusive; |
| |
| private transient NavigableSubMapKeySet navigableSubMapKeySet; |
| |
| NavigableSubMap(final SplayMap<E> map, |
| final boolean fromStart, final int start, final boolean startInclusive, |
| final boolean toEnd, final int end, final boolean endInclusive) { |
| |
| if (SplayMap.compare(start, end) > 0) { |
| throw new IllegalArgumentException("The start key cannot be greater than the end key"); |
| } |
| |
| this.backingMap = map; |
| this.fromStart = fromStart; |
| this.toEnd = toEnd; |
| this.startKey = fromStart ? 0 : start; |
| this.endKey = toEnd ? UnsignedInteger.MAX_VALUE.intValue() : end; |
| this.startInclusive = fromStart ? true : startInclusive; |
| this.endInclusive = toEnd ? true : endInclusive; |
| } |
| |
| //----- Basic Map implementation that defers to backing when possible |
| |
| @Override |
| public boolean isEmpty() { |
| return (fromStart && toEnd) ? backingMap.size == 0 : entrySet().isEmpty(); |
| } |
| |
| @Override |
| public int size() { |
| return (fromStart && toEnd) ? backingMap.size : entrySet().size(); |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| Number numericKey = (Number) key; |
| return containsKey(numericKey.intValue()); |
| } |
| |
| public boolean containsKey(int key) { |
| return isInRange(key) && backingMap.containsKey(key); |
| } |
| |
| @Override |
| public final E put(UnsignedInteger key, E value) { |
| return put(key.intValue(), value); |
| } |
| |
| public final E put(int key, E value) { |
| if (!isInRange(key)) { |
| throw new IllegalArgumentException("The given key is out of range for this ranged sub-map"); |
| } |
| |
| return backingMap.put(key, value); |
| } |
| |
| @Override |
| public final E get(Object key) { |
| Number numericKey = (Number) key; |
| return get(numericKey.intValue()); |
| } |
| |
| public final E get(int key) { |
| return !isInRange(key) ? null : backingMap.get(key); |
| } |
| |
| @Override |
| public final E remove(Object key) { |
| Number numericKey = (Number) key; |
| return remove(numericKey.intValue()); |
| } |
| |
| public final E remove(int key) { |
| return !isInRange(key) ? null : backingMap.remove(key); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> ceilingEntry(UnsignedInteger key) { |
| return SplayMap.export(getCeilingEntry(key.intValue())); |
| } |
| |
| @Override |
| public final UnsignedInteger ceilingKey(UnsignedInteger key) { |
| SplayedEntry<E> result = getCeilingEntry(key.intValue()); |
| return result == null ? null : result.getKey(); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> higherEntry(UnsignedInteger key) { |
| return SplayMap.export(getHigherEntry(key.intValue())); |
| } |
| |
| @Override |
| public final UnsignedInteger higherKey(UnsignedInteger key) { |
| SplayedEntry<E> result = getHigherEntry(key.intValue()); |
| return result == null ? null : result.getKey(); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> floorEntry(UnsignedInteger key) { |
| return SplayMap.export(getFloorEntry(key.intValue())); |
| } |
| |
| @Override |
| public final UnsignedInteger floorKey(UnsignedInteger key) { |
| SplayedEntry<E> result = getFloorEntry(key.intValue()); |
| return result == null ? null : result.getKey(); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> lowerEntry(UnsignedInteger key) { |
| return SplayMap.export(getLowerEntry(key.intValue())); |
| } |
| |
| @Override |
| public final UnsignedInteger lowerKey(UnsignedInteger key) { |
| SplayedEntry<E> result = getLowerEntry(key.intValue()); |
| return result == null ? null : result.getKey(); |
| } |
| |
| @Override |
| public final UnsignedInteger firstKey() { |
| SplayedEntry<E> result = getLowestEntry(); |
| if (result != null) { |
| return result.getKey(); |
| } |
| |
| throw new NoSuchElementException(); |
| } |
| |
| @Override |
| public final UnsignedInteger lastKey() { |
| SplayedEntry<E> result = getHighestEntry(); |
| if (result != null) { |
| return result.getKey(); |
| } |
| |
| throw new NoSuchElementException(); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> firstEntry() { |
| return SplayMap.export(getLowestEntry()); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> lastEntry() { |
| return SplayMap.export(getHighestEntry()); |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> pollFirstEntry() { |
| SplayedEntry<E> result = getLowestEntry(); |
| Map.Entry<UnsignedInteger, E> exported = SplayMap.export(result); |
| if (exported != null) { |
| backingMap.delete(result); |
| } |
| |
| return exported; |
| } |
| |
| @Override |
| public final Map.Entry<UnsignedInteger, E> pollLastEntry() { |
| SplayedEntry<E> result = getHighestEntry(); |
| Map.Entry<UnsignedInteger, E> exported = SplayMap.export(result); |
| if (exported != null) { |
| backingMap.delete(result); |
| } |
| |
| return exported; |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> subMap(UnsignedInteger fromKey, UnsignedInteger toKey) { |
| return subMap(fromKey, true, toKey, false); |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> headMap(UnsignedInteger toKey) { |
| return headMap(toKey, false); |
| } |
| |
| @Override |
| public SortedMap<UnsignedInteger, E> tailMap(UnsignedInteger fromKey) { |
| return tailMap(fromKey, true); |
| } |
| |
| @Override |
| public final Set<UnsignedInteger> keySet() { |
| return navigableKeySet(); |
| } |
| |
| @Override |
| public NavigableSet<UnsignedInteger> descendingKeySet() { |
| return descendingMap().navigableKeySet(); |
| } |
| |
| @Override |
| public final NavigableSet<UnsignedInteger> navigableKeySet() { |
| return (navigableSubMapKeySet != null) ? |
| navigableSubMapKeySet : (navigableSubMapKeySet = new NavigableSubMapKeySet(this)); |
| } |
| |
| //----- The abstract API that sub-classes will define |
| |
| /** |
| * Returns an iterator appropriate to the sub map implementation |
| * which may be ascending or descending but must be the inverse of |
| * the direction of iterator returned from the {@link #descendingKeyIterator()} |
| * method. |
| * |
| * @return an iterator that operates over the keys in this sub map range. |
| */ |
| abstract Iterator<UnsignedInteger> keyIterator(); |
| |
| /** |
| * Returns an iterator appropriate to the sub map implementation |
| * which may be ascending or descending but must be the inverse of |
| * the direction of iterator returned from the {@link #keyIterator()} |
| * method. |
| * |
| * @return an iterator that operates over the keys in this sub map range. |
| */ |
| abstract Iterator<UnsignedInteger> descendingKeyIterator(); |
| |
| //----- Sub Map collection types |
| |
| /** |
| * Specialized iterator for the sub-map type that iterators on a generic type |
| * but internally contains splayed entries from the splay map tree. |
| * |
| * @param <T> |
| */ |
| protected abstract class NavigableSubMapIterator<T> implements Iterator<T> { |
| SplayedEntry<E> lastReturned; |
| SplayedEntry<E> next; |
| final int limitKey; |
| int expectedModCount; |
| |
| NavigableSubMapIterator(SplayedEntry<E> start, SplayedEntry<E> limit) { |
| expectedModCount = backingMap.modCount; |
| lastReturned = null; |
| next = start; |
| limitKey = limit != null ? limit.key : UnsignedInteger.MAX_VALUE.intValue(); |
| } |
| |
| @Override |
| public abstract boolean hasNext(); |
| |
| @Override |
| public abstract T next(); |
| |
| @Override |
| public final void remove() { |
| if (lastReturned == null) { |
| throw new IllegalStateException(); |
| } |
| if (backingMap.modCount != expectedModCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| backingMap.delete(lastReturned); |
| lastReturned = null; |
| expectedModCount = backingMap.modCount; |
| } |
| |
| final boolean hasNextEntry() { |
| return next != null && SplayMap.compare(next.key, limitKey) <= 0; |
| } |
| |
| final SplayedEntry<E> nextEntry() { |
| SplayedEntry<E> e = next; |
| |
| if (e == null || SplayMap.compare(next.key, limitKey) > 0) { |
| throw new NoSuchElementException(); |
| } |
| if (backingMap.modCount != expectedModCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| next = successor(e); |
| lastReturned = e; |
| return e; |
| } |
| |
| final boolean hasPrevEntry() { |
| return next != null && SplayMap.compare(next.key, limitKey) >= 0; |
| } |
| |
| final SplayedEntry<E> previousEntry() { |
| SplayedEntry<E> e = next; |
| |
| if (e == null || SplayMap.compare(next.key, limitKey) < 0) { |
| throw new NoSuchElementException(); |
| } |
| if (backingMap.modCount != expectedModCount) { |
| throw new ConcurrentModificationException(); |
| } |
| |
| next = predecessor(e); |
| lastReturned = e; |
| return e; |
| } |
| } |
| |
| final class NavigableSubMapEntryIterator extends NavigableSubMapIterator<Map.Entry<UnsignedInteger, E>> { |
| NavigableSubMapEntryIterator(SplayedEntry<E> first, SplayedEntry<E> fence) { |
| super(first, fence); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return hasNextEntry(); |
| } |
| |
| @Override |
| public Map.Entry<UnsignedInteger, E> next() { |
| return nextEntry(); |
| } |
| } |
| |
| final class DescendingNavigableSubMapEntryIterator extends NavigableSubMapIterator<Map.Entry<UnsignedInteger, E>> { |
| DescendingNavigableSubMapEntryIterator(SplayedEntry<E> first, SplayedEntry<E> fence) { |
| super(first, fence); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return hasPrevEntry(); |
| } |
| |
| @Override |
| public Map.Entry<UnsignedInteger, E> next() { |
| return previousEntry(); |
| } |
| } |
| |
| final class NavigableSubMapKeyIterator extends NavigableSubMapIterator<UnsignedInteger> { |
| NavigableSubMapKeyIterator(SplayedEntry<E> first, SplayedEntry<E> fence) { |
| super(first, fence); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return hasNextEntry(); |
| } |
| |
| @Override |
| public UnsignedInteger next() { |
| return nextEntry().getKey(); |
| } |
| } |
| |
| final class DescendingNavigableSubMapKeyIterator extends NavigableSubMapIterator<UnsignedInteger> { |
| DescendingNavigableSubMapKeyIterator(SplayedEntry<E> first, SplayedEntry<E> fence) { |
| super(first, fence); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return hasPrevEntry(); |
| } |
| |
| @Override |
| public UnsignedInteger next() { |
| return previousEntry().getKey(); |
| } |
| } |
| |
| protected abstract class NavigableSubMapEntrySet extends AbstractSet<Map.Entry<UnsignedInteger, E>> { |
| private transient int size = -1; |
| private transient int sizeModCount; |
| |
| @Override |
| public int size() { |
| if ((!fromStart || !toEnd) && (size == -1 || sizeModCount != backingMap.modCount)) { |
| sizeModCount = backingMap.modCount; |
| size = 0; |
| Iterator<?> i = iterator(); |
| while (i.hasNext()) { |
| size++; |
| i.next(); |
| } |
| |
| return size; |
| } |
| |
| return backingMap.size; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| SplayedEntry<E> n = getLowestEntry(); |
| return n == null || isToHigh(n.key); |
| } |
| |
| @Override |
| public boolean contains(Object o) { |
| if (o instanceof Map.Entry) { |
| Map.Entry<?,?> entry = (Map.Entry<?,?>) o; |
| Number key = Number.class.cast(entry.getKey()); |
| |
| if (!isInRange(key.intValue())) { |
| return false; |
| } |
| |
| SplayedEntry<E> node = backingMap.findEntry(key.intValue()); |
| |
| if (node != null) { |
| return Objects.equals(node.getValue(), entry.getValue()); |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public boolean remove(Object o) { |
| if (o instanceof Map.Entry) { |
| Map.Entry<?,?> entry = (Map.Entry<?,?>) o; |
| Number key = Number.class.cast(entry.getKey()); |
| |
| if (!isInRange(key.intValue())) { |
| return false; |
| } |
| |
| SplayedEntry<E> node = backingMap.findEntry(key.intValue()); |
| |
| if (node != null) { |
| backingMap.delete(node); |
| return Objects.equals(node.getValue(), entry.getValue()); |
| } |
| } |
| |
| return false; |
| } |
| } |
| |
| //----- Abstract access API which will be inverted based on sub map type |
| |
| abstract SplayedEntry<E> getLowestEntry(); |
| |
| abstract SplayedEntry<E> getHighestEntry(); |
| |
| abstract SplayedEntry<E> getCeilingEntry(int key); |
| |
| abstract SplayedEntry<E> getHigherEntry(int key); |
| |
| abstract SplayedEntry<E> getFloorEntry(int key); |
| |
| abstract SplayedEntry<E> getLowerEntry(int key); |
| |
| //----- Internal API that aids this and other sub-classes |
| |
| protected final SplayedEntry<E> lowestPossibleEntry() { |
| SplayedEntry<E> e = |
| startInclusive ? backingMap.getCeilingEntry(startKey) : backingMap.getHigherEntry(startKey); |
| |
| return (e == null || isToHigh(e.key)) ? null : e; |
| } |
| |
| protected final SplayedEntry<E> highestPossibleEntry() { |
| SplayedEntry<E> e = |
| endInclusive ? backingMap.getFloorEntry(endKey) : backingMap.getLowerEntry(endKey); |
| |
| return (e == null || isToLow(e.key)) ? null : e; |
| } |
| |
| protected final SplayedEntry<E> entryOrSuccessor(int key) { |
| if (isToLow(key)) { |
| return lowestPossibleEntry(); |
| } |
| SplayedEntry<E> e = backingMap.getCeilingEntry(key); |
| |
| return (e == null || isToHigh(e.key)) ? null : e; |
| } |
| |
| protected final SplayedEntry<E> entrySuccessor(int key) { |
| if (isToLow(key)) { |
| return lowestPossibleEntry(); |
| } |
| SplayedEntry<E> e = backingMap.getHigherEntry(key); |
| |
| return (e == null || isToHigh(e.key)) ? null : e; |
| } |
| |
| protected final SplayedEntry<E> entryOrPredecessor(int key) { |
| if (isToHigh(key)) { |
| return highestPossibleEntry(); |
| } |
| SplayedEntry<E> e = backingMap.getFloorEntry(key); |
| |
| return (e == null || isToLow(e.key)) ? null : e; |
| } |
| |
| protected final SplayedEntry<E> entryPredecessor(int key) { |
| if (isToHigh(key)) { |
| return highestPossibleEntry(); |
| } |
| SplayedEntry<E> e = backingMap.getLowerEntry(key); |
| |
| return (e == null || isToLow(e.key)) ? null : e; |
| } |
| |
| protected final void checkInRange(int fromKey, boolean fromInclusive, int toKey, boolean toInclusive) { |
| if (!isInRange(fromKey, fromInclusive)) { |
| throw new IllegalArgumentException("Given from key is out of range of this sub map view: " + fromKey); |
| } |
| if (!isInRange(toKey, toInclusive)) { |
| throw new IllegalArgumentException("Given to key is out of range of this sub map view: " + toKey); |
| } |
| } |
| |
| protected final boolean isInRange(int key) { |
| return !isToLow(key) && !isToHigh(key); |
| } |
| |
| final boolean isInCapturedRange(int key) { |
| return SplayMap.compare(key, startKey) >= 0 && SplayMap.compare(endKey, key) >= 0; |
| } |
| |
| final boolean isInRange(int key, boolean inclusive) { |
| return inclusive ? isInRange(key) : isInCapturedRange(key); |
| } |
| |
| protected final boolean isToLow(int key) { |
| int result = SplayMap.compare(key, startKey); |
| if (result < 0 || result == 0 && !startInclusive) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| protected final boolean isToHigh(int key) { |
| int result = SplayMap.compare(key, endKey); |
| if (result > 0 || result == 0 && !endInclusive) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| } |
| |
| protected static final class AscendingSubMap<V> extends NavigableSubMap<V> { |
| |
| AscendingSubMap(SplayMap<V> m, |
| boolean fromStart, int fromKey, boolean startInclusive, |
| boolean toEnd, int endKey, boolean endInclusive) { |
| super(m, fromStart, fromKey, startInclusive, toEnd, endKey, endInclusive); |
| } |
| |
| private transient NavigableMap<UnsignedInteger, V> descendingMapView; |
| private transient NavigableSubMapEntrySet navigableEntrySet; |
| |
| @Override |
| public Comparator<? super UnsignedInteger> comparator() { |
| return COMPARATOR; |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> descendingMap() { |
| return descendingMapView != null ? descendingMapView : |
| (descendingMapView = new DescendingSubMap<>(backingMap, |
| fromStart, startKey, startInclusive, toEnd, endKey, endInclusive)); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> subMap(UnsignedInteger fromKey, boolean fromInclusive, |
| UnsignedInteger toKey, boolean toInclusive) { |
| checkInRange(fromKey.intValue(), fromInclusive, toKey.compareTo(0), toInclusive); |
| return new AscendingSubMap<>(backingMap, |
| false, fromKey.intValue(), fromInclusive, false, toKey.intValue(), toInclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> headMap(UnsignedInteger toKey, boolean inclusive) { |
| isInRange(toKey.intValue(), inclusive); |
| return new AscendingSubMap<>(backingMap, |
| fromStart, startKey, startInclusive, false, toKey.intValue(), inclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> tailMap(UnsignedInteger fromKey, boolean inclusive) { |
| isInRange(fromKey.intValue(), inclusive); |
| return new AscendingSubMap<>(backingMap, |
| false, fromKey.intValue(), inclusive, toEnd, endKey, endInclusive); |
| } |
| |
| @Override |
| public Set<Entry<UnsignedInteger, V>> entrySet() { |
| return navigableEntrySet != null ? |
| navigableEntrySet : (navigableEntrySet = new AscendingNavigableSubMapEntrySet()); |
| } |
| |
| @Override |
| Iterator<UnsignedInteger> keyIterator() { |
| return new NavigableSubMapKeyIterator(getLowestEntry(), getHighestEntry()); |
| } |
| |
| @Override |
| Iterator<UnsignedInteger> descendingKeyIterator() { |
| return new DescendingNavigableSubMapKeyIterator(getLowestEntry(), getHighestEntry()); |
| } |
| |
| @Override |
| SplayedEntry<V> getLowestEntry() { |
| return super.lowestPossibleEntry(); |
| } |
| |
| @Override |
| SplayedEntry<V> getHighestEntry() { |
| return super.highestPossibleEntry(); |
| } |
| |
| @Override |
| SplayedEntry<V> getCeilingEntry(int key) { |
| return super.entryOrSuccessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getHigherEntry(int key) { |
| return super.entrySuccessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getFloorEntry(int key) { |
| return super.entryOrPredecessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getLowerEntry(int key) { |
| return super.entryPredecessor(key); |
| } |
| |
| private final class AscendingNavigableSubMapEntrySet extends NavigableSubMapEntrySet { |
| |
| @Override |
| public Iterator<Entry<UnsignedInteger, V>> iterator() { |
| return new NavigableSubMapEntryIterator(lowestPossibleEntry(), highestPossibleEntry()); |
| } |
| } |
| } |
| |
| protected static final class DescendingSubMap<V> extends NavigableSubMap<V> { |
| |
| DescendingSubMap(SplayMap<V> m, |
| boolean fromStart, int fromKey, boolean startInclusive, |
| boolean toEnd, int endKey, boolean endInclusive) { |
| super(m, fromStart, fromKey, startInclusive, toEnd, endKey, endInclusive); |
| } |
| |
| private transient NavigableMap<UnsignedInteger, V> ascendingMapView; |
| private transient NavigableSubMapEntrySet navigableEntrySet; |
| |
| @Override |
| public Comparator<? super UnsignedInteger> comparator() { |
| return REVERSE_COMPARATOR; |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> descendingMap() { |
| return ascendingMapView != null ? ascendingMapView : |
| (ascendingMapView = new AscendingSubMap<>(backingMap, |
| fromStart, startKey, startInclusive, toEnd, endKey, endInclusive)); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> subMap(UnsignedInteger fromKey, boolean fromInclusive, |
| UnsignedInteger toKey, boolean toInclusive) { |
| checkInRange(fromKey.intValue(), fromInclusive, toKey.intValue(), toInclusive); |
| return new DescendingSubMap<>(backingMap, |
| false, toKey.intValue(), toInclusive, false, fromKey.intValue(), fromInclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> headMap(UnsignedInteger toKey, boolean inclusive) { |
| isInRange(toKey.intValue(), inclusive); |
| return new DescendingSubMap<>(backingMap, |
| false, toKey.intValue(), inclusive, toEnd, endKey, endInclusive); |
| } |
| |
| @Override |
| public NavigableMap<UnsignedInteger, V> tailMap(UnsignedInteger fromKey, boolean inclusive) { |
| isInRange(fromKey.intValue(), inclusive); |
| return new DescendingSubMap<>(backingMap, |
| fromStart, startKey, startInclusive, false, fromKey.intValue(), inclusive); |
| } |
| |
| @Override |
| public Set<Entry<UnsignedInteger, V>> entrySet() { |
| return navigableEntrySet != null ? |
| navigableEntrySet : (navigableEntrySet = new DescendingNavigableSubMapEntrySet()); |
| } |
| |
| @Override |
| Iterator<UnsignedInteger> keyIterator() { |
| return new DescendingNavigableSubMapKeyIterator(getHighestEntry(), getLowestEntry()); |
| } |
| |
| @Override |
| Iterator<UnsignedInteger> descendingKeyIterator() { |
| return new NavigableSubMapKeyIterator(getHighestEntry(), getLowestEntry()); |
| } |
| |
| @Override |
| SplayedEntry<V> getLowestEntry() { |
| return super.highestPossibleEntry(); |
| } |
| |
| @Override |
| SplayedEntry<V> getHighestEntry() { |
| return super.lowestPossibleEntry(); |
| } |
| |
| @Override |
| SplayedEntry<V> getCeilingEntry(int key) { |
| return super.entryPredecessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getHigherEntry(int key) { |
| return super.entryPredecessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getFloorEntry(int key) { |
| return super.entryOrSuccessor(key); |
| } |
| |
| @Override |
| SplayedEntry<V> getLowerEntry(int key) { |
| return super.entryOrSuccessor(key); |
| } |
| |
| @Override |
| public void forEach(BiConsumer<? super UnsignedInteger, ? super V> action) { |
| Objects.requireNonNull(action); |
| for (Map.Entry<UnsignedInteger, V> entry : entrySet()) { |
| UnsignedInteger k; |
| V v; |
| try { |
| k = entry.getKey(); |
| v = entry.getValue(); |
| } catch (IllegalStateException ise) { |
| // this usually means the entry is no longer in the map. |
| throw new ConcurrentModificationException(ise); |
| } |
| action.accept(k, v); |
| } |
| } |
| |
| private final class DescendingNavigableSubMapEntrySet extends NavigableSubMapEntrySet { |
| |
| @Override |
| public Iterator<Entry<UnsignedInteger, V>> iterator() { |
| return new DescendingNavigableSubMapEntryIterator(highestPossibleEntry(), lowestPossibleEntry()); |
| } |
| } |
| } |
| } |