blob: 6e8d97d71dd746a5d84d03498d5d489ef534e077 [file] [log] [blame]
/*
* 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.AbstractSet;
import java.util.Collection;
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.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> {
private static final UnsignedComparator COMPARATOR = new UnsignedComparator();
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;
}
}
}
/**
* 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;
}
@Override
public E putIfAbsent(UnsignedInteger key, E value) {
return putIfAbsent(key.intValue(), value);
}
/**
* 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 get(Object key) {
return get(Number.class.cast(key).intValue());
}
@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;
}
// Once requested we will create an store a single instance to a collection
// with no state for each of the key, values and entries types. Since the
// types are stateless the trivial race on create is not important to the
// eventual outcome of having a cached instance.
protected Set<UnsignedInteger> keySet;
protected Collection<E> values;
protected Set<Entry<UnsignedInteger, E>> entrySet;
@Override
public Set<UnsignedInteger> keySet() {
if (keySet == null) {
keySet = new SplayMapKeySet();
}
return keySet;
}
@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 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 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 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 != 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> firstEntry(SplayedEntry<E> node) {
SplayedEntry<E> firstEntry = node;
if (firstEntry != null) {
while (firstEntry.left != null) {
firstEntry = firstEntry.left;
}
}
return firstEntry;
}
private SplayedEntry<E> lastEntry(SplayedEntry<E> node) {
SplayedEntry<E> lastEntry = node;
if (lastEntry != null) {
while (lastEntry.right != null) {
lastEntry = lastEntry.right;
}
}
return lastEntry;
}
private 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 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;
}
}
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> {
private SplayedEntry<E> nextNode;
private SplayedEntry<E> lastReturned;
private 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;
}
// Unused as of now but can be used for NavigableMap amongst other things
@SuppressWarnings("unused")
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();
}
}
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();
}
}
private final class SplayMapKeySet extends AbstractSet<UnsignedInteger> {
@Override
public Iterator<UnsignedInteger> iterator() {
return new SplayMapKeyIterator(firstEntry(root));
}
@Override
public int size() {
return SplayMap.this.size;
}
@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 void clear() {
SplayMap.this.clear();
}
}
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) {
return false;
}
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 class ImmutableSplayMapEntry 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 ImmutableSplayMapEntry export(SplayedEntry<E> 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);
}
}
//----- 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 firstEntry() {
return export(firstEntry(root));
}
@Override
public ImmutableSplayMapEntry lastEntry() {
return export(lastEntry(root));
}
@Override
public ImmutableSplayMapEntry pollFirstEntry() {
SplayedEntry<E> firstEntry = firstEntry(root);
if (firstEntry != null) {
delete(firstEntry);
}
return export(firstEntry);
}
@Override
public ImmutableSplayMapEntry pollLastEntry() {
SplayedEntry<E> lastEntry = lastEntry(root);
if (lastEntry != null) {
delete(lastEntry);
}
return export(lastEntry);
}
@Override
public ImmutableSplayMapEntry lowerEntry(UnsignedInteger key) {
return export(lowerEntry(key.intValue()));
}
@Override
public UnsignedInteger lowerKey(UnsignedInteger key) {
final SplayedEntry<E> result = lowerEntry(key.intValue());
return result == null ? null : result.getKey();
}
private SplayedEntry<E> lowerEntry(int key) {
root = splay(root, key);
while (root != null) {
if (compare(root.getIntKey(), key) >= 0) {
root = predecessor(root);
} else {
break;
}
}
return root;
}
@Override
public ImmutableSplayMapEntry higherEntry(UnsignedInteger key) {
return export(higherEntry(key.intValue()));
}
@Override
public UnsignedInteger higherKey(UnsignedInteger key) {
final SplayedEntry<E> result = higherEntry(key.intValue());
return result == null ? null : result.getKey();
}
private SplayedEntry<E> higherEntry(int key) {
root = splay(root, key);
while (root != null) {
if (compare(root.getIntKey(), key) <= 0) {
root = successor(root);
} else {
break;
}
}
return root;
}
@Override
public ImmutableSplayMapEntry floorEntry(UnsignedInteger key) {
return export(floorEntry(key.intValue()));
}
@Override
public UnsignedInteger floorKey(UnsignedInteger key) {
final SplayedEntry<E> result = floorEntry(key.intValue());
return result == null ? null : result.getKey();
}
private SplayedEntry<E> floorEntry(int key) {
root = splay(root, key);
while (root != null) {
if (compare(root.getIntKey(), key) > 0) {
root = predecessor(root);
} else {
break;
}
}
return root;
}
@Override
public ImmutableSplayMapEntry ceilingEntry(UnsignedInteger key) {
return export(ceilingEntry(key.intValue()));
}
@Override
public UnsignedInteger ceilingKey(UnsignedInteger key) {
final SplayedEntry<E> result = ceilingEntry(key.intValue());
return result == null ? null : result.getKey();
}
private SplayedEntry<E> ceilingEntry(int key) {
root = splay(root, key);
while (root != null) {
if (compare(root.getIntKey(), key) < 0) {
root = successor(root);
} else {
break;
}
}
return root;
}
@Override
public NavigableMap<UnsignedInteger, E> descendingMap() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableSet<UnsignedInteger> navigableKeySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableSet<UnsignedInteger> descendingKeySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableMap<UnsignedInteger, E> subMap(UnsignedInteger fromKey, boolean fromInclusive, UnsignedInteger toKey, boolean toInclusive) {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableMap<UnsignedInteger, E> headMap(UnsignedInteger toKey, boolean inclusive) {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableMap<UnsignedInteger, E> tailMap(UnsignedInteger fromKey, boolean inclusive) {
// TODO Auto-generated method stub
return null;
}
@Override
public SortedMap<UnsignedInteger, E> subMap(UnsignedInteger fromKey, UnsignedInteger toKey) {
// TODO Auto-generated method stub
return null;
}
@Override
public SortedMap<UnsignedInteger, E> headMap(UnsignedInteger toKey) {
// TODO Auto-generated method stub
return null;
}
@Override
public SortedMap<UnsignedInteger, E> tailMap(UnsignedInteger fromKey) {
// TODO Auto-generated method stub
return null;
}
}