Remove unneeded code and beautify HashPMap and its dependencies
This commit is contained in:
@@ -1,210 +1,141 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.AbstractSequentialList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
*
|
||||
* A simple persistent stack of non-null values.
|
||||
* <p>
|
||||
* This implementation is thread-safe (assuming Java's AbstractSequentialList is thread-safe),
|
||||
* although its iterators may not be.
|
||||
*
|
||||
* @author harold
|
||||
* <p/>
|
||||
* This implementation is thread-safe, although its iterators may not be.
|
||||
*
|
||||
* @param <E>
|
||||
* @author harold
|
||||
*/
|
||||
public final class ConsPStack<E> extends AbstractSequentialList<E> implements PStack<E> {
|
||||
//// STATIC FACTORY METHODS ////
|
||||
private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>();
|
||||
|
||||
/**
|
||||
* @param <E>
|
||||
* @return an empty stack
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> ConsPStack<E> empty() {
|
||||
return (ConsPStack<E>)EMPTY; }
|
||||
|
||||
/**
|
||||
* @param <E>
|
||||
* @param e
|
||||
* @return empty().plus(e)
|
||||
*/
|
||||
public static <E> ConsPStack<E> singleton(final E e) {
|
||||
return ConsPStack.<E>empty().plus(e); }
|
||||
|
||||
/**
|
||||
* @param <E>
|
||||
* @param list
|
||||
* @return a stack consisting of the elements of list in the order of list.iterator()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> ConsPStack<E> from(final Collection<? extends E> list) {
|
||||
if(list instanceof ConsPStack)
|
||||
return (ConsPStack<E>)list; //(actually we only know it's ConsPStack<? extends E>)
|
||||
// but that's good enough for an immutable
|
||||
// (i.e. we can't mess someone else up by adding the wrong type to it)
|
||||
return from(list.iterator());
|
||||
}
|
||||
|
||||
private static <E> ConsPStack<E> from(final Iterator<? extends E> i) {
|
||||
if(!i.hasNext()) return empty();
|
||||
E e = i.next();
|
||||
return from(i).plus(e);
|
||||
}
|
||||
public final class ConsPStack<E> implements PStack<E> {
|
||||
private static final ConsPStack<Object> EMPTY = new ConsPStack<Object>();
|
||||
|
||||
|
||||
//// PRIVATE CONSTRUCTORS ////
|
||||
private final E first; private final ConsPStack<E> rest;
|
||||
private final int size;
|
||||
// not externally instantiable (or subclassable):
|
||||
private ConsPStack() { // EMPTY constructor
|
||||
if(EMPTY!=null)
|
||||
throw new RuntimeException("empty constructor should only be used once");
|
||||
size = 0; first=null; rest=null;
|
||||
}
|
||||
private ConsPStack(final E first, final ConsPStack<E> rest) {
|
||||
this.first = first; this.rest = rest;
|
||||
|
||||
size = 1 + rest.size;
|
||||
}
|
||||
|
||||
|
||||
//// REQUIRED METHODS FROM AbstractSequentialList ////
|
||||
@Override
|
||||
public int size() {
|
||||
return size; }
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(final int index) {
|
||||
if(index<0 || index>size) throw new IndexOutOfBoundsException();
|
||||
|
||||
return new ListIterator<E>() {
|
||||
int i = index;
|
||||
ConsPStack<E> next = subList(index);
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> ConsPStack<E> empty() {
|
||||
return (ConsPStack<E>) EMPTY;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return next.size>0; }
|
||||
public boolean hasPrevious() {
|
||||
return i>0; }
|
||||
public int nextIndex() {
|
||||
return index; }
|
||||
public int previousIndex() {
|
||||
return index-1; }
|
||||
public E next() {
|
||||
E e = next.first;
|
||||
next = next.rest;
|
||||
return e;
|
||||
}
|
||||
public E previous() {
|
||||
System.err.println("ConsPStack.listIterator().previous() is inefficient, don't use it!");
|
||||
next = subList(index-1); // go from beginning...
|
||||
return next.first;
|
||||
}
|
||||
private final E first;
|
||||
private final ConsPStack<E> rest;
|
||||
private final int size;
|
||||
|
||||
public void add(final E o) {
|
||||
throw new UnsupportedOperationException(); }
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException(); }
|
||||
public void set(final E o) {
|
||||
throw new UnsupportedOperationException(); }
|
||||
};
|
||||
}
|
||||
private ConsPStack() { // EMPTY constructor
|
||||
size = 0;
|
||||
first = null;
|
||||
rest = null;
|
||||
}
|
||||
|
||||
private ConsPStack(E first, ConsPStack<E> rest) {
|
||||
this.first = first;
|
||||
this.rest = rest;
|
||||
this.size = 1 + rest.size;
|
||||
}
|
||||
|
||||
//// OVERRIDDEN METHODS FROM AbstractSequentialList ////
|
||||
@Override
|
||||
public ConsPStack<E> subList(final int start, final int end) {
|
||||
if(start<0 || end>size || start>end)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if(end==size) // want a substack
|
||||
return subList(start); // this is faster
|
||||
if(start==end) // want nothing
|
||||
return empty();
|
||||
if(start==0) // want the current element
|
||||
return new ConsPStack<E>(first, rest.subList(0, end-1));
|
||||
// otherwise, don't want the current element:
|
||||
return rest.subList(start-1, end-1);
|
||||
}
|
||||
|
||||
|
||||
//// IMPLEMENTED METHODS OF PStack ////
|
||||
public ConsPStack<E> plus(final E e) {
|
||||
return new ConsPStack<E>(e, this);
|
||||
}
|
||||
|
||||
public ConsPStack<E> plusAll(final Collection<? extends E> list) {
|
||||
ConsPStack<E> result = this;
|
||||
for(E e : list)
|
||||
result = result.plus(e);
|
||||
return result;
|
||||
}
|
||||
public E get(int index) {
|
||||
try {
|
||||
return listIterator(index).next();
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IndexOutOfBoundsException("Index: " + index);
|
||||
}
|
||||
}
|
||||
|
||||
public ConsPStack<E> plus(final int i, final E e) {
|
||||
if(i<0 || i>size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if(i==0) // insert at beginning
|
||||
return plus(e);
|
||||
return new ConsPStack<E>(first, rest.plus(i-1, e));
|
||||
}
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
public ConsPStack<E> plusAll(final int i, final Collection<? extends E> list) {
|
||||
// TODO inefficient if list.isEmpty()
|
||||
if(i<0 || i>size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if(i==0)
|
||||
return plusAll(list);
|
||||
return new ConsPStack<E>(first, rest.plusAll(i-1, list));
|
||||
}
|
||||
|
||||
public ConsPStack<E> minus(final Object e) {
|
||||
if(size==0)
|
||||
return this;
|
||||
if(first.equals(e)) // found it
|
||||
return rest; // don't recurse (only remove one)
|
||||
// otherwise keep looking:
|
||||
ConsPStack<E> newRest = rest.minus(e);
|
||||
if(newRest==rest) return this;
|
||||
return new ConsPStack<E>(first, newRest);
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public ConsPStack<E> minus(final int i) {
|
||||
return minus(get(i));
|
||||
}
|
||||
public ListIterator<E> listIterator(final int index) {
|
||||
if (index < 0 || index > size) throw new IndexOutOfBoundsException();
|
||||
|
||||
public ConsPStack<E> minusAll(final Collection<?> list) {
|
||||
if(size==0)
|
||||
return this;
|
||||
if(list.contains(first)) // get rid of current element
|
||||
return rest.minusAll(list); // recursively delete all
|
||||
// either way keep looking:
|
||||
ConsPStack<E> newRest = rest.minusAll(list);
|
||||
if(newRest==rest) return this;
|
||||
return new ConsPStack<E>(first, newRest);
|
||||
}
|
||||
|
||||
public ConsPStack<E> with(final int i, final E e) {
|
||||
if(i<0 || i>=size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if(i==0) {
|
||||
if(first.equals(e)) return this;
|
||||
return new ConsPStack<E>(e, rest);
|
||||
}
|
||||
ConsPStack<E> newRest = rest.with(i-1, e);
|
||||
if(newRest==rest) return this;
|
||||
return new ConsPStack<E>(first, newRest);
|
||||
}
|
||||
return new ListIterator<E>() {
|
||||
int i = index;
|
||||
ConsPStack<E> next = subList(index);
|
||||
|
||||
public ConsPStack<E> subList(final int start) {
|
||||
if(start<0 || start>size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if(start==0)
|
||||
return this;
|
||||
return rest.subList(start-1);
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next.size > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return index - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
E e = next.first;
|
||||
next = next.rest;
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E previous() {
|
||||
System.err.println("ConsPStack.listIterator().previous() is inefficient, don't use it!");
|
||||
next = subList(index - 1); // go from beginning...
|
||||
return next.first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(E o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(E o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsPStack<E> plus(E e) {
|
||||
return new ConsPStack<E>(e, this);
|
||||
}
|
||||
|
||||
public ConsPStack<E> minus(Object e) {
|
||||
if (size == 0) return this;
|
||||
if (first.equals(e)) // found it
|
||||
return rest; // don't recurse (only remove one)
|
||||
// otherwise keep looking:
|
||||
ConsPStack<E> newRest = rest.minus(e);
|
||||
if (newRest == rest) return this;
|
||||
return new ConsPStack<E>(first, newRest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsPStack<E> minus(int i) {
|
||||
return minus(get(i));
|
||||
}
|
||||
|
||||
public ConsPStack<E> subList(int start) {
|
||||
if (start < 0 || start > size)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if (start == 0)
|
||||
return this;
|
||||
return rest.subList(start - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,175 +1,87 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
|
||||
import static java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
*
|
||||
* A persistent map from non-null keys to non-null values.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This map uses a given integer map to map hashcodes to lists of elements
|
||||
* with the same hashcode. Thus if all elements have the same hashcode, performance
|
||||
* is reduced to that of an association list.
|
||||
* <p>
|
||||
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe),
|
||||
* although its iterators may not be.
|
||||
*
|
||||
* @author harold
|
||||
* <p/>
|
||||
* This implementation is thread-safe, although its iterators may not be.
|
||||
*
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @author harold
|
||||
*/
|
||||
public final class HashPMap<K,V> extends AbstractMap<K,V> implements PMap<K,V> {
|
||||
//// STATIC FACTORY METHODS ////
|
||||
/**
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @param intMap
|
||||
* @return a map backed by an empty version of intMap,
|
||||
* i.e. backed by intMap.minusAll(intMap.keySet())
|
||||
*/
|
||||
public static <K,V> HashPMap<K,V> empty(final PMap<Integer,PSequence<Entry<K,V>>> intMap) {
|
||||
return new HashPMap<K,V>(intMap.minusAll(intMap.keySet()), 0); }
|
||||
|
||||
public final class HashPMap<K, V> implements PMap<K, V> {
|
||||
public static final HashPMap<Object, Object> EMPTY = new HashPMap<Object, Object>(IntTreePMap.<PStack<Entry<Object, Object>>>empty(), 0);
|
||||
|
||||
//// PRIVATE CONSTRUCTORS ////
|
||||
private final PMap<Integer,PSequence<Entry<K,V>>> intMap;
|
||||
private final int size;
|
||||
// not externally instantiable (or subclassable):
|
||||
private HashPMap(final PMap<Integer,PSequence<Entry<K,V>>> intMap, final int size) {
|
||||
this.intMap = intMap; this.size = size; }
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <K, V> HashPMap<K, V> empty() {
|
||||
return (HashPMap<K, V>) HashPMap.EMPTY;
|
||||
}
|
||||
|
||||
|
||||
//// REQUIRED METHODS FROM AbstractMap ////
|
||||
// this cache variable is thread-safe since assignment in Java is atomic:
|
||||
private Set<Entry<K,V>> entrySet = null;
|
||||
@Override
|
||||
public Set<Entry<K,V>> entrySet() {
|
||||
if(entrySet==null)
|
||||
entrySet = new AbstractSet<Entry<K,V>>() {
|
||||
// REQUIRED METHODS OF AbstractSet //
|
||||
@Override
|
||||
public int size() {
|
||||
return size; }
|
||||
@Override
|
||||
public Iterator<Entry<K,V>> iterator() {
|
||||
return new SequenceIterator<Entry<K,V>>(intMap.values().iterator()); }
|
||||
// OVERRIDDEN METHODS OF AbstractSet //
|
||||
@Override
|
||||
public boolean contains(final Object e) {
|
||||
if(!(e instanceof Entry))
|
||||
return false;
|
||||
V value = get(((Entry<?,?>)e).getKey());
|
||||
return value!=null && value.equals(((Entry<?,?>)e).getValue());
|
||||
}
|
||||
};
|
||||
return entrySet;
|
||||
}
|
||||
private final IntTreePMap<PStack<Entry<K, V>>> intMap;
|
||||
private final int size;
|
||||
|
||||
|
||||
//// OVERRIDDEN METHODS FROM AbstractMap ////
|
||||
@Override
|
||||
public int size() {
|
||||
return size; }
|
||||
private HashPMap(IntTreePMap<PStack<Entry<K, V>>> intMap, int size) {
|
||||
this.intMap = intMap;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final Object key) {
|
||||
return keyIndexIn(getEntries(key.hashCode()), key) != -1; }
|
||||
|
||||
@Override
|
||||
public V get(final Object key) {
|
||||
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
|
||||
for(Entry<K,V> entry : entries)
|
||||
if(entry.getKey().equals(key))
|
||||
return entry.getValue();
|
||||
return null;
|
||||
}
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
//// IMPLEMENTED METHODS OF PMap////
|
||||
public HashPMap<K,V> plusAll(final Map<? extends K, ? extends V> map) {
|
||||
HashPMap<K,V> result = this;
|
||||
for(Entry<? extends K,? extends V> entry : map.entrySet())
|
||||
result = result.plus(entry.getKey(), entry.getValue());
|
||||
return result;
|
||||
}
|
||||
public boolean containsKey(Object key) {
|
||||
return keyIndexIn(getEntries(key.hashCode()), key) != -1;
|
||||
}
|
||||
|
||||
public HashPMap<K,V> minusAll(final Collection<?> keys) {
|
||||
HashPMap<K,V> result = this;
|
||||
for(Object key : keys)
|
||||
result = result.minus(key);
|
||||
return result;
|
||||
}
|
||||
|
||||
public HashPMap<K,V> plus(final K key, final V value) {
|
||||
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
|
||||
int size0 = entries.size(),
|
||||
i = keyIndexIn(entries, key);
|
||||
if(i!=-1) entries = entries.minus(i);
|
||||
entries = entries.plus(new SimpleImmutableEntry<K,V>(key, value));
|
||||
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries),
|
||||
size-size0+entries.size());
|
||||
}
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
PStack<Entry<K, V>> entries = getEntries(key.hashCode());
|
||||
for (Entry<K, V> entry : entries)
|
||||
if (entry.getKey().equals(key))
|
||||
return entry.getValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
public HashPMap<K,V> minus(final Object key) {
|
||||
PSequence<Entry<K,V>> entries = getEntries(key.hashCode());
|
||||
int i = keyIndexIn(entries, key);
|
||||
if(i==-1) // key not in this
|
||||
return this;
|
||||
entries = entries.minus(i);
|
||||
if(entries.size()==0) // get rid of the entire hash entry
|
||||
return new HashPMap<K,V>(intMap.minus(key.hashCode()),
|
||||
size-1);
|
||||
// otherwise replace hash entry with new smaller one:
|
||||
return new HashPMap<K,V>(intMap.plus(key.hashCode(), entries),
|
||||
size-1);
|
||||
}
|
||||
|
||||
|
||||
//// PRIVATE UTILITIES ////
|
||||
private PSequence<Entry<K,V>> getEntries(final int hash) {
|
||||
PSequence<Entry<K,V>> entries = intMap.get(hash);
|
||||
if(entries==null) return ConsPStack.empty();
|
||||
return entries;
|
||||
}
|
||||
@Override
|
||||
public HashPMap<K, V> plus(K key, V value) {
|
||||
PStack<Entry<K, V>> entries = getEntries(key.hashCode());
|
||||
int size0 = entries.size();
|
||||
int i = keyIndexIn(entries, key);
|
||||
if (i != -1) entries = entries.minus(i);
|
||||
entries = entries.plus(new SimpleImmutableEntry<K, V>(key, value));
|
||||
return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - size0 + entries.size());
|
||||
}
|
||||
|
||||
|
||||
//// PRIVATE STATIC UTILITIES ////
|
||||
private static <K,V> int keyIndexIn(final PSequence<Entry<K,V>> entries, final Object key) {
|
||||
int i=0;
|
||||
for(Entry<K,V> entry : entries) {
|
||||
if(entry.getKey().equals(key))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static class SequenceIterator<E> implements Iterator<E> {
|
||||
private final Iterator<PSequence<E>> i;
|
||||
private PSequence<E> seq = ConsPStack.empty();
|
||||
SequenceIterator(Iterator<PSequence<E>> i) {
|
||||
this.i = i; }
|
||||
@Override
|
||||
public HashPMap<K, V> minus(Object key) {
|
||||
PStack<Entry<K, V>> entries = getEntries(key.hashCode());
|
||||
int i = keyIndexIn(entries, key);
|
||||
if (i == -1) // key not in this
|
||||
return this;
|
||||
entries = entries.minus(i);
|
||||
if (entries.size() == 0) // get rid of the entire hash entry
|
||||
return new HashPMap<K, V>(intMap.minus(key.hashCode()), size - 1);
|
||||
// otherwise replace hash entry with new smaller one:
|
||||
return new HashPMap<K, V>(intMap.plus(key.hashCode(), entries), size - 1);
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return seq.size()>0 || i.hasNext(); }
|
||||
private PStack<Entry<K, V>> getEntries(int hash) {
|
||||
PStack<Entry<K, V>> entries = intMap.get(hash);
|
||||
if (entries == null) return ConsPStack.empty();
|
||||
return entries;
|
||||
}
|
||||
|
||||
public E next() {
|
||||
if(seq.size()==0)
|
||||
seq = i.next();
|
||||
final E result = seq.get(0);
|
||||
seq = seq.subList(1, seq.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException(); }
|
||||
}
|
||||
private static <K, V> int keyIndexIn(PStack<Entry<K, V>> entries, Object key) {
|
||||
int i = 0;
|
||||
for (Entry<K, V> entry : entries) {
|
||||
if (entry.getKey().equals(key))
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* A static convenience class for creating efficient persistent maps.
|
||||
* <p>
|
||||
* This class simply creates HashPMaps backed by IntTreePMaps.
|
||||
*
|
||||
* @author harold
|
||||
*/
|
||||
public final class HashTreePMap {
|
||||
// not instantiable (or subclassable):
|
||||
private HashTreePMap() {}
|
||||
|
||||
private static final HashPMap<Object,Object> EMPTY
|
||||
= HashPMap.empty(IntTreePMap.<PSequence<Entry<Object,Object>>>empty());
|
||||
|
||||
/**
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @return an empty map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <K,V> HashPMap<K,V> empty() {
|
||||
return (HashPMap<K,V>)EMPTY; }
|
||||
|
||||
/**
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @param key
|
||||
* @param value
|
||||
* @return empty().plus(key, value)
|
||||
*/
|
||||
public static <K,V> HashPMap<K,V> singleton(final K key, final V value) {
|
||||
return HashTreePMap.<K,V>empty().plus(key, value); }
|
||||
|
||||
/**
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @param map
|
||||
* @return empty().plusAll(map)
|
||||
*/
|
||||
public static <K,V> HashPMap<K,V> from(final Map<? extends K, ? extends V> map) {
|
||||
return HashTreePMap.<K,V>empty().plusAll(map); }
|
||||
}
|
||||
@@ -1,309 +1,248 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* A non-public utility class for persistent balanced tree maps with integer keys.
|
||||
* <p>
|
||||
* <p/>
|
||||
* To allow for efficiently increasing all keys above a certain value or decreasing
|
||||
* all keys below a certain value, the keys values are stored relative to their parent.
|
||||
* This makes this map a good backing for fast insertion and removal of indices in a
|
||||
* vector.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This implementation is thread-safe except for its iterators.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Other than that, this tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
|
||||
* which in turn is based on "size balanced binary trees" as described by:
|
||||
* <p>
|
||||
* <p/>
|
||||
* Stephen Adams, "Efficient sets: a balancing act",
|
||||
* Journal of Functional Programming 3(4):553-562, October 1993,
|
||||
* http://www.swiss.ai.mit.edu/~adams/BB/.
|
||||
* <p>
|
||||
* <p/>
|
||||
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
|
||||
* SIAM journal of computing 2(1), March 1973.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <V>
|
||||
* @author harold
|
||||
*/
|
||||
class IntTree<V> {
|
||||
// marker value:
|
||||
static final IntTree<Object> EMPTYNODE = new IntTree<Object>();
|
||||
|
||||
private final long key; // we use longs so relative keys can express all ints
|
||||
// (e.g. if this has key -10 and right has 'absolute' key MAXINT,
|
||||
// then its relative key is MAXINT+10 which overflows)
|
||||
// there might be some way to deal with this based on left-verse-right logic,
|
||||
// but that sounds like a mess.
|
||||
private final V value; // null value means this is empty node
|
||||
private final IntTree<V> left, right;
|
||||
private final int size;
|
||||
private IntTree() {
|
||||
if(EMPTYNODE!=null)
|
||||
throw new RuntimeException("empty constructor should only be used once");
|
||||
size = 0;
|
||||
|
||||
key=0; value=null; left=null; right=null;
|
||||
}
|
||||
private IntTree(final long key, final V value, final IntTree<V> left, final IntTree<V> right) {
|
||||
this.key = key; this.value = value;
|
||||
this.left = left; this.right = right;
|
||||
size = 1 + left.size + right.size;
|
||||
}
|
||||
|
||||
private IntTree<V> withKey(final long newKey) {
|
||||
if(size==0 || newKey==key) return this;
|
||||
return new IntTree<V>(newKey, value, left, right); }
|
||||
|
||||
Iterator<Entry<Integer,V>> iterator() {
|
||||
return new EntryIterator<V>(this); }
|
||||
|
||||
int size() {
|
||||
return size; }
|
||||
final class IntTree<V> {
|
||||
// marker value:
|
||||
static final IntTree<Object> EMPTYNODE = new IntTree<Object>();
|
||||
|
||||
boolean containsKey(final long key) {
|
||||
if(size==0)
|
||||
return false;
|
||||
if(key < this.key)
|
||||
return left.containsKey(key-this.key);
|
||||
if(key > this.key)
|
||||
return right.containsKey(key-this.key);
|
||||
// otherwise key==this.key:
|
||||
return true;
|
||||
}
|
||||
|
||||
V get(final long key) {
|
||||
if(size==0)
|
||||
return null;
|
||||
if(key < this.key)
|
||||
return left.get(key-this.key);
|
||||
if(key > this.key)
|
||||
return right.get(key-this.key);
|
||||
// otherwise key==this.key:
|
||||
return value;
|
||||
}
|
||||
// we use longs so relative keys can express all ints
|
||||
// (e.g. if this has key -10 and right has 'absolute' key MAXINT,
|
||||
// then its relative key is MAXINT+10 which overflows)
|
||||
// there might be some way to deal with this based on left-verse-right logic,
|
||||
// but that sounds like a mess.
|
||||
private final long key;
|
||||
private final V value; // null value means this is empty node
|
||||
private final IntTree<V> left, right;
|
||||
private final int size;
|
||||
|
||||
IntTree<V> plus(final long key, final V value) {
|
||||
if(size==0)
|
||||
return new IntTree<V>(key, value, this, this);
|
||||
if(key < this.key)
|
||||
return rebalanced(left.plus(key-this.key, value), right);
|
||||
if(key > this.key)
|
||||
return rebalanced(left, right.plus(key-this.key, value));
|
||||
// otherwise key==this.key, so we simply replace this, with no effect on balance:
|
||||
if(value==this.value)
|
||||
return this;
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
private IntTree() {
|
||||
size = 0;
|
||||
key = 0;
|
||||
value = null;
|
||||
left = null;
|
||||
right = null;
|
||||
}
|
||||
|
||||
IntTree<V> minus(final long key) {
|
||||
if(size==0)
|
||||
return this;
|
||||
if(key < this.key)
|
||||
return rebalanced(left.minus(key-this.key), right);
|
||||
if(key > this.key)
|
||||
return rebalanced(left, right.minus(key-this.key));
|
||||
private IntTree(long key, V value, IntTree<V> left, IntTree<V> right) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
size = 1 + left.size + right.size;
|
||||
}
|
||||
|
||||
// otherwise key==this.key, so we are killing this node:
|
||||
private IntTree<V> withKey(long newKey) {
|
||||
if (size == 0 || newKey == key) return this;
|
||||
return new IntTree<V>(newKey, value, left, right);
|
||||
}
|
||||
|
||||
if(left.size==0) // we can just become right node
|
||||
// make key 'absolute':
|
||||
return right.withKey(right.key+this.key);
|
||||
if(right.size==0) // we can just become left node
|
||||
return left.withKey(left.key+this.key);
|
||||
boolean containsKey(long key) {
|
||||
if (size == 0)
|
||||
return false;
|
||||
if (key < this.key)
|
||||
return left.containsKey(key - this.key);
|
||||
if (key > this.key)
|
||||
return right.containsKey(key - this.key);
|
||||
// otherwise key==this.key:
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise replace this with the next key (i.e. the smallest key to the right):
|
||||
|
||||
// TODO have minNode() instead of minKey to avoid having to call get()
|
||||
// TODO get node from larger subtree, i.e. if left.size>right.size use left.maxNode()
|
||||
// TODO have faster minusMin() instead of just using minus()
|
||||
|
||||
long newKey = right.minKey() + this.key;
|
||||
//(right.minKey() is relative to this; adding this.key makes it 'absolute'
|
||||
// where 'absolute' really means relative to the parent of this)
|
||||
V get(long key) {
|
||||
if (size == 0)
|
||||
return null;
|
||||
if (key < this.key)
|
||||
return left.get(key - this.key);
|
||||
if (key > this.key)
|
||||
return right.get(key - this.key);
|
||||
// otherwise key==this.key:
|
||||
return value;
|
||||
}
|
||||
|
||||
V newValue = right.get(newKey-this.key);
|
||||
// now that we've got the new stuff, take it out of the right subtree:
|
||||
IntTree<V> newRight = right.minus(newKey-this.key);
|
||||
IntTree<V> plus(long key, V value) {
|
||||
if (size == 0)
|
||||
return new IntTree<V>(key, value, this, this);
|
||||
if (key < this.key)
|
||||
return rebalanced(left.plus(key - this.key, value), right);
|
||||
if (key > this.key)
|
||||
return rebalanced(left, right.plus(key - this.key, value));
|
||||
// otherwise key==this.key, so we simply replace this, with no effect on balance:
|
||||
if (value == this.value)
|
||||
return this;
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
|
||||
// lastly, make the subtree keys relative to newKey (currently they are relative to this.key):
|
||||
newRight = newRight.withKey( (newRight.key+this.key) - newKey );
|
||||
// left is definitely not empty:
|
||||
IntTree<V> newLeft = left.withKey( (left.key+this.key) - newKey );
|
||||
|
||||
return rebalanced(newKey, newValue, newLeft, newRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes every key k>=key to k+delta.
|
||||
*
|
||||
* This method will create an _invalid_ tree if delta<0
|
||||
* and the distance between the smallest k>=key in this
|
||||
* and the largest j<key in this is |delta| or less.
|
||||
*
|
||||
* In other words, this method must not result in any change
|
||||
* in the order of the keys in this, since the tree structure is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysAbove(final long key, final int delta) {
|
||||
if(size==0 || delta==0)
|
||||
return this;
|
||||
IntTree<V> minus(long key) {
|
||||
if (size == 0)
|
||||
return this;
|
||||
if (key < this.key)
|
||||
return rebalanced(left.minus(key - this.key), right);
|
||||
if (key > this.key)
|
||||
return rebalanced(left, right.minus(key - this.key));
|
||||
|
||||
if(this.key>=key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this smaller than key,
|
||||
// all of which are to the left. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key+delta, value, left.changeKeysBelow(key-this.key, -delta), right);
|
||||
// otherwise key==this.key, so we are killing this node:
|
||||
|
||||
// otherwise, doesn't apply yet, look to the right:
|
||||
IntTree<V> newRight = right.changeKeysAbove(key-this.key, delta);
|
||||
if(newRight==right) return this;
|
||||
return new IntTree<V>(this.key, value, left, newRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes every key k<key to k+delta.
|
||||
*
|
||||
* This method will create an _invalid_ tree if delta>0
|
||||
* and the distance between the largest k<key in this
|
||||
* and the smallest j>=key in this is delta or less.
|
||||
*
|
||||
* In other words, this method must not result in any overlap or change
|
||||
* in the order of the keys in this, since the tree _structure_ is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysBelow(final long key, final int delta) {
|
||||
if(size==0 || delta==0)
|
||||
return this;
|
||||
if (left.size == 0) // we can just become right node
|
||||
// make key 'absolute':
|
||||
return right.withKey(right.key + this.key);
|
||||
if (right.size == 0) // we can just become left node
|
||||
return left.withKey(left.key + this.key);
|
||||
|
||||
if(this.key<key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this larger than key,
|
||||
// all of which are to the right. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key+delta, value, left, right.changeKeysAbove(key-this.key, -delta));
|
||||
// otherwise replace this with the next key (i.e. the smallest key to the right):
|
||||
|
||||
// otherwise, doesn't apply yet, look to the left:
|
||||
IntTree<V> newLeft = left.changeKeysBelow(key-this.key, delta);
|
||||
if(newLeft==left) return this;
|
||||
return new IntTree<V>(this.key, value, newLeft, right);
|
||||
}
|
||||
|
||||
// min key in this:
|
||||
private long minKey() {
|
||||
if(left.size==0)
|
||||
return key;
|
||||
// make key 'absolute' (i.e. relative to the parent of this):
|
||||
return left.minKey() + this.key;
|
||||
}
|
||||
// TODO have minNode() instead of minKey to avoid having to call get()
|
||||
// TODO get node from larger subtree, i.e. if left.size>right.size use left.maxNode()
|
||||
// TODO have faster minusMin() instead of just using minus()
|
||||
|
||||
private IntTree<V> rebalanced(final IntTree<V> newLeft, final IntTree<V> newRight) {
|
||||
if(newLeft==left && newRight==right)
|
||||
return this; // already balanced
|
||||
return rebalanced(key, value, newLeft, newRight);
|
||||
}
|
||||
long newKey = right.minKey() + this.key;
|
||||
//(right.minKey() is relative to this; adding this.key makes it 'absolute'
|
||||
// where 'absolute' really means relative to the parent of this)
|
||||
|
||||
private static final int OMEGA = 5;
|
||||
private static final int ALPHA = 2;
|
||||
// rebalance a tree that is off-balance by at most 1:
|
||||
private static <V> IntTree<V> rebalanced(final long key, final V value,
|
||||
final IntTree<V> left, final IntTree<V> right) {
|
||||
if(left.size + right.size > 1) {
|
||||
if(left.size >= OMEGA*right.size) { // rotate to the right
|
||||
IntTree<V> ll = left.left, lr = left.right;
|
||||
if(lr.size < ALPHA*ll.size) // single rotation
|
||||
return new IntTree<V>(left.key+key, left.value,
|
||||
ll,
|
||||
new IntTree<V>(-left.key, value,
|
||||
lr.withKey(lr.key+left.key),
|
||||
right));
|
||||
else { // double rotation:
|
||||
IntTree<V> lrl = lr.left, lrr = lr.right;
|
||||
return new IntTree<V>(lr.key+left.key+key, lr.value,
|
||||
new IntTree<V>(-lr.key, left.value,
|
||||
ll,
|
||||
lrl.withKey(lrl.key+lr.key)),
|
||||
new IntTree<V>(-left.key-lr.key, value,
|
||||
lrr.withKey(lrr.key+lr.key+left.key),
|
||||
right));
|
||||
}
|
||||
}
|
||||
else if(right.size >= OMEGA*left.size) { // rotate to the left
|
||||
IntTree<V> rl = right.left, rr = right.right;
|
||||
if(rl.size < ALPHA*rr.size) // single rotation
|
||||
return new IntTree<V>(right.key+key, right.value,
|
||||
new IntTree<V>(-right.key, value,
|
||||
left,
|
||||
rl.withKey(rl.key+right.key)),
|
||||
rr);
|
||||
else { // double rotation:
|
||||
IntTree<V> rll = rl.left, rlr = rl.right;
|
||||
return new IntTree<V>(rl.key+right.key+key, rl.value,
|
||||
new IntTree<V>(-right.key-rl.key, value,
|
||||
left,
|
||||
rll.withKey(rll.key+rl.key+right.key)),
|
||||
new IntTree<V>(-rl.key, right.value,
|
||||
rlr.withKey(rlr.key+rl.key),
|
||||
rr));
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise already balanced enough:
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
V newValue = right.get(newKey - this.key);
|
||||
// now that we've got the new stuff, take it out of the right subtree:
|
||||
IntTree<V> newRight = right.minus(newKey - this.key);
|
||||
|
||||
|
||||
////entrySet().iterator() IMPLEMENTATION ////
|
||||
// TODO make this a ListIterator?
|
||||
private static final class EntryIterator<V> implements Iterator<Entry<Integer,V>> {
|
||||
private PStack<IntTree<V>> stack = ConsPStack.empty(); //path of nonempty nodes
|
||||
private int key = 0; // note we use _int_ here since this is a truly absolute key
|
||||
|
||||
EntryIterator(final IntTree<V> root) {
|
||||
gotoMinOf(root); }
|
||||
|
||||
public boolean hasNext() {
|
||||
return stack.size()>0; }
|
||||
|
||||
public Entry<Integer,V> next() {
|
||||
IntTree<V> node = stack.get(0);
|
||||
final Entry<Integer,V> result = new SimpleImmutableEntry<Integer,V>(key, node.value);
|
||||
|
||||
// find next node.
|
||||
// we've already done everything smaller,
|
||||
// so try least larger node:
|
||||
|
||||
if(node.right.size>0) // we can descend to the right
|
||||
gotoMinOf(node.right);
|
||||
|
||||
else // can't descend to the right -- try ascending to the right
|
||||
while (true) { // find current node's least larger ancestor, if any
|
||||
key -= node.key; // revert to parent's key
|
||||
stack = stack.subList(1); // climb up to parent
|
||||
// if parent was larger than child or there was no parent, we're done:
|
||||
if(node.key<0 || stack.size()==0)
|
||||
break;
|
||||
// otherwise parent was smaller -- try its parent:
|
||||
node = stack.get(0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// lastly, make the subtree keys relative to newKey (currently they are relative to this.key):
|
||||
newRight = newRight.withKey((newRight.key + this.key) - newKey);
|
||||
// left is definitely not empty:
|
||||
IntTree<V> newLeft = left.withKey((left.key + this.key) - newKey);
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException(); }
|
||||
return rebalanced(newKey, newValue, newLeft, newRight);
|
||||
}
|
||||
|
||||
// extend the stack to its least non-empty node:
|
||||
private void gotoMinOf(IntTree<V> node) {
|
||||
while(node.size>0) {
|
||||
stack = stack.plus(node);
|
||||
key += node.key;
|
||||
node = node.left;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Changes every key k>=key to k+delta.
|
||||
* <p/>
|
||||
* This method will create an _invalid_ tree if delta<0
|
||||
* and the distance between the smallest k>=key in this
|
||||
* and the largest j<key in this is |delta| or less.
|
||||
* <p/>
|
||||
* In other words, this method must not result in any change
|
||||
* in the order of the keys in this, since the tree structure is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysAbove(long key, int delta) {
|
||||
if (size == 0 || delta == 0)
|
||||
return this;
|
||||
|
||||
if (this.key >= key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this smaller than key,
|
||||
// all of which are to the left. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key + delta, value, left.changeKeysBelow(key - this.key, -delta), right);
|
||||
|
||||
// otherwise, doesn't apply yet, look to the right:
|
||||
IntTree<V> newRight = right.changeKeysAbove(key - this.key, delta);
|
||||
if (newRight == right) return this;
|
||||
return new IntTree<V>(this.key, value, left, newRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes every key k<key to k+delta.
|
||||
* <p/>
|
||||
* This method will create an _invalid_ tree if delta>0
|
||||
* and the distance between the largest k<key in this
|
||||
* and the smallest j>=key in this is delta or less.
|
||||
* <p/>
|
||||
* In other words, this method must not result in any overlap or change
|
||||
* in the order of the keys in this, since the tree _structure_ is
|
||||
* not being changed at all.
|
||||
*/
|
||||
IntTree<V> changeKeysBelow(long key, int delta) {
|
||||
if (size == 0 || delta == 0)
|
||||
return this;
|
||||
|
||||
if (this.key < key)
|
||||
// adding delta to this.key changes the keys of _all_ children of this,
|
||||
// so we now need to un-change the children of this larger than key,
|
||||
// all of which are to the right. note that we still use the 'old' relative key...:
|
||||
return new IntTree<V>(this.key + delta, value, left, right.changeKeysAbove(key - this.key, -delta));
|
||||
|
||||
// otherwise, doesn't apply yet, look to the left:
|
||||
IntTree<V> newLeft = left.changeKeysBelow(key - this.key, delta);
|
||||
if (newLeft == left) return this;
|
||||
return new IntTree<V>(this.key, value, newLeft, right);
|
||||
}
|
||||
|
||||
// min key in this:
|
||||
private long minKey() {
|
||||
if (left.size == 0)
|
||||
return key;
|
||||
// make key 'absolute' (i.e. relative to the parent of this):
|
||||
return left.minKey() + this.key;
|
||||
}
|
||||
|
||||
private IntTree<V> rebalanced(IntTree<V> newLeft, IntTree<V> newRight) {
|
||||
if (newLeft == left && newRight == right)
|
||||
return this; // already balanced
|
||||
return rebalanced(key, value, newLeft, newRight);
|
||||
}
|
||||
|
||||
private static final int OMEGA = 5;
|
||||
private static final int ALPHA = 2;
|
||||
|
||||
// rebalance a tree that is off-balance by at most 1:
|
||||
private static <V> IntTree<V> rebalanced(long key, V value, IntTree<V> left, IntTree<V> right) {
|
||||
if (left.size + right.size > 1) {
|
||||
if (left.size >= OMEGA * right.size) { // rotate to the right
|
||||
IntTree<V> ll = left.left, lr = left.right;
|
||||
if (lr.size < ALPHA * ll.size) // single rotation
|
||||
return new IntTree<V>(left.key + key, left.value,
|
||||
ll,
|
||||
new IntTree<V>(-left.key, value,
|
||||
lr.withKey(lr.key + left.key),
|
||||
right));
|
||||
else { // double rotation:
|
||||
IntTree<V> lrl = lr.left, lrr = lr.right;
|
||||
return new IntTree<V>(lr.key + left.key + key, lr.value,
|
||||
new IntTree<V>(-lr.key, left.value,
|
||||
ll,
|
||||
lrl.withKey(lrl.key + lr.key)),
|
||||
new IntTree<V>(-left.key - lr.key, value,
|
||||
lrr.withKey(lrr.key + lr.key + left.key),
|
||||
right));
|
||||
}
|
||||
} else if (right.size >= OMEGA * left.size) { // rotate to the left
|
||||
IntTree<V> rl = right.left, rr = right.right;
|
||||
if (rl.size < ALPHA * rr.size) // single rotation
|
||||
return new IntTree<V>(right.key + key, right.value,
|
||||
new IntTree<V>(-right.key, value,
|
||||
left,
|
||||
rl.withKey(rl.key + right.key)),
|
||||
rr);
|
||||
else { // double rotation:
|
||||
IntTree<V> rll = rl.left, rlr = rl.right;
|
||||
return new IntTree<V>(rl.key + right.key + key, rl.value,
|
||||
new IntTree<V>(-right.key - rl.key, value,
|
||||
left,
|
||||
rll.withKey(rll.key + rl.key + right.key)),
|
||||
new IntTree<V>(-rl.key, right.value,
|
||||
rlr.withKey(rlr.key + rl.key),
|
||||
rr));
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise already balanced enough:
|
||||
return new IntTree<V>(key, value, left, right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,163 +1,52 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* An efficient persistent map from integer keys to non-null values.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Iteration occurs in the integer order of the keys.
|
||||
* <p>
|
||||
* This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe),
|
||||
* although its iterators may not be.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This implementation is thread-safe, although its iterators may not be.
|
||||
* <p/>
|
||||
* The balanced tree is based on the Glasgow Haskell Compiler's Data.Map implementation,
|
||||
* which in turn is based on "size balanced binary trees" as described by:
|
||||
* <p>
|
||||
* <p/>
|
||||
* Stephen Adams, "Efficient sets: a balancing act",
|
||||
* Journal of Functional Programming 3(4):553-562, October 1993,
|
||||
* http://www.swiss.ai.mit.edu/~adams/BB/.
|
||||
* <p>
|
||||
* <p/>
|
||||
* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance",
|
||||
* SIAM journal of computing 2(1), March 1973.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <V>
|
||||
* @author harold
|
||||
*/
|
||||
public final class IntTreePMap<V> extends AbstractMap<Integer,V> implements PMap<Integer,V> {
|
||||
//// STATIC FACTORY METHODS ////
|
||||
private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE);
|
||||
public final class IntTreePMap<V> {
|
||||
private static final IntTreePMap<Object> EMPTY = new IntTreePMap<Object>(IntTree.EMPTYNODE);
|
||||
|
||||
/**
|
||||
* @param <V>
|
||||
* @return an empty map
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V> IntTreePMap<V> empty() {
|
||||
return (IntTreePMap<V>)EMPTY; }
|
||||
|
||||
/**
|
||||
* @param <V>
|
||||
* @param key
|
||||
* @param value
|
||||
* @return empty().plus(key, value)
|
||||
*/
|
||||
public static <V> IntTreePMap<V> singleton(final Integer key, final V value) {
|
||||
return IntTreePMap.<V>empty().plus(key, value); }
|
||||
|
||||
/**
|
||||
* @param <V>
|
||||
* @param map
|
||||
* @return empty().plusAll(map)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V> IntTreePMap<V> from(final Map<? extends Integer, ? extends V> map) {
|
||||
if(map instanceof IntTreePMap)
|
||||
return (IntTreePMap<V>)map; //(actually we only know it's IntTreePMap<? extends V>)
|
||||
// but that's good enough for an immutable
|
||||
// (i.e. we can't mess someone else up by adding the wrong type to it)
|
||||
return IntTreePMap.<V>empty().plusAll(map); }
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <V> IntTreePMap<V> empty() {
|
||||
return (IntTreePMap<V>) EMPTY;
|
||||
}
|
||||
|
||||
//// PRIVATE CONSTRUCTORS ////
|
||||
private final IntTree<V> root;
|
||||
// not externally instantiable (or subclassable):
|
||||
private IntTreePMap(final IntTree<V> root) {
|
||||
this.root = root; }
|
||||
private IntTreePMap<V> withRoot(final IntTree<V> root) {
|
||||
if(root==this.root) return this;
|
||||
return new IntTreePMap<V>(root); }
|
||||
|
||||
private final IntTree<V> root;
|
||||
|
||||
//// UNINHERITED METHODS OF IntTreePMap ////
|
||||
IntTreePMap<V> withKeysChangedAbove(final int key, final int delta) {
|
||||
// TODO check preconditions of changeKeysAbove()
|
||||
// TODO make public?
|
||||
return withRoot( root.changeKeysAbove(key, delta) );
|
||||
}
|
||||
|
||||
IntTreePMap<V> withKeysChangedBelow(final int key, final int delta) {
|
||||
// TODO check preconditions of changeKeysAbove()
|
||||
// TODO make public?
|
||||
return withRoot( root.changeKeysBelow(key, delta) );
|
||||
}
|
||||
|
||||
//// REQUIRED METHODS FROM AbstractMap ////
|
||||
// this cache variable is thread-safe, since assignment in Java is atomic:
|
||||
private Set<Entry<Integer,V>> entrySet = null;
|
||||
@Override
|
||||
public Set<Entry<Integer,V>> entrySet() {
|
||||
if(entrySet==null)
|
||||
entrySet = new AbstractSet<Entry<Integer,V>>() {
|
||||
// REQUIRED METHODS OF AbstractSet //
|
||||
@Override
|
||||
public int size() { // same as Map
|
||||
return IntTreePMap.this.size(); }
|
||||
@Override
|
||||
public Iterator<Entry<Integer,V>> iterator() {
|
||||
return root.iterator(); }
|
||||
// OVERRIDDEN METHODS OF AbstractSet //
|
||||
@Override
|
||||
public boolean contains(final Object e) {
|
||||
if(!(e instanceof Entry))
|
||||
return false;
|
||||
V value = get(((Entry<?,?>)e).getKey());
|
||||
return value!=null && value.equals(((Entry<?,?>)e).getValue());
|
||||
}
|
||||
};
|
||||
return entrySet;
|
||||
}
|
||||
private IntTreePMap(IntTree<V> root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
|
||||
//// OVERRIDDEN METHODS FROM AbstractMap ////
|
||||
@Override
|
||||
public int size() {
|
||||
return root.size(); }
|
||||
private IntTreePMap<V> withRoot(IntTree<V> root) {
|
||||
if (root == this.root) return this;
|
||||
return new IntTreePMap<V>(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final Object key) {
|
||||
if(!(key instanceof Integer))
|
||||
return false;
|
||||
return root.containsKey((Integer)key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(final Object key) {
|
||||
if(!(key instanceof Integer))
|
||||
return null;
|
||||
return root.get((Integer)key);
|
||||
}
|
||||
public V get(int key) {
|
||||
return root.get(key);
|
||||
}
|
||||
|
||||
|
||||
//// IMPLEMENTED METHODS OF PMap////
|
||||
public IntTreePMap<V> plus(final Integer key, final V value) {
|
||||
return withRoot( root.plus(key, value) ); }
|
||||
|
||||
public IntTreePMap<V> minus(final Object key) {
|
||||
if(!(key instanceof Integer)) return this;
|
||||
return withRoot( root.minus((Integer)key) ); }
|
||||
|
||||
public IntTreePMap<V> plusAll(final Map<? extends Integer, ? extends V> map) {
|
||||
IntTree<V> root = this.root;
|
||||
for(Entry<? extends Integer,? extends V> entry : map.entrySet())
|
||||
root = root.plus(entry.getKey(), entry.getValue());
|
||||
return withRoot(root);
|
||||
}
|
||||
public IntTreePMap<V> plus(int key, V value) {
|
||||
return withRoot(root.plus(key, value));
|
||||
}
|
||||
|
||||
public IntTreePMap<V> minusAll(final Collection<?> keys) {
|
||||
IntTree<V> root = this.root;
|
||||
for(Object key : keys)
|
||||
if(key instanceof Integer)
|
||||
root = root.minus((Integer)key);
|
||||
return withRoot(root);
|
||||
}
|
||||
public IntTreePMap<V> minus(int key) {
|
||||
return withRoot(root.minus(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
* An immutable, persistent collection of non-null elements of type E.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface PCollection<E> extends Collection<E> {
|
||||
|
||||
/**
|
||||
* @param e non-null
|
||||
* @return a collection which contains e and all of the elements of this
|
||||
*/
|
||||
public PCollection<E> plus(E e);
|
||||
|
||||
/**
|
||||
* @param list contains no null elements
|
||||
* @return a collection which contains all of the elements of list and this
|
||||
*/
|
||||
public PCollection<E> plusAll(Collection<? extends E> list);
|
||||
|
||||
/**
|
||||
* @param e
|
||||
* @return this with a single instance of e removed, if e is in this
|
||||
*/
|
||||
public PCollection<E> minus(Object e);
|
||||
|
||||
/**
|
||||
* @param list
|
||||
* @return this with all elements of list completely removed
|
||||
*/
|
||||
public PCollection<E> minusAll(Collection<?> list);
|
||||
|
||||
// TODO public PCollection<E> retainingAll(Collection<?> list);
|
||||
|
||||
@Deprecated boolean add(E o);
|
||||
@Deprecated boolean remove(Object o);
|
||||
@Deprecated boolean addAll(Collection<? extends E> c);
|
||||
@Deprecated boolean removeAll(Collection<?> c);
|
||||
@Deprecated boolean retainAll(Collection<?> c);
|
||||
@Deprecated void clear();
|
||||
}
|
||||
@@ -1,45 +1,14 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* An immutable, persistent map from non-null keys of type K to non-null values of type V.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @author harold
|
||||
*/
|
||||
public interface PMap<K,V> extends Map<K,V> {
|
||||
/**
|
||||
* @param key non-null
|
||||
* @param value non-null
|
||||
* @return a map with the mappings of this but with key mapped to value
|
||||
*/
|
||||
public PMap<K,V> plus(K key, V value);
|
||||
|
||||
/**
|
||||
* @param map
|
||||
* @return this combined with map, with map's mappings used for any keys in both map and this
|
||||
*/
|
||||
public PMap<K,V> plusAll(Map<? extends K, ? extends V> map);
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @return a map with the mappings of this but with no value for key
|
||||
*/
|
||||
public PMap<K,V> minus(Object key);
|
||||
|
||||
/**
|
||||
* @param keys
|
||||
* @return a map with the mappings of this but with no value for any element of keys
|
||||
*/
|
||||
public PMap<K,V> minusAll(Collection<?> keys);
|
||||
|
||||
@Deprecated V put(K k, V v);
|
||||
@Deprecated V remove(Object k);
|
||||
@Deprecated void putAll(Map<? extends K, ? extends V> m);
|
||||
@Deprecated void clear();
|
||||
public interface PMap<K, V> {
|
||||
public PMap<K, V> plus(K key, V value);
|
||||
|
||||
public PMap<K, V> minus(Object key);
|
||||
|
||||
V get(Object key);
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* An immutable, persistent indexed collection.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface PSequence<E> extends PCollection<E>, List<E> {
|
||||
|
||||
//@Override
|
||||
public PSequence<E> plus(E e);
|
||||
|
||||
//@Override
|
||||
public PSequence<E> plusAll(Collection<? extends E> list);
|
||||
|
||||
/**
|
||||
* @param i
|
||||
* @param e
|
||||
* @return a sequence consisting of the elements of this with e replacing the element at index i.
|
||||
* @throws IndexOutOfBOundsException if i<0 || i>=this.size()
|
||||
*/
|
||||
public PSequence<E> with(int i, E e);
|
||||
|
||||
/**
|
||||
* @param i
|
||||
* @param e non-null
|
||||
* @return a sequence consisting of the elements of this with e inserted at index i.
|
||||
* @throws IndexOutOfBOundsException if i<0 || i>this.size()
|
||||
*/
|
||||
public PSequence<E> plus(int i, E e);
|
||||
|
||||
/**
|
||||
* @param i
|
||||
* @param list
|
||||
* @return a sequence consisting of the elements of this with list inserted at index i.
|
||||
* @throws IndexOutOfBOundsException if i<0 || i>this.size()
|
||||
*/
|
||||
public PSequence<E> plusAll(int i, Collection<? extends E> list);
|
||||
|
||||
/**
|
||||
* Returns a sequence consisting of the elements of this without the first occurrence of e.
|
||||
*/
|
||||
//@Override
|
||||
public PSequence<E> minus(Object e);
|
||||
|
||||
//@Override
|
||||
public PSequence<E> minusAll(Collection<?> list);
|
||||
|
||||
/**
|
||||
* @param i
|
||||
* @return a sequence consisting of the elements of this with the element at index i removed.
|
||||
* @throws IndexOutOfBOundsException if i<0 || i>=this.size()
|
||||
*/
|
||||
public PSequence<E> minus(int i);
|
||||
|
||||
//@Override
|
||||
public PSequence<E> subList(int start, int end);
|
||||
|
||||
@Deprecated boolean addAll(int index, Collection<? extends E> c);
|
||||
@Deprecated E set(int index, E element);
|
||||
@Deprecated void add(int index, E element);
|
||||
@Deprecated E remove(int index);
|
||||
}
|
||||
@@ -1,53 +1,14 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
* An immutable, persistent stack.
|
||||
*
|
||||
* @author harold
|
||||
*
|
||||
* @param <E>
|
||||
* @author harold
|
||||
*/
|
||||
public interface PStack<E> extends PSequence<E> {
|
||||
|
||||
/**
|
||||
* Returns a stack consisting of the elements of this with e prepended.
|
||||
*/
|
||||
//@Override
|
||||
public PStack<E> plus(E e);
|
||||
|
||||
/**
|
||||
* Returns a stack consisting of the elements of this with list prepended in reverse.
|
||||
*/
|
||||
//@Override
|
||||
public PStack<E> plusAll(Collection<? extends E> list);
|
||||
|
||||
//@Override
|
||||
public PStack<E> with(int i, E e);
|
||||
|
||||
//@Override
|
||||
public PStack<E> plus(int i, E e);
|
||||
|
||||
//@Override
|
||||
public PStack<E> plusAll(int i, Collection<? extends E> list);
|
||||
|
||||
//@Override
|
||||
public PStack<E> minus(Object e);
|
||||
|
||||
//@Override
|
||||
public PStack<E> minusAll(Collection<?> list);
|
||||
public interface PStack<E> extends Iterable<E> {
|
||||
PStack<E> plus(E e);
|
||||
|
||||
//@Override
|
||||
public PStack<E> minus(int i);
|
||||
PStack<E> minus(int i);
|
||||
|
||||
//@Override
|
||||
public PStack<E> subList(int start, int end);
|
||||
|
||||
/**
|
||||
* @param start
|
||||
* @return subList(start,this.size())
|
||||
*/
|
||||
public PStack<E> subList(int start);
|
||||
int size();
|
||||
}
|
||||
|
||||
+106
-119
@@ -1,12 +1,9 @@
|
||||
package kotlin.reflect.jvm.internal.pcollections;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* (Taken from Java 6 code, so pcollections can be Java 5-compatible.)
|
||||
* <p>
|
||||
* <p/>
|
||||
* An Entry maintaining an immutable key and value. This class
|
||||
* does not support method <tt>setValue</tt>. This class may be
|
||||
* convenient in methods that return thread-safe snapshots of
|
||||
@@ -14,129 +11,119 @@ import java.util.Map.Entry;
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
/*public*/ final class SimpleImmutableEntry<K,V>
|
||||
implements Map.Entry<K,V>, java.io.Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 7138329143949025153L;
|
||||
final class SimpleImmutableEntry<K, V> implements Map.Entry<K, V>, java.io.Serializable {
|
||||
private static final long serialVersionUID = 7138329143949025153L;
|
||||
|
||||
private final K key;
|
||||
private final V value;
|
||||
private final K key;
|
||||
private final V value;
|
||||
|
||||
/**
|
||||
* Creates an entry representing a mapping from the specified
|
||||
* key to the specified value.
|
||||
*
|
||||
* @param key the key represented by this entry
|
||||
* @param value the value represented by this entry
|
||||
*/
|
||||
public SimpleImmutableEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
/**
|
||||
* Creates an entry representing a mapping from the specified
|
||||
* key to the specified value.
|
||||
*
|
||||
* @param key the key represented by this entry
|
||||
* @param value the value represented by this entry
|
||||
*/
|
||||
public SimpleImmutableEntry(K key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entry representing the same mapping as the
|
||||
* specified entry.
|
||||
*
|
||||
* @param entry the entry to copy
|
||||
*/
|
||||
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
|
||||
this.key = entry.getKey();
|
||||
this.value = entry.getValue();
|
||||
}
|
||||
/**
|
||||
* Returns the key corresponding to this entry.
|
||||
*
|
||||
* @return the key corresponding to this entry
|
||||
*/
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key corresponding to this entry.
|
||||
*
|
||||
* @return the key corresponding to this entry
|
||||
*/
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
/**
|
||||
* Returns the value corresponding to this entry.
|
||||
*
|
||||
* @return the value corresponding to this entry
|
||||
*/
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value corresponding to this entry.
|
||||
*
|
||||
* @return the value corresponding to this entry
|
||||
*/
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Replaces the value corresponding to this entry with the specified
|
||||
* value (optional operation). This implementation simply throws
|
||||
* <tt>UnsupportedOperationException</tt>, as this class implements
|
||||
* an <i>immutable</i> map entry.
|
||||
*
|
||||
* @param value new value to be stored in this entry
|
||||
* @return (Does not return)
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the value corresponding to this entry with the specified
|
||||
* value (optional operation). This implementation simply throws
|
||||
* <tt>UnsupportedOperationException</tt>, as this class implements
|
||||
* an <i>immutable</i> map entry.
|
||||
*
|
||||
* @param value new value to be stored in this entry
|
||||
* @return (Does not return)
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public V setValue(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
/**
|
||||
* Compares the specified object with this entry for equality.
|
||||
* Returns {@code true} if the given object is also a map entry and
|
||||
* the two entries represent the same mapping. More formally, two
|
||||
* entries {@code e1} and {@code e2} represent the same mapping
|
||||
* if<pre>
|
||||
* (e1.getKey()==null ?
|
||||
* e2.getKey()==null :
|
||||
* e1.getKey().equals(e2.getKey()))
|
||||
* &&
|
||||
* (e1.getValue()==null ?
|
||||
* e2.getValue()==null :
|
||||
* e1.getValue().equals(e2.getValue()))</pre>
|
||||
* This ensures that the {@code equals} method works properly across
|
||||
* different implementations of the {@code Map.Entry} interface.
|
||||
*
|
||||
* @param o object to be compared for equality with this map entry
|
||||
* @return {@code true} if the specified object is equal to this map
|
||||
* entry
|
||||
* @see #hashCode
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
|
||||
return eq(key, e.getKey()) && eq(value, e.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified object with this entry for equality.
|
||||
* Returns {@code true} if the given object is also a map entry and
|
||||
* the two entries represent the same mapping. More formally, two
|
||||
* entries {@code e1} and {@code e2} represent the same mapping
|
||||
* if<pre>
|
||||
* (e1.getKey()==null ?
|
||||
* e2.getKey()==null :
|
||||
* e1.getKey().equals(e2.getKey()))
|
||||
* &&
|
||||
* (e1.getValue()==null ?
|
||||
* e2.getValue()==null :
|
||||
* e1.getValue().equals(e2.getValue()))</pre>
|
||||
* This ensures that the {@code equals} method works properly across
|
||||
* different implementations of the {@code Map.Entry} interface.
|
||||
*
|
||||
* @param o object to be compared for equality with this map entry
|
||||
* @return {@code true} if the specified object is equal to this map
|
||||
* entry
|
||||
* @see #hashCode
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
|
||||
return eq(key, e.getKey()) && eq(value, e.getValue());
|
||||
}
|
||||
/**
|
||||
* Returns the hash code value for this map entry. The hash code
|
||||
* of a map entry {@code e} is defined to be: <pre>
|
||||
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
|
||||
* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
|
||||
* This ensures that {@code e1.equals(e2)} implies that
|
||||
* {@code e1.hashCode()==e2.hashCode()} for any two Entries
|
||||
* {@code e1} and {@code e2}, as required by the general
|
||||
* contract of {@link Object#hashCode}.
|
||||
*
|
||||
* @return the hash code value for this map entry
|
||||
* @see #equals
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this map entry. The hash code
|
||||
* of a map entry {@code e} is defined to be: <pre>
|
||||
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
|
||||
* (e.getValue()==null ? 0 : e.getValue().hashCode())</pre>
|
||||
* This ensures that {@code e1.equals(e2)} implies that
|
||||
* {@code e1.hashCode()==e2.hashCode()} for any two Entries
|
||||
* {@code e1} and {@code e2}, as required by the general
|
||||
* contract of {@link Object#hashCode}.
|
||||
*
|
||||
* @return the hash code value for this map entry
|
||||
* @see #equals
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (key == null ? 0 : key.hashCode()) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String representation of this map entry. This
|
||||
* implementation returns the string representation of this
|
||||
* entry's key followed by the equals character ("<tt>=</tt>")
|
||||
* followed by the string representation of this entry's value.
|
||||
*
|
||||
* @return a String representation of this map entry
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
/**
|
||||
* Returns a String representation of this map entry. This
|
||||
* implementation returns the string representation of this
|
||||
* entry's key followed by the equals character ("<tt>=</tt>")
|
||||
* followed by the string representation of this entry's value.
|
||||
*
|
||||
* @return a String representation of this map entry
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for SimpleEntry and SimpleImmutableEntry.
|
||||
|
||||
Reference in New Issue
Block a user