Separate the EqualityComparator interface from the Set and Map implementations that need it

This commit is contained in:
Sam Harwell 2012-11-21 19:30:41 -06:00
parent 094f40c961
commit f3ca54d350
11 changed files with 244 additions and 74 deletions

View File

@ -30,6 +30,7 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.DoubleKeyMap;
@ -239,7 +240,14 @@ public class ATNConfigSet implements Set<ATNConfig> {
*/
public static class ConfigHashSet extends Array2DHashSet<ATNConfig> {
public ConfigHashSet() {
super(16,2);
super(ConfigEqualityComparator.INSTANCE,16,2);
}
}
public static final class ConfigEqualityComparator extends AbstractEqualityComparator<ATNConfig> {
public static final ConfigEqualityComparator INSTANCE = new ConfigEqualityComparator();
private ConfigEqualityComparator() {
}
@Override
@ -273,7 +281,7 @@ public class ATNConfigSet implements Set<ATNConfig> {
/** All configs but hashed by (s, i, _, pi) not incl context. Wiped out
* when we go readonly as this set becomes a DFA state.
*/
public ConfigHashSet configLookup;
public Array2DHashSet<ATNConfig> configLookup;
/** Track the elements as they are added to the set; supports get(i) */
public final ArrayList<ATNConfig> configs = new ArrayList<ATNConfig>(7);

View File

@ -30,6 +30,9 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.ObjectEqualityComparator;
/**
*
* @author Sam Harwell
@ -40,18 +43,9 @@ public class OrderedATNConfigSet extends ATNConfigSet {
this.configLookup = new LexerConfigHashSet();
}
protected static class LexerConfigHashSet extends ConfigHashSet {
@Override
public int hashCode(ATNConfig o) {
return o.hashCode();
public static class LexerConfigHashSet extends Array2DHashSet<ATNConfig> {
public LexerConfigHashSet() {
super(ObjectEqualityComparator.INSTANCE, 16, 2);
}
@Override
public boolean equals(ATNConfig a, ATNConfig b) {
return a.equals(b);
}
}
}

View File

@ -1,5 +1,6 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.FlexibleHashMap;
import org.antlr.v4.runtime.misc.NotNull;
@ -34,6 +35,17 @@ public enum PredictionMode {
/** A Map that uses just the state and the stack context as the key. */
static class AltAndContextMap extends FlexibleHashMap<ATNConfig,BitSet> {
public AltAndContextMap() {
super(AltAndContextConfigEqualityComparator.INSTANCE);
}
}
private static final class AltAndContextConfigEqualityComparator extends AbstractEqualityComparator<ATNConfig> {
public static final AltAndContextConfigEqualityComparator INSTANCE = new AltAndContextConfigEqualityComparator();
private AltAndContextConfigEqualityComparator() {
}
/** Code is function of (s, _, ctx, _) */
@Override
public int hashCode(ATNConfig o) {

View File

@ -0,0 +1,40 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* Copyright (c) 2012 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.misc;
/**
* This abstract base class is provided so performance-critical applications can
* use virtual- instead of interface-dispatch when calling comparator methods.
*
* @author Sam Harwell
*/
public abstract class AbstractEqualityComparator<T> implements EqualityComparator<T> {
}

View File

@ -6,11 +6,14 @@ import java.util.NoSuchElementException;
import java.util.Set;
/** Set impl with closed hashing (open addressing). */
public class Array2DHashSet<T> implements EquivalenceSet<T> {
public class Array2DHashSet<T> implements Set<T> {
public static final int INITAL_CAPACITY = 16; // must be power of 2
public static final int INITAL_BUCKET_CAPACITY = 8;
public static final double LOAD_FACTOR = 0.75;
@NotNull
protected final AbstractEqualityComparator<? super T> comparator;
protected T[][] buckets;
/** How many elements in set */
@ -22,11 +25,20 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
protected int initialBucketCapacity = INITAL_BUCKET_CAPACITY;
public Array2DHashSet() {
this(INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
this(null, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
}
public Array2DHashSet(int initialCapacity, int initialBucketCapacity) {
buckets = (T[][])new Object[initialCapacity][];
public Array2DHashSet(@Nullable AbstractEqualityComparator<? super T> comparator) {
this(comparator, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
}
public Array2DHashSet(@Nullable AbstractEqualityComparator<? super T> comparator, int initialCapacity, int initialBucketCapacity) {
if (comparator == null) {
comparator = ObjectEqualityComparator.INSTANCE;
}
this.comparator = comparator;
this.buckets = (T[][])new Object[initialCapacity][];
this.initialBucketCapacity = initialBucketCapacity;
}
@ -56,7 +68,7 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
n++;
return o;
}
if ( equals(existing, o) ) return existing; // found existing, quit
if ( comparator.equals(existing, o) ) return existing; // found existing, quit
}
// FULL BUCKET, expand and add to end
T[] old = bucket;
@ -75,13 +87,13 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
if ( bucket==null ) return null; // no bucket
for (T e : bucket) {
if ( e==null ) return null; // empty slot; not there
if ( equals(e, o) ) return e;
if ( comparator.equals(e, o) ) return e;
}
return null;
}
protected int getBucket(T o) {
int hash = hashCode(o);
int hash = comparator.hashCode(o);
int b = hash & (buckets.length-1); // assumes len is power of 2
return b;
}
@ -93,7 +105,7 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
if ( bucket==null ) continue;
for (T o : bucket) {
if ( o==null ) break;
h += hashCode(o);
h += comparator.hashCode(o);
}
}
return h;
@ -129,14 +141,6 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
n = oldSize;
}
public int hashCode(T o) {
return o.hashCode();
}
public boolean equals(T a, T b) {
return a.equals(b);
}
@Override
public boolean add(T t) {
T existing = absorb(t);
@ -229,7 +233,7 @@ public class Array2DHashSet<T> implements EquivalenceSet<T> {
for (int i=0; i<bucket.length; i++) {
T e = bucket[i];
if ( e==null ) return false; // empty slot; not there
if ( equals(e, (T) o) ) { // found it
if ( comparator.equals(e, (T) o) ) { // found it
// shift all elements to the right down one
// for (int j=i; j<bucket.length-1; j++) bucket[j] = bucket[j+1];
System.arraycopy(bucket, i+1, bucket, i, bucket.length-i-1);

View File

@ -0,0 +1,59 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* Copyright (c) 2012 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.misc;
/**
* This interface provides an abstract concept of object equality independent of
* {@link Object#equals} (object equality) and the {@code ==} operator
* (reference equality). It can be used to provide algorithm-specific unordered
* comparisons without requiring changes to the object itself.
*
* @author Sam Harwell
*/
public interface EqualityComparator<T> {
/**
* This method returns a hash code for the specified object.
*
* @param obj The object.
* @return The hash code for {@code obj}.
*/
int hashCode(T obj);
/**
* This method tests if two objects are equal.
*
* @param a The first object to compare.
* @param b The second object to compare.
* @return {@code true} if {@code a} equals {@code b}, otherwise {@code false}.
*/
boolean equals(T a, T b);
}

View File

@ -1,6 +0,0 @@
package org.antlr.v4.runtime.misc;
import java.util.Map;
public interface EquivalenceMap<K,V> extends Map<K,V>, EquivalenceRelation<K> {
}

View File

@ -1,7 +0,0 @@
package org.antlr.v4.runtime.misc;
/** What does it mean for two objects to be equivalent? */
public interface EquivalenceRelation<T> {
public int hashCode(T o);
public boolean equals(T a, T b);
}

View File

@ -1,12 +0,0 @@
package org.antlr.v4.runtime.misc;
import java.util.Set;
/** A set that allows us to override equivalence. For a single set, we might
* want multiple subset perspectives as defined by different hash code
* and equivalence methods. HashSet does not allow us to subclass and
* override the equivalence operations, so we have to implement our own
* sets that are flexible in terms of equivalence.
*/
public interface EquivalenceSet<T> extends Set<T>, EquivalenceRelation<T> {
}

View File

@ -10,7 +10,7 @@ import java.util.Set;
/** A limited map (many unsupported operations) that lets me use
* varying hashCode/equals.
*/
public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
public class FlexibleHashMap<K,V> implements Map<K, V> {
public static final int INITAL_CAPACITY = 16; // must be power of 2
public static final int INITAL_BUCKET_CAPACITY = 8;
public static final double LOAD_FACTOR = 0.75;
@ -27,6 +27,9 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
}
}
@NotNull
protected final AbstractEqualityComparator<? super K> comparator;
protected LinkedList<Entry<K, V>>[] buckets;
/** How many elements in set */
@ -38,11 +41,20 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
protected int initialBucketCapacity = INITAL_BUCKET_CAPACITY;
public FlexibleHashMap() {
this(INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
this(null, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
}
public FlexibleHashMap(int initialCapacity, int initialBucketCapacity) {
buckets = createEntryListArray(initialBucketCapacity);
public FlexibleHashMap(@Nullable AbstractEqualityComparator<? super K> comparator) {
this(comparator, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY);
}
public FlexibleHashMap(@Nullable AbstractEqualityComparator<? super K> comparator, int initialCapacity, int initialBucketCapacity) {
if (comparator == null) {
comparator = ObjectEqualityComparator.INSTANCE;
}
this.comparator = comparator;
this.buckets = createEntryListArray(initialBucketCapacity);
this.initialBucketCapacity = initialBucketCapacity;
}
@ -52,18 +64,8 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
return result;
}
@Override
public boolean equals(K keyA, K keyB) {
return keyA.equals(keyB);
}
@Override
public int hashCode(K o) {
return o.hashCode();
}
protected int getBucket(K key) {
int hash = hashCode(key);
int hash = comparator.hashCode(key);
int b = hash & (buckets.length-1); // assumes len is power of 2
return b;
}
@ -77,7 +79,9 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
LinkedList<Entry<K, V>> bucket = buckets[b];
if ( bucket==null ) return null; // no bucket
for (Entry<K, V> e : bucket) {
if ( equals(e.key, typedKey) ) return e.value; // use special equals
if ( comparator.equals(e.key, typedKey) ) {
return e.value;
}
}
return null;
}
@ -92,7 +96,7 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
bucket = buckets[b] = new LinkedList<Entry<K, V>>();
}
for (Entry<K, V> e : bucket) {
if ( equals(e.key, key) ) {
if ( comparator.equals(e.key, key) ) {
V prev = e.value;
e.value = value;
n++;
@ -154,7 +158,7 @@ public class FlexibleHashMap<K,V> implements EquivalenceMap<K,V> {
if ( bucket==null ) continue;
for (Entry<K, V> e : bucket) {
if ( e==null ) break;
h += hashCode(e.key);
h += comparator.hashCode(e.key);
}
}
return h;

View File

@ -0,0 +1,74 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* Copyright (c) 2012 Sam Harwell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.misc;
/**
* This default implementation of {@link EqualityComparator} uses object equality
* for comparisons by calling {@link Object#hashCode} and {@link Object#equals}.
*
* @author Sam Harwell
*/
public final class ObjectEqualityComparator extends AbstractEqualityComparator<Object> {
public static final ObjectEqualityComparator INSTANCE = new ObjectEqualityComparator();
/**
* {@inheritDoc}
* <p/>
* This implementation returns
* {@code obj.}{@link Object#hashCode hashCode()}.
*/
@Override
public int hashCode(Object obj) {
if (obj == null) {
return 0;
}
return obj.hashCode();
}
/**
* {@inheritDoc}
* <p/>
* This implementation relies on object equality. If both objects are
* {@code null}, this method returns {@code true}. Otherwise if only
* {@code a} is {@code null}, this method returns {@code false}. Otherwise,
* this method returns the result of
* {@code a.}{@link Object#equals equals}{@code (b)}.
*/
@Override
public boolean equals(Object a, Object b) {
if (a == null) {
return b == null;
}
return a.equals(b);
}
}