// Base class for an implementation of FMap. package fmap; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Collections; // for its sort algorithm import java.util.ArrayList; // for its iterator abstract class FMapBase implements FMap, Iterable { static final boolean DEBUGGING = false; // set to true for debugging static final boolean SIZE_IS_FAST = true; // if true, makes size() fast static final boolean HASH_IS_SIZE = false; // if true, uses size as hash static final int HASH_OF_EMPTY_FMAP = 0; // hash code for an empty FMap int theSize; // if SIZE_IS_FAST, theSize is the size of this FMap FMapBase () { } private FMapBase (int theSize) { this.theSize = theSize; } static FMapBase emptyMap () { return AList.emptyMap(); } static FMapBase emptyMap (Comparator c) { return FTree.emptyTree(c); } // abstract methods // As specified by the algebraic specification, // returns an FMap that associates key k with value v // but otherwise behaves like this FMap. public abstract FMapBase add (K x, V v); // Returns true if and only if this FMap has size 0. public abstract boolean isEmpty (); // Returns the size of this FMap. public abstract int size (); // Returns true if and only if k is a key of this FMap. public abstract boolean containsKey (K x); // Returns the value associated with k in this FMap. // May throw an exception if k is not a key of this FMap. public abstract V get (K x); // Implementation-specific help method. // // Returns true if and only if // m2 has every key that this FMapBase has // and associates the same values with those keys. abstract boolean equalsLoop (FMap m2); // Implementation-specific help method. // // Adds all keys in this FMap to the given ArrayList, // taking care to add each key only once. abstract ArrayList allKeys (ArrayList keys); // Implementation-specific help method. // // Returns an ArrayList of the keys in this FMap, // with each key appearing only once. private ArrayList allKeys () { ArrayList keys = new ArrayList(); this.allKeys (keys); return keys; } @Override public String toString () { return "{...(" + size() + " entries)...}"; } // overridden equals(Object) // Two FMap objects are considered equal if and only if // they are defined on the same set of keys and each of those // keys is associated with the same value. @Override public boolean equals (Object x) { if (x instanceof FMap) { @SuppressWarnings(value="unchecked") FMap f2 = (FMap) x; return this.size() == f2.size() && this.equalsLoop (f2); } else return false; } // overridden hashCode() @Override public int hashCode() { if (HASH_IS_SIZE) return size(); else { int result = HASH_OF_EMPTY_FMAP; for (K k : this) result = result + WEIGHT1 * k.hashCode() + WEIGHT2 * get(k).hashCode() + WEIGHT3; return result; } } // constants used above to spread out the hash codes private final int WEIGHT1 = 2072679696; private final int WEIGHT2 = 1693108132; private final int WEIGHT3 = 2442080; // iterator methods public Iterator iterator () { ArrayList a = this.allKeys(); return new KeyIterator (a.iterator()); } public Iterator iterator (java.util.Comparator c) { ArrayList a = this.allKeys(); Collections.sort (a, c); return new KeyIterator (a.iterator()); } // FIXME: temporary hack for debugging V get (K k, int h) { return get (k); } // The iterator() method returns instances of this class. private static class KeyIterator implements Iterator { private Iterator it; // generator for keys not yet generated KeyIterator (Iterator it) { this.it = it; } // The hasNext() and next() methods just delegate to it. public boolean hasNext () { return it.hasNext(); } public K next () { return it.next(); } // The optional remove() method is not supported. public void remove () { throw new UnsupportedOperationException ("remove"); } } }