diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/ConsPStack.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/ConsPStack.java index b529b6580c8..90ea3b6dee2 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/ConsPStack.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/ConsPStack.java @@ -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. - *

- * This implementation is thread-safe (assuming Java's AbstractSequentialList is thread-safe), - * although its iterators may not be. - * - * @author harold + *

+ * This implementation is thread-safe, although its iterators may not be. * - * @param + * @author harold */ -public final class ConsPStack extends AbstractSequentialList implements PStack { -//// STATIC FACTORY METHODS //// - private static final ConsPStack EMPTY = new ConsPStack(); - - /** - * @param - * @return an empty stack - */ - @SuppressWarnings("unchecked") - public static ConsPStack empty() { - return (ConsPStack)EMPTY; } - - /** - * @param - * @param e - * @return empty().plus(e) - */ - public static ConsPStack singleton(final E e) { - return ConsPStack.empty().plus(e); } - - /** - * @param - * @param list - * @return a stack consisting of the elements of list in the order of list.iterator() - */ - @SuppressWarnings("unchecked") - public static ConsPStack from(final Collection list) { - if(list instanceof ConsPStack) - return (ConsPStack)list; //(actually we only know it's ConsPStack) - // 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 ConsPStack from(final Iterator i) { - if(!i.hasNext()) return empty(); - E e = i.next(); - return from(i).plus(e); - } +public final class ConsPStack implements PStack { + private static final ConsPStack EMPTY = new ConsPStack(); - -//// PRIVATE CONSTRUCTORS //// - private final E first; private final ConsPStack 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 rest) { - this.first = first; this.rest = rest; - - size = 1 + rest.size; - } - - -//// REQUIRED METHODS FROM AbstractSequentialList //// - @Override - public int size() { - return size; } - - @Override - public ListIterator listIterator(final int index) { - if(index<0 || index>size) throw new IndexOutOfBoundsException(); - - return new ListIterator() { - int i = index; - ConsPStack next = subList(index); + @SuppressWarnings("unchecked") + public static ConsPStack empty() { + return (ConsPStack) 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 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 rest) { + this.first = first; + this.rest = rest; + this.size = 1 + rest.size; + } -//// OVERRIDDEN METHODS FROM AbstractSequentialList //// - @Override - public ConsPStack 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(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 plus(final E e) { - return new ConsPStack(e, this); - } - - public ConsPStack plusAll(final Collection list) { - ConsPStack 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 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(first, rest.plus(i-1, e)); - } + @Override + public Iterator iterator() { + return listIterator(0); + } - public ConsPStack plusAll(final int i, final Collection list) { - // TODO inefficient if list.isEmpty() - if(i<0 || i>size) - throw new IndexOutOfBoundsException(); - if(i==0) - return plusAll(list); - return new ConsPStack(first, rest.plusAll(i-1, list)); - } - - public ConsPStack 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 newRest = rest.minus(e); - if(newRest==rest) return this; - return new ConsPStack(first, newRest); - } + @Override + public int size() { + return size; + } - public ConsPStack minus(final int i) { - return minus(get(i)); - } + public ListIterator listIterator(final int index) { + if (index < 0 || index > size) throw new IndexOutOfBoundsException(); - public ConsPStack 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 newRest = rest.minusAll(list); - if(newRest==rest) return this; - return new ConsPStack(first, newRest); - } - - public ConsPStack 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, rest); - } - ConsPStack newRest = rest.with(i-1, e); - if(newRest==rest) return this; - return new ConsPStack(first, newRest); - } + return new ListIterator() { + int i = index; + ConsPStack next = subList(index); - public ConsPStack 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 plus(E e) { + return new ConsPStack(e, this); + } + + public ConsPStack 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 newRest = rest.minus(e); + if (newRest == rest) return this; + return new ConsPStack(first, newRest); + } + + @Override + public ConsPStack minus(int i) { + return minus(get(i)); + } + + public ConsPStack subList(int start) { + if (start < 0 || start > size) + throw new IndexOutOfBoundsException(); + if (start == 0) + return this; + return rest.subList(start - 1); + } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashPMap.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashPMap.java index f2c777c364b..36c6abc287e 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashPMap.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashPMap.java @@ -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. - *

+ *

* 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. - *

- * This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe), - * although its iterators may not be. - * - * @author harold + *

+ * This implementation is thread-safe, although its iterators may not be. * - * @param - * @param + * @author harold */ -public final class HashPMap extends AbstractMap implements PMap { -//// STATIC FACTORY METHODS //// - /** - * @param - * @param - * @param intMap - * @return a map backed by an empty version of intMap, - * i.e. backed by intMap.minusAll(intMap.keySet()) - */ - public static HashPMap empty(final PMap>> intMap) { - return new HashPMap(intMap.minusAll(intMap.keySet()), 0); } - +public final class HashPMap implements PMap { + public static final HashPMap EMPTY = new HashPMap(IntTreePMap.>>empty(), 0); -//// PRIVATE CONSTRUCTORS //// - private final PMap>> intMap; - private final int size; - // not externally instantiable (or subclassable): - private HashPMap(final PMap>> intMap, final int size) { - this.intMap = intMap; this.size = size; } + @SuppressWarnings("unchecked") + public static HashPMap empty() { + return (HashPMap) HashPMap.EMPTY; + } - -//// REQUIRED METHODS FROM AbstractMap //// - // this cache variable is thread-safe since assignment in Java is atomic: - private Set> entrySet = null; - @Override - public Set> entrySet() { - if(entrySet==null) - entrySet = new AbstractSet>() { - // REQUIRED METHODS OF AbstractSet // - @Override - public int size() { - return size; } - @Override - public Iterator> iterator() { - return new SequenceIterator>(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>> intMap; + private final int size; - -//// OVERRIDDEN METHODS FROM AbstractMap //// - @Override - public int size() { - return size; } + private HashPMap(IntTreePMap>> 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> entries = getEntries(key.hashCode()); - for(Entry entry : entries) - if(entry.getKey().equals(key)) - return entry.getValue(); - return null; - } + public int size() { + return size; + } - -//// IMPLEMENTED METHODS OF PMap//// - public HashPMap plusAll(final Map map) { - HashPMap result = this; - for(Entry 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 minusAll(final Collection keys) { - HashPMap result = this; - for(Object key : keys) - result = result.minus(key); - return result; - } - - public HashPMap plus(final K key, final V value) { - PSequence> entries = getEntries(key.hashCode()); - int size0 = entries.size(), - i = keyIndexIn(entries, key); - if(i!=-1) entries = entries.minus(i); - entries = entries.plus(new SimpleImmutableEntry(key, value)); - return new HashPMap(intMap.plus(key.hashCode(), entries), - size-size0+entries.size()); - } + @Override + public V get(Object key) { + PStack> entries = getEntries(key.hashCode()); + for (Entry entry : entries) + if (entry.getKey().equals(key)) + return entry.getValue(); + return null; + } - public HashPMap minus(final Object key) { - PSequence> 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(intMap.minus(key.hashCode()), - size-1); - // otherwise replace hash entry with new smaller one: - return new HashPMap(intMap.plus(key.hashCode(), entries), - size-1); - } - - -//// PRIVATE UTILITIES //// - private PSequence> getEntries(final int hash) { - PSequence> entries = intMap.get(hash); - if(entries==null) return ConsPStack.empty(); - return entries; - } + @Override + public HashPMap plus(K key, V value) { + PStack> 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(key, value)); + return new HashPMap(intMap.plus(key.hashCode(), entries), size - size0 + entries.size()); + } - -//// PRIVATE STATIC UTILITIES //// - private static int keyIndexIn(final PSequence> entries, final Object key) { - int i=0; - for(Entry entry : entries) { - if(entry.getKey().equals(key)) - return i; - i++; - } - return -1; - } - - static class SequenceIterator implements Iterator { - private final Iterator> i; - private PSequence seq = ConsPStack.empty(); - SequenceIterator(Iterator> i) { - this.i = i; } + @Override + public HashPMap minus(Object key) { + PStack> 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(intMap.minus(key.hashCode()), size - 1); + // otherwise replace hash entry with new smaller one: + return new HashPMap(intMap.plus(key.hashCode(), entries), size - 1); + } - public boolean hasNext() { - return seq.size()>0 || i.hasNext(); } + private PStack> getEntries(int hash) { + PStack> 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 int keyIndexIn(PStack> entries, Object key) { + int i = 0; + for (Entry entry : entries) { + if (entry.getKey().equals(key)) + return i; + i++; + } + return -1; + } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashTreePMap.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashTreePMap.java deleted file mode 100644 index 46b3ab9c195..00000000000 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/HashTreePMap.java +++ /dev/null @@ -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. - *

- * This class simply creates HashPMaps backed by IntTreePMaps. - * - * @author harold - */ -public final class HashTreePMap { - // not instantiable (or subclassable): - private HashTreePMap() {} - - private static final HashPMap EMPTY - = HashPMap.empty(IntTreePMap.>>empty()); - - /** - * @param - * @param - * @return an empty map - */ - @SuppressWarnings("unchecked") - public static HashPMap empty() { - return (HashPMap)EMPTY; } - - /** - * @param - * @param - * @param key - * @param value - * @return empty().plus(key, value) - */ - public static HashPMap singleton(final K key, final V value) { - return HashTreePMap.empty().plus(key, value); } - - /** - * @param - * @param - * @param map - * @return empty().plusAll(map) - */ - public static HashPMap from(final Map map) { - return HashTreePMap.empty().plusAll(map); } -} diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTree.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTree.java index d5a1ea7b2bb..5d3fcd44160 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTree.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTree.java @@ -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. - *

+ *

* 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. - *

+ *

* This implementation is thread-safe except for its iterators. - *

+ *

* 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: - *

+ *

* 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/. - *

+ *

* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance", * SIAM journal of computing 2(1), March 1973. - * - * @author harold * - * @param + * @author harold */ -class IntTree { - // marker value: - static final IntTree EMPTYNODE = new IntTree(); - - 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 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 left, final IntTree right) { - this.key = key; this.value = value; - this.left = left; this.right = right; - size = 1 + left.size + right.size; - } - - private IntTree withKey(final long newKey) { - if(size==0 || newKey==key) return this; - return new IntTree(newKey, value, left, right); } - - Iterator> iterator() { - return new EntryIterator(this); } - - int size() { - return size; } +final class IntTree { + // marker value: + static final IntTree EMPTYNODE = new IntTree(); - 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 left, right; + private final int size; - IntTree plus(final long key, final V value) { - if(size==0) - return new IntTree(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(key, value, left, right); - } + private IntTree() { + size = 0; + key = 0; + value = null; + left = null; + right = null; + } - IntTree 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 left, IntTree 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 withKey(long newKey) { + if (size == 0 || newKey == key) return this; + return new IntTree(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 newRight = right.minus(newKey-this.key); + IntTree plus(long key, V value) { + if (size == 0) + return new IntTree(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(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 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 changeKeysAbove(final long key, final int delta) { - if(size==0 || delta==0) - return this; + IntTree 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(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 newRight = right.changeKeysAbove(key-this.key, delta); - if(newRight==right) return this; - return new IntTree(this.key, value, left, newRight); - } - - /** - * Changes every key k0 - * and the distance between the largest k=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 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(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 newLeft = left.changeKeysBelow(key-this.key, delta); - if(newLeft==left) return this; - return new IntTree(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 rebalanced(final IntTree newLeft, final IntTree 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 IntTree rebalanced(final long key, final V value, - final IntTree left, final IntTree right) { - if(left.size + right.size > 1) { - if(left.size >= OMEGA*right.size) { // rotate to the right - IntTree ll = left.left, lr = left.right; - if(lr.size < ALPHA*ll.size) // single rotation - return new IntTree(left.key+key, left.value, - ll, - new IntTree(-left.key, value, - lr.withKey(lr.key+left.key), - right)); - else { // double rotation: - IntTree lrl = lr.left, lrr = lr.right; - return new IntTree(lr.key+left.key+key, lr.value, - new IntTree(-lr.key, left.value, - ll, - lrl.withKey(lrl.key+lr.key)), - new IntTree(-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 rl = right.left, rr = right.right; - if(rl.size < ALPHA*rr.size) // single rotation - return new IntTree(right.key+key, right.value, - new IntTree(-right.key, value, - left, - rl.withKey(rl.key+right.key)), - rr); - else { // double rotation: - IntTree rll = rl.left, rlr = rl.right; - return new IntTree(rl.key+right.key+key, rl.value, - new IntTree(-right.key-rl.key, value, - left, - rll.withKey(rll.key+rl.key+right.key)), - new IntTree(-rl.key, right.value, - rlr.withKey(rlr.key+rl.key), - rr)); - } - } - } - // otherwise already balanced enough: - return new IntTree(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 newRight = right.minus(newKey - this.key); - -////entrySet().iterator() IMPLEMENTATION //// - // TODO make this a ListIterator? - private static final class EntryIterator implements Iterator> { - private PStack> 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 root) { - gotoMinOf(root); } - - public boolean hasNext() { - return stack.size()>0; } - - public Entry next() { - IntTree node = stack.get(0); - final Entry result = new SimpleImmutableEntry(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 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 node) { - while(node.size>0) { - stack = stack.plus(node); - key += node.key; - node = node.left; - } - } - } + /** + * 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 + * 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 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(this.key + delta, value, left.changeKeysBelow(key - this.key, -delta), right); + + // otherwise, doesn't apply yet, look to the right: + IntTree newRight = right.changeKeysAbove(key - this.key, delta); + if (newRight == right) return this; + return new IntTree(this.key, value, left, newRight); + } + + /** + * Changes every key k + * This method will create an _invalid_ tree if delta>0 + * and the distance between the largest k=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 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(this.key + delta, value, left, right.changeKeysAbove(key - this.key, -delta)); + + // otherwise, doesn't apply yet, look to the left: + IntTree newLeft = left.changeKeysBelow(key - this.key, delta); + if (newLeft == left) return this; + return new IntTree(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 rebalanced(IntTree newLeft, IntTree 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 IntTree rebalanced(long key, V value, IntTree left, IntTree right) { + if (left.size + right.size > 1) { + if (left.size >= OMEGA * right.size) { // rotate to the right + IntTree ll = left.left, lr = left.right; + if (lr.size < ALPHA * ll.size) // single rotation + return new IntTree(left.key + key, left.value, + ll, + new IntTree(-left.key, value, + lr.withKey(lr.key + left.key), + right)); + else { // double rotation: + IntTree lrl = lr.left, lrr = lr.right; + return new IntTree(lr.key + left.key + key, lr.value, + new IntTree(-lr.key, left.value, + ll, + lrl.withKey(lrl.key + lr.key)), + new IntTree(-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 rl = right.left, rr = right.right; + if (rl.size < ALPHA * rr.size) // single rotation + return new IntTree(right.key + key, right.value, + new IntTree(-right.key, value, + left, + rl.withKey(rl.key + right.key)), + rr); + else { // double rotation: + IntTree rll = rl.left, rlr = rl.right; + return new IntTree(rl.key + right.key + key, rl.value, + new IntTree(-right.key - rl.key, value, + left, + rll.withKey(rll.key + rl.key + right.key)), + new IntTree(-rl.key, right.value, + rlr.withKey(rlr.key + rl.key), + rr)); + } + } + } + // otherwise already balanced enough: + return new IntTree(key, value, left, right); + } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTreePMap.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTreePMap.java index 4c144faf378..169409aee9b 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTreePMap.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/IntTreePMap.java @@ -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. - *

+ *

* Iteration occurs in the integer order of the keys. - *

- * This implementation is thread-safe (assuming Java's AbstractMap and AbstractSet are thread-safe), - * although its iterators may not be. - *

+ *

+ * This implementation is thread-safe, although its iterators may not be. + *

* 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: - *

+ *

* 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/. - *

+ *

* J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance", * SIAM journal of computing 2(1), March 1973. - * - * @author harold * - * @param + * @author harold */ -public final class IntTreePMap extends AbstractMap implements PMap { -//// STATIC FACTORY METHODS //// - private static final IntTreePMap EMPTY = new IntTreePMap(IntTree.EMPTYNODE); +public final class IntTreePMap { + private static final IntTreePMap EMPTY = new IntTreePMap(IntTree.EMPTYNODE); - /** - * @param - * @return an empty map - */ - @SuppressWarnings("unchecked") - public static IntTreePMap empty() { - return (IntTreePMap)EMPTY; } - - /** - * @param - * @param key - * @param value - * @return empty().plus(key, value) - */ - public static IntTreePMap singleton(final Integer key, final V value) { - return IntTreePMap.empty().plus(key, value); } - - /** - * @param - * @param map - * @return empty().plusAll(map) - */ - @SuppressWarnings("unchecked") - public static IntTreePMap from(final Map map) { - if(map instanceof IntTreePMap) - return (IntTreePMap)map; //(actually we only know it's IntTreePMap) - // 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.empty().plusAll(map); } - + @SuppressWarnings("unchecked") + public static IntTreePMap empty() { + return (IntTreePMap) EMPTY; + } -//// PRIVATE CONSTRUCTORS //// - private final IntTree root; - // not externally instantiable (or subclassable): - private IntTreePMap(final IntTree root) { - this.root = root; } - private IntTreePMap withRoot(final IntTree root) { - if(root==this.root) return this; - return new IntTreePMap(root); } - + private final IntTree root; -//// UNINHERITED METHODS OF IntTreePMap //// - IntTreePMap withKeysChangedAbove(final int key, final int delta) { - // TODO check preconditions of changeKeysAbove() - // TODO make public? - return withRoot( root.changeKeysAbove(key, delta) ); - } - - IntTreePMap 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> entrySet = null; - @Override - public Set> entrySet() { - if(entrySet==null) - entrySet = new AbstractSet>() { - // REQUIRED METHODS OF AbstractSet // - @Override - public int size() { // same as Map - return IntTreePMap.this.size(); } - @Override - public Iterator> 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 root) { + this.root = root; + } - -//// OVERRIDDEN METHODS FROM AbstractMap //// - @Override - public int size() { - return root.size(); } + private IntTreePMap withRoot(IntTree root) { + if (root == this.root) return this; + return new IntTreePMap(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 plus(final Integer key, final V value) { - return withRoot( root.plus(key, value) ); } - - public IntTreePMap minus(final Object key) { - if(!(key instanceof Integer)) return this; - return withRoot( root.minus((Integer)key) ); } - - public IntTreePMap plusAll(final Map map) { - IntTree root = this.root; - for(Entry entry : map.entrySet()) - root = root.plus(entry.getKey(), entry.getValue()); - return withRoot(root); - } + public IntTreePMap plus(int key, V value) { + return withRoot(root.plus(key, value)); + } - public IntTreePMap minusAll(final Collection keys) { - IntTree root = this.root; - for(Object key : keys) - if(key instanceof Integer) - root = root.minus((Integer)key); - return withRoot(root); - } + public IntTreePMap minus(int key) { + return withRoot(root.minus(key)); + } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PCollection.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PCollection.java deleted file mode 100644 index 333acc4ca15..00000000000 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PCollection.java +++ /dev/null @@ -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 - */ -public interface PCollection extends Collection { - - /** - * @param e non-null - * @return a collection which contains e and all of the elements of this - */ - public PCollection plus(E e); - - /** - * @param list contains no null elements - * @return a collection which contains all of the elements of list and this - */ - public PCollection plusAll(Collection list); - - /** - * @param e - * @return this with a single instance of e removed, if e is in this - */ - public PCollection minus(Object e); - - /** - * @param list - * @return this with all elements of list completely removed - */ - public PCollection minusAll(Collection list); - - // TODO public PCollection retainingAll(Collection list); - - @Deprecated boolean add(E o); - @Deprecated boolean remove(Object o); - @Deprecated boolean addAll(Collection c); - @Deprecated boolean removeAll(Collection c); - @Deprecated boolean retainAll(Collection c); - @Deprecated void clear(); -} diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PMap.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PMap.java index bdfd4f54896..e8e1c2c4217 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PMap.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PMap.java @@ -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 - * @param + * @author harold */ -public interface PMap extends Map { - /** - * @param key non-null - * @param value non-null - * @return a map with the mappings of this but with key mapped to value - */ - public PMap 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 plusAll(Map map); - - /** - * @param key - * @return a map with the mappings of this but with no value for key - */ - public PMap minus(Object key); - - /** - * @param keys - * @return a map with the mappings of this but with no value for any element of keys - */ - public PMap minusAll(Collection keys); - - @Deprecated V put(K k, V v); - @Deprecated V remove(Object k); - @Deprecated void putAll(Map m); - @Deprecated void clear(); +public interface PMap { + public PMap plus(K key, V value); + + public PMap minus(Object key); + + V get(Object key); } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PSequence.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PSequence.java deleted file mode 100644 index 3be82dcec0f..00000000000 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PSequence.java +++ /dev/null @@ -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 - */ -public interface PSequence extends PCollection, List { - - //@Override - public PSequence plus(E e); - - //@Override - public PSequence plusAll(Collection 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 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 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 plusAll(int i, Collection list); - - /** - * Returns a sequence consisting of the elements of this without the first occurrence of e. - */ - //@Override - public PSequence minus(Object e); - - //@Override - public PSequence 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 minus(int i); - - //@Override - public PSequence subList(int start, int end); - - @Deprecated boolean addAll(int index, Collection c); - @Deprecated E set(int index, E element); - @Deprecated void add(int index, E element); - @Deprecated E remove(int index); -} diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PStack.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PStack.java index ca68a69360e..5b52152671a 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PStack.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/PStack.java @@ -1,53 +1,14 @@ package kotlin.reflect.jvm.internal.pcollections; -import java.util.Collection; - /** - * * An immutable, persistent stack. - * - * @author harold * - * @param + * @author harold */ -public interface PStack extends PSequence { - - /** - * Returns a stack consisting of the elements of this with e prepended. - */ - //@Override - public PStack plus(E e); - - /** - * Returns a stack consisting of the elements of this with list prepended in reverse. - */ - //@Override - public PStack plusAll(Collection list); - - //@Override - public PStack with(int i, E e); - - //@Override - public PStack plus(int i, E e); - - //@Override - public PStack plusAll(int i, Collection list); - - //@Override - public PStack minus(Object e); - - //@Override - public PStack minusAll(Collection list); +public interface PStack extends Iterable { + PStack plus(E e); - //@Override - public PStack minus(int i); + PStack minus(int i); - //@Override - public PStack subList(int start, int end); - - /** - * @param start - * @return subList(start,this.size()) - */ - public PStack subList(int start); + int size(); } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/SimpleImmutableEntry.java b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/SimpleImmutableEntry.java index 71452649e25..ae79bc2bb6c 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/SimpleImmutableEntry.java +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/pcollections/SimpleImmutableEntry.java @@ -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.) - *

+ *

* An Entry maintaining an immutable key and value. This class * does not support method setValue. 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 - implements Map.Entry, java.io.Serializable -{ - private static final long serialVersionUID = 7138329143949025153L; +final class SimpleImmutableEntry implements Map.Entry, 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 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 + * UnsupportedOperationException, as this class implements + * an immutable 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 - * UnsupportedOperationException, as this class implements - * an immutable 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

+     *   (e1.getKey()==null ?
+     *    e2.getKey()==null :
+     *    e1.getKey().equals(e2.getKey()))
+     *   &&
+     *   (e1.getValue()==null ?
+     *    e2.getValue()==null :
+     *    e1.getValue().equals(e2.getValue()))
+ * 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
-	 *   (e1.getKey()==null ?
-	 *    e2.getKey()==null :
-	 *    e1.getKey().equals(e2.getKey()))
-	 *   &&
-	 *   (e1.getValue()==null ?
-	 *    e2.getValue()==null :
-	 *    e1.getValue().equals(e2.getValue()))
- * 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:
+     *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
+     *   (e.getValue()==null ? 0 : e.getValue().hashCode())
+ * 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:
-	 *   (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
-	 *   (e.getValue()==null ? 0 : e.getValue().hashCode())
- * 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 ("=") - * 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 ("=") + * 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.