/*******************************************************************************
* Copyright 2012 Analog Devices, Inc.
*
* Licensed 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 com.analog.lyric.collect;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Comparator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
// TODO: implement NavigableMap - requires implementing various submap/keyset views.
public class SkipMap<K, V> extends AbstractSkipList<K> implements Map<K, V>
{
public static class Entry<K,V> implements Map.Entry<K, V>
{
private final Object[] node;
protected Entry(Object[] node)
{
this.node = node;
}
@Override
public boolean equals(@Nullable Object other)
{
if (this == other)
{
return true;
}
if (other instanceof Entry)
{
Entry<?,?> that = (Entry<?,?>)other;
return node[0].equals(that.node[0]) && Objects.equals(node[1], that.node[1]);
}
return false;
}
@Override
public int hashCode()
{
Object value = node[1];
return node[0].hashCode() ^ (value == null ? 0 : value.hashCode());
}
@SuppressWarnings("unchecked")
@Override
public K getKey()
{
return (K)node[0];
}
@SuppressWarnings("unchecked")
@Override
public V getValue()
{
return (V)node[1];
}
@Override
public @Nullable V setValue(@Nullable V value)
{
V oldValue = this.getValue();
node[1] = value;
return oldValue;
}
}
private @Nullable Entry<K,V> makeEntry(@Nullable Object[] node)
{
return node == null ? null : new Entry<K,V>(node);
}
@SuppressWarnings("unchecked")
protected final @Nullable V getNodeValue(Object[] node)
{
return (V)node[1];
}
protected final @Nullable V setNodeValue(Object[] node, @Nullable V value)
{
@SuppressWarnings("unchecked")
V oldValue = (V)node[1];
node[1] = value;
return oldValue;
}
/*--------------
* Construction
*/
public SkipMap(Comparator<? super K> comparator)
{
super(comparator, (short)2);
}
/**
* @since 0.05
*/
public static <K,V> SkipMap<K,V> create(Comparator<? super K> comparator)
{
return new SkipMap<K,V>(comparator);
}
// TODO: add following constructors: (), (Map), (SortedMap)
/*----------------
* Object methods
*/
@Override
public boolean equals(@Nullable Object other)
{
if (this == other)
{
return true;
}
if (other instanceof Map)
{
Map<?,?> otherMap = (Map<?,?>)other;
if (otherMap.size() == size())
{
// TODO: special case if other is a SkipMap or a SortedMap with the same comparator
for (Object[] node = this.getNextNode(this.head); node != null; node = this.getNextNode(node))
{
Object key = node[0], value = node[1];
if (!Objects.equals(otherMap.get(key), value))
{
return false;
}
}
return true;
}
}
return false;
}
@Override
public int hashCode()
{
// Implements computation specified by Map.hashCode()
int hash = 0;
for (Object[] node = this.getNextNode(this.head); node != null; node = this.getNextNode(node))
{
Object key = node[0], value = node[1];
hash += key.hashCode() ^ (value == null ? 0 : value.hashCode());
}
return hash;
}
/*
* Map methods
*/
@Override
public boolean containsKey(@Nullable Object key)
{
try
{
@SuppressWarnings("unchecked")
K k = (K)key;
return k != null && this.containsNode(k);
}
catch (ClassCastException ex)
{
return false;
}
}
@Override
public boolean containsValue(@Nullable Object value)
{
for (Object[] node = this.getNextNode(this.head); node != null; node = this.getNextNode(node))
{
if (Objects.equals(this.getNodeValue(node), value))
{
return true;
}
}
return false;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public EntrySet entrySet()
{
return new EntrySet(this);
}
@Override
public @Nullable V get(@Nullable Object key)
{
V result = null;
if (key != null)
{
try
{
@SuppressWarnings("unchecked")
K k = (K)key;
result = this.get2(k);
}
catch (ClassCastException ex)
{
}
}
return result;
}
@Override
public KeySet<K,V> keySet()
{
return new KeySet<K,V>(this);
}
@Override
public @Nullable V put(@Nullable K key, @Nullable V value)
{
return this.setNodeValue(this.addNode(key), value);
}
@Override
@NonNullByDefault(false)
public void putAll(Map<? extends K, ? extends V> m)
{
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet())
{
this.put(entry.getKey(), entry.getValue());
}
}
@Override
public @Nullable V remove(@Nullable Object key)
{
V result = null;
if (key != null)
{
try
{
@SuppressWarnings("unchecked")
K k = (K)key;
result = this.remove2(k);
}
catch (ClassCastException ex)
{
}
}
return result;
}
@Override
public ValueCollection<K, V> values()
{
return new ValueCollection<K,V>(this);
}
/*
* SortedMap methods
*/
public K firstKey()
{
final Object[] node = this.firstNode();
if (node == null)
{
throw new NoSuchElementException();
}
return this.getNodeKey(node);
}
public K lastKey()
{
final Object[] node = this.lastNode();
if (node == null)
{
throw new NoSuchElementException();
}
return this.getNodeKey(node);
}
/*
* NavigableMap methods
*/
public @Nullable Map.Entry<K,V> ceilingEntry(K key)
{
return this.makeEntry(this.findCeilingNode(key));
}
public @Nullable K ceilingKey(K key)
{
Object[] node = this.findCeilingNode(key);
return node == null ? null : this.getNodeKey(node);
}
public @Nullable Map.Entry<K,V> firstEntry()
{
return this.makeEntry(this.getNextNode(this.head));
}
/**
* Returns greatest value in set that is less than or equal to {@code value} or null.
*/
public @Nullable Map.Entry<K,V> floorEntry(K key)
{
return this.makeEntry(this.findFloorNode(key));
}
public @Nullable K floorKey(K key)
{
Object[] node = this.findFloorNode(key);
return node == null ? null : this.getNodeKey(node);
}
public @Nullable Map.Entry<K,V> higherEntry(K key)
{
return this.makeEntry(this.findHigherNode(key));
}
public @Nullable K higherKey(K key)
{
Object[] node = this.findHigherNode(key);
return node == null ? null : this.getNodeKey(node);
}
public @Nullable Map.Entry<K, V> lastEntry()
{
return this.makeEntry(this.lastNode());
}
public @Nullable Map.Entry<K,V> lowerEntry(K key)
{
Object[] node = this.findLowerNode(key);
return node == this.head ? null : this.makeEntry(node);
}
public K lowerKey(K key)
{
return this.getNodeKey(this.findLowerNode(key));
}
public @Nullable Map.Entry<K,V> pollFirstEntry()
{
return this.makeEntry(this.pollFirstNode());
}
public @Nullable Map.Entry<K,V> pollLastEntry()
{
return this.makeEntry(this.pollLastNode());
}
/*
* SkipMap methods
*/
/** Like {@link #containsKey} but {@code key} must be of type {@code K}. */
public boolean containsKey2(K key)
{
return this.containsNode(key);
}
/** Like {@link #get} but {@code key} must be of type {@code K}. */
public @Nullable V get2(K key)
{
Object[] node = this.getNode(key);
return node != null ? this.getNodeValue(node) : null;
}
/** Like {@link #remove} but {@code key} must be of type {@code K}. */
public @Nullable V remove2(K key)
{
Object[] node = this.removeNode(key);
return node == null ? null : this.getNodeValue(node);
}
/*
* Views
*/
public static class EntrySet<K,V> extends AbstractSet<Entry<K,V>>
implements ReleasableIterableCollection<Entry<K,V>>
{
private final SkipMap<K,V> map;
private EntrySet(SkipMap<K,V> map)
{
this.map = map;
}
@Override
public void clear()
{
this.map.clear();
}
@Override
public boolean contains(@Nullable Object obj)
{
if (obj != null)
{
try
{
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K, V>)obj;
K key = entry.getKey();
return entry.node == this.map.getNode(key);
}
catch (ClassCastException ex)
{
}
}
return false;
}
@Override
public Iterator<K,V> iterator()
{
return Iterator.make(this.map);
}
@SuppressWarnings("rawtypes")
@Override
public boolean remove(@Nullable Object obj)
{
if (obj instanceof Map.Entry)
{
return this.map.remove(((Map.Entry)obj).getKey()) != null;
}
return false;
}
@Override
public int size()
{
return this.map.size();
}
}
public static class KeySet<K,V> extends AbstractSet<K> implements ReleasableIterableCollection<K>
{
private final SkipMap<K,V> map;
private KeySet(SkipMap<K,V> map)
{
this.map = map;
}
@Override
public void clear()
{
this.map.clear();
}
@Override
public boolean contains(@Nullable Object key)
{
return this.map.containsKey(key);
}
@Override
public KeyIterator<K,V> iterator()
{
return KeyIterator.make(this.map);
}
@Override
public boolean remove(@Nullable Object key)
{
return key != null && this.map.remove(key) != null;
}
@Override
public int size()
{
return this.map.size();
}
}
public static class ValueCollection<K,V> extends AbstractCollection<V>
implements ReleasableIterableCollection<V>
{
private final SkipMap<K,V> map;
private ValueCollection(SkipMap<K,V> map)
{
this.map = map;
}
@Override
public void clear()
{
this.map.clear();
}
@Override
public boolean contains(@Nullable Object value)
{
return this.map.containsValue(value);
}
@Override
public ValueIterator<K,V> iterator()
{
return ValueIterator.make(this.map);
}
@Override
public int size()
{
return this.map.size();
}
}
/*
* Iterators
*/
/**
* A reusable iterator for fast iteration over {@link SkipMap} entries.
* <p>
* This iterator is optimized for visiting elements in set without removal.
* Iterating over the entire set costs O(n) in the size of the map. The
* {@link #remove} method is supported but costs O(log(n)).
*/
public static class Iterator<K,V> implements ReleasableIterator<Entry<K,V>>
{
/*
* State
*/
/** The underlying map to be iterated over. This may be null. */
protected @Nullable SkipMap<K,V> map;
/** The next node to be returned by {@link #next}. Null when there are no more nodes. */
private @Nullable Object[] nextNode;
/** The last value returned by {@link #next}. Could be null. */
private @Nullable K lastKey;
private static final ThreadLocal<Iterator<?,?>> reusableInstance = new ThreadLocal<Iterator<?,?>>();
/*
* Construction/initialization methods
*/
/**
* Constructs iterator over given {@code map}, which may be null.
*/
public Iterator(@Nullable SkipMap<K,V> map)
{
this.reset(map);
}
protected static <K,V> Iterator<K,V> make(@Nullable SkipMap<K,V> map)
{
@SuppressWarnings("unchecked")
Iterator<K,V> iter = (Iterator<K, V>)Iterator.reusableInstance.get();
if (iter != null)
{
Iterator.reusableInstance.set(null);
iter.reset(map);
}
else
{
iter = new Iterator<K,V>(map);
}
return iter;
}
@Override
public void release()
{
if (Iterator.reusableInstance.get() == null)
{
this.reset(null);
Iterator.reusableInstance.set(this);
}
}
/**
* Resets iterator back to beginning of map.
*/
public void reset()
{
final @Nullable SkipMap<K,V> map2 = this.map;
this.nextNode = map2 == null ? null : map2.getNextNode(map2.head);
}
/**
* Resets iterator to beginning of {@code newMap}, which may be null.
*/
public void reset(@Nullable SkipMap<K,V> newMap)
{
this.map = newMap;
this.reset();
}
/*
* java.util.Iterator methods
*/
/**
* Returns true if {@link #next} method will return a non-null value.
*/
@Override
public boolean hasNext()
{
return this.nextNode != null;
}
/**
* Returns the next element in the iteration or null if at the end of the list.
* It is not necessary to invoke {@link #hasNext} before calling this method.
*/
@Override
public @Nullable Entry<K,V> next()
{
Object[] node = this.advance();
return node == null ? null : new Entry<K,V>(node);
}
@Override
public void remove()
{
final SkipMap<K,V> map2 = map;
if (map2 == null)
{
throw new IllegalStateException();
}
map2.removeNode(this.lastKey);
}
/*
* Local Iterator methods
*/
/**
* Returns the next value in the iteration or null if at the end of the list.
* It is not necessary to invoke {@link #hasNext} before calling this method.
* You can obtain the key to which this value was mapped by calling
* {@link #getLastKey} before the next call to {@link #next} or {@link #nextValue}.
*/
public @Nullable V nextValue()
{
final SkipMap<K,V> map2 = this.map;
@Nullable V result = null;
if (map2 != null)
{
@Nullable Object[] node = this.advance();
result = node == null ? null : map2.getNodeValue(node);
}
return result;
}
/**
* Returns the key associated with the last value returned by {@link #nextValue}
* or with the last entry returned by {@link #next}. Returns null if at start
* or end of iteration.
*/
public @Nullable K getLastKey()
{
return this.lastKey;
}
private @Nullable Object[] advance()
{
final SkipMap<K,V> map2 = this.map;
@Nullable Object[] node = null;
if (map2 != null)
{
node = this.nextNode;
if (node != null)
{
this.lastKey = map2.getNodeKey(node);
this.nextNode = map2.getNextNode(node);
return node;
}
else
{
this.lastKey = null;
}
}
return node;
}
}
public static class KeyIterator<K,V> extends AbstractSkipList.KeyIterator<K>
implements ReleasableIterator<K>
{
private static final ThreadLocal<KeyIterator<?,?>> reusableInstance= new ThreadLocal<KeyIterator<?,?>>();
public KeyIterator(@Nullable SkipMap<K,V> map)
{
super(map);
}
public static <K,V> KeyIterator<K,V> make(@Nullable SkipMap<K,V> map)
{
@SuppressWarnings("unchecked")
KeyIterator<K,V> iter = (KeyIterator<K, V>) KeyIterator.reusableInstance.get();
if (iter != null)
{
KeyIterator.reusableInstance.set(null);
iter.reset(map);
}
else
{
iter = new KeyIterator<K,V>(map);
}
return iter;
}
@Override
public void release()
{
if (KeyIterator.reusableInstance.get() == null)
{
this.reset(null);
KeyIterator.reusableInstance.set(this);
}
}
public void reset(@Nullable SkipMap<K,V> map)
{
super.reset(map);
}
}
public static class ValueIterator<K,V> implements ReleasableIterator<V>
{
/*
* State
*/
/** The underlying set to be iterated over. This may be null. */
protected @Nullable SkipMap<K,V> map;
/** The next node to be returned by {@link #next}. Null when there are no more nodes. */
private @Nullable Object[] nextNode;
/** The last key returned by {@link #next}. Could be null. */
private @Nullable K lastKey;
private static final ThreadLocal<ValueIterator<?,?>> reusableInstance = new ThreadLocal<ValueIterator<?,?>>();
/*
* Construction/initialization methods
*/
public ValueIterator(@Nullable SkipMap<K,V> map)
{
this.reset(map);
}
public static <K,V> ValueIterator<K,V> make(@Nullable SkipMap<K,V> map)
{
@SuppressWarnings("unchecked")
ValueIterator<K,V> iter = (ValueIterator<K, V>) ValueIterator.reusableInstance.get();
if (iter != null)
{
ValueIterator.reusableInstance.set(null);
iter.reset(map);
}
else
{
iter = new ValueIterator<K,V>(map);
}
return iter;
}
@Override
public void release()
{
if (ValueIterator.reusableInstance.get() == null)
{
this.reset(null);
ValueIterator.reusableInstance.set(this);
}
}
/**
* Resets iterator back to beginning of set.
*/
public void reset()
{
final @Nullable SkipMap<K,V> map2 = this.map;
this.nextNode = map2 == null ? null : map2.getNextNode(map2.head);
}
/**
* Resets iterator to beginning of {@code newList}, which may be null.
*/
public void reset(@Nullable SkipMap<K,V> newMap)
{
this.map = newMap;
this.reset();
}
/*
* java.util.Iterator methods
*/
/**
* Returns true if {@link #next} method will return a non-null value.
*/
@Override
public boolean hasNext()
{
return this.nextNode != null;
}
/**
* Returns the next element in the iteration or null if at the end of the list.
* It is not necessary to invoke {@link #hasNext} before calling this method.
*/
@Override
public @Nullable V next()
{
V value = null;
final SkipMap<K,V> map2 = map;
if (map2 != null)
{
K key = null;
Object[] n = this.nextNode;
if (n != null)
{
value = map2.getNodeValue(n);
key = map2.getNodeKey(n);
this.nextNode = map2.getNextNode(n);
}
this.lastKey = key;
}
return value;
}
/**
* Removes the entry that was most recently returned by the {@link #next} method.
* Costs O(log n).
*/
@Override
public void remove()
{
final SkipMap<K,V> map2 = map;
if (map2 == null)
{
throw new IllegalStateException();
}
map2.removeNode(this.lastKey);
this.lastKey = null;
}
}
}