home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / Extras / OSpace / jgl.exe / jgl_2_0 / src / COM / objectspace / jgl / HashMap.java < prev    next >
Encoding:
Java Source  |  1997-03-14  |  26.1 KB  |  923 lines

  1. // Copyright(c) 1996,1997 ObjectSpace, Inc.
  2. // Portions Copyright(c) 1995, 1996 Hewlett-Packard Company.
  3.  
  4. package COM.objectspace.jgl;
  5.  
  6. import java.util.Enumeration;
  7. import java.io.ObjectInputStream;
  8. import java.io.ObjectOutputStream;
  9. import java.io.IOException;
  10.  
  11. /**
  12.  * A HashMap is an associative container that manages a set of key/value pairs.
  13.  * A pair is stored in a hashing structure based on the hash code of its key,
  14.  * which is obtained by using the standard hashCode() function. Keys are
  15.  * matched using a BinaryPredicate which is EqualTo() by default. Duplicate
  16.  * keys are not allowed unless otherwise specified.
  17.  * <p>
  18.  * A HashMap is useful for implementing a collection of one-to-one or
  19.  * one-to-many mappings.
  20.  * <p>
  21.  * Insertion can invalidate iterators.
  22.  * <p>
  23.  * Removal can invalidate iterators.
  24.  * <p>
  25.  * @see COM.objectspace.jgl.BinaryPredicate
  26.  * @see COM.objectspace.jgl.examples.HashMapExamples
  27.  * @version 2.0.2
  28.  * @author ObjectSpace, Inc.
  29.  */
  30.  
  31. public class HashMap extends Map
  32.   {
  33.   static final int DEFAULT_SIZE = 203;
  34.   static final float DEFAULT_RATIO = 0.75F;
  35.  
  36.   BinaryPredicate comparator;
  37.   boolean allowDups; // does the map allow duplicate keys?
  38.   boolean expandActive = true; // will expand() have any effect?
  39.   transient int size; // # nodes.
  40.   transient HashMapNode[] buckets; // Array of buckets.
  41.   int length; // buckets.length, cached for speed.
  42.   int limit;
  43.   float ratio;
  44.  
  45.   /**
  46.    * Construct myself to be an empty HashMap that compares key using equals() and
  47.    * does not allow duplicates.
  48.    */
  49.   public HashMap()
  50.     {
  51.     this( new EqualTo(), false, DEFAULT_SIZE, DEFAULT_RATIO );
  52.     }
  53.  
  54.   /**
  55.    * Construct myself to be an empty HashMap that compares keys using equals() and
  56.    * conditionally allows duplicates.
  57.    * @param allowDuplicates true if duplicates are allowed.
  58.    */
  59.   public HashMap( boolean allowDuplicates )
  60.     {
  61.     this( new EqualTo(), allowDuplicates, DEFAULT_SIZE, DEFAULT_RATIO );
  62.     }
  63.  
  64.   /**
  65.    * Construct myself to be an empty HashMap that compares keys using the specified
  66.    * binary predicate and does not allow duplicates.
  67.    * @param comparator The predicate for comparing keys.
  68.    */
  69.   public HashMap( BinaryPredicate comparator )
  70.     {
  71.     this( comparator, false, DEFAULT_SIZE, DEFAULT_RATIO );
  72.     }
  73.  
  74.   /**
  75.    * Construct myself to be an empty HashMap that compares keys using the specified
  76.    * binary predicate and conditionally allows duplicates.
  77.    * @param comparator The predicate for comparing keys.
  78.    * @param allowDuplicates true if duplicates are allowed.
  79.    */
  80.   public HashMap( BinaryPredicate comparator, boolean allowDuplicates )
  81.     {
  82.     this( comparator, allowDuplicates, DEFAULT_SIZE, DEFAULT_RATIO );
  83.     }
  84.  
  85.   /**
  86.    * Construct myself to be an empty HashMap that compares keys using the specified
  87.    * binary predicate. The initial buckets and load ratio must also be specified.
  88.    * @param comparator The predicate for comparing keys.
  89.    * @param capacity The initial number of hash buckets to reserve.
  90.    * @param loadRatio The maximum load ratio.
  91.    */
  92.   public HashMap( BinaryPredicate comparator, int capacity, float loadRatio )
  93.     {
  94.     this( comparator, false, capacity, loadRatio );
  95.     }
  96.  
  97.   /**
  98.    * Construct myself to be an empty HashMap that compares keys using the specified
  99.    * binary predicate and conditionally allows duplicates. The initial buckets and
  100.    * load ratio must also be specified.
  101.    * @param comparator The predicate for comparing keys.
  102.    * @param allowDuplicates true if duplicates are allowed.
  103.    * @param capacity The initial number of hash buckets to reserve.
  104.    * @param loadRatio The maximum load ratio.
  105.    */
  106.   public HashMap( BinaryPredicate comparator, boolean allowDuplicates, int capacity, float loadRatio )
  107.     {
  108.     this.comparator = comparator;
  109.     ratio = loadRatio;
  110.     length = capacity;
  111.     limit = (int)( length * ratio );
  112.     buckets = new HashMapNode[ length ];
  113.     allowDups = allowDuplicates;
  114.     }
  115.  
  116.   /**
  117.    * Construct myself to be a shallow copy of an existing HashMap.
  118.    * @param map The HashMap to copy.
  119.    */
  120.   public HashMap( HashMap map )
  121.     {
  122.     copy( map );
  123.     }
  124.  
  125.   /**
  126.    * Return true if I allow duplicate keys.
  127.    */
  128.   public boolean allowsDuplicates()
  129.     {
  130.     return allowDups;
  131.     }
  132.  
  133.   /**
  134.    * Return my comparator.
  135.    */
  136.   public BinaryPredicate getComparator()
  137.     {
  138.     return comparator;
  139.     }
  140.  
  141.   /**
  142.    * Return my load ratio.
  143.    */
  144.   public float getLoadRatio()
  145.     {
  146.     return ratio;
  147.     }
  148.  
  149.   /**
  150.    * Return a shallow copy of myself.
  151.    */
  152.   public synchronized Object clone()
  153.     {
  154.     return new HashMap( this );
  155.     }
  156.  
  157.   /**
  158.    * Become a shallow copy of an existing HashMap.
  159.    * @param map The HashMap that I shall become a shallow copy of.
  160.    */
  161.   public synchronized void copy( HashMap map )
  162.     {
  163.     synchronized( map )
  164.       {
  165.       comparator = map.comparator;
  166.       length = map.length;
  167.       ratio = map.ratio;
  168.       limit = map.limit;
  169.       size = map.size();
  170.       buckets = new HashMapNode[ length ];
  171.       allowDups = map.allowDups;
  172.  
  173.       for ( int i = 0; i < length; i++ )
  174.         {
  175.         HashMapNode oldNode = null;
  176.         HashMapNode node = map.buckets[ i ];
  177.  
  178.         while ( node != null )
  179.           {
  180.           HashMapNode newNode = new HashMapNode();
  181.           newNode.key = node.key;
  182.           newNode.value = node.value;
  183.           newNode.hash = node.hash;
  184.  
  185.           if ( buckets[ i ] == null )
  186.             buckets[ i ] = newNode;
  187.           else
  188.             oldNode.next = newNode;
  189.  
  190.           oldNode = newNode;
  191.           node = node.next;
  192.           }
  193.         }
  194.       }
  195.     }
  196.  
  197.   /**
  198.    * Return a string that describes me.
  199.    */
  200.   public synchronized String toString()
  201.     {
  202.     return Printing.toString( this, "HashMap" );
  203.     }
  204.  
  205.   /**
  206.    * Return an Enumeration to my values.
  207.    */
  208.   public synchronized Enumeration elements()
  209.     {
  210.     return new HashMapIterator( first(), this, HashMapIterator.VALUE );
  211.     }
  212.  
  213.   /**
  214.    * Return an iterator positioned at my first pair.
  215.    */
  216.   public ForwardIterator start()
  217.     {
  218.     return begin();
  219.     }
  220.  
  221.   /**
  222.    * Return an iterator positioned immediately afer my last pair.
  223.    */
  224.   public ForwardIterator finish()
  225.     {
  226.     return end();
  227.     }
  228.  
  229.   /**
  230.    * Return an iterator positioned at my first pair.
  231.    */
  232.   public synchronized HashMapIterator begin()
  233.     {
  234.     return new HashMapIterator( first(), this, HashMapIterator.PAIR );
  235.     }
  236.  
  237.   /**
  238.    * Return an iterator positioned immediately after my last pair.
  239.    */
  240.   public synchronized HashMapIterator end()
  241.     {
  242.     return new HashMapIterator( null, this, HashMapIterator.PAIR );
  243.     }
  244.  
  245.   /**
  246.    * Return true if I contain no entries.
  247.    */
  248.   public boolean isEmpty()
  249.     {
  250.     return size == 0;
  251.     }
  252.  
  253.   /**
  254.    * Return the number of entries that I contain.
  255.    */
  256.   public int size()
  257.     {
  258.     return size;
  259.     }
  260.  
  261.   /**
  262.    * Return the maximum number of entries that I can contain.
  263.    */
  264.   public int maxSize()
  265.     {
  266.     return Allocator.maxSize();
  267.     }
  268.  
  269.   /**
  270.    * Return true if I'm equal to another object.
  271.    * @param object The object to compare myself against.
  272.    */
  273.   public boolean equals( Object object )
  274.     {
  275.     return object instanceof HashMap && equals( (HashMap)object );
  276.     }
  277.  
  278.   /**
  279.    * Return true if I contain exactly the same key/value pairs as another HashMap.
  280.    * Use equals() to compare values.
  281.    * @param map The HashMap to compare myself against.
  282.    */
  283.   public synchronized boolean equals( HashMap map )
  284.     {
  285.     synchronized( map )
  286.       {
  287.       if ( size() != map.size() )
  288.         return false;
  289.  
  290.       if ( allowDups )
  291.         {
  292.         Object previous = null;
  293.  
  294.         for ( HashMapIterator iterator = begin(); iterator.hasMoreElements(); iterator.advance() )
  295.           {
  296.           Object key = iterator.key();
  297.  
  298.           // Execute the following code for each unique key in the source.
  299.           if ( previous == null || !key.equals( previous ) )
  300.             {
  301.             previous = key;
  302.             if ( !same( values( key ), map.values( key ) ) )
  303.               return false;
  304.             }
  305.           }
  306.         }
  307.       else
  308.         {
  309.         for ( HashMapIterator iterator = begin(); iterator.hasMoreElements(); iterator.advance() )
  310.           {
  311.           Object value = map.get( iterator.key() );
  312.  
  313.           if ( value == null || !value.equals( iterator.value() ) )
  314.             return false;
  315.           }
  316.         }
  317.       }
  318.     return true;
  319.     }
  320.  
  321.   /**
  322.    * Return my hash code for support of hashing containers
  323.    */
  324.   public synchronized int hashCode()
  325.     {
  326.     ForwardIterator start = new HashMapIterator( first(), this, HashMapIterator.KEY );
  327.     ForwardIterator end = new HashMapIterator( null, this, HashMapIterator.KEY );
  328.     return Hashing.unorderedHash( start, end );
  329.     }
  330.  
  331.   /**
  332.    * Swap my contents with another HashMap.
  333.    * @param map The HashMap that I will swap my contents with.
  334.    */
  335.   public synchronized void swap( HashMap map )
  336.     {
  337.     synchronized( map )
  338.       {
  339.       int tmpSize = size;
  340.       size = map.size();
  341.       map.size( tmpSize );
  342.  
  343.       HashMapNode[] tmpBuckets = buckets;
  344.       buckets = map.buckets;
  345.       map.buckets = tmpBuckets;
  346.  
  347.       int tmpLength = length;
  348.       length = map.length;
  349.       map.length = tmpLength;
  350.  
  351.       int tmpLimit = limit;
  352.       limit = map.limit;
  353.       map.limit = tmpLimit;
  354.  
  355.       float tmpRatio = ratio;
  356.       ratio = map.ratio;
  357.       map.ratio = tmpRatio;
  358.  
  359.       boolean tmpDups = allowDups;
  360.       allowDups = map.allowDups;
  361.       map.allowDups = tmpDups;
  362.       }
  363.     }
  364.  
  365.   /**
  366.    * Remove all of my elements.
  367.    */
  368.   public synchronized void clear()
  369.     {
  370.     buckets = new HashMapNode[ length ];
  371.     size = 0;
  372.     }
  373.  
  374.   /**
  375.    * Remove all key/value pairs that match a particular key.
  376.    * @param key The key of the pair(s) to be removed.
  377.    * @return Return the first object removed or null if not changed.
  378.    */
  379.   public Object remove( Object key )
  380.     {
  381.     return removeAux( key, size ).first;
  382.     }
  383.  
  384.   /**
  385.    * Remove at most a given number of key/value pairs that match a particular key.
  386.    * @param key The key of the pair(s) to be removed.
  387.    * @param count The maximum number of the pair(s) to remove.
  388.    * @return Return the number of pairs removed.
  389.    */
  390.   public int remove( Object key, int count )
  391.     {
  392.     Pair result = removeAux( key, count );
  393.     return ( (Number)result.second ).intValue();
  394.     }
  395.  
  396.   synchronized Pair removeAux( Object key, int maximum )
  397.     {
  398.     if ( maximum > 0 )
  399.       {
  400.       int hash = key.hashCode() & 0x7FFFFFFF;
  401.       int probe = hash % length;
  402.  
  403.       for ( HashMapNode node = buckets[ probe ], previous = null; node != null; previous = node, node = node.next )
  404.         if ( node.hash == hash && comparator.execute( node.key, key ) )
  405.           {
  406.           int count = 1;
  407.           --maximum;
  408.  
  409.           HashMapNode end = node.next;
  410.           Object value = node.value; // we only want the first one.
  411.  
  412.           if ( allowDups )
  413.             {
  414.             while ( maximum > 0 && end != null && end.hash == hash && comparator.execute( end.key, key ) )
  415.               {
  416.               ++count;
  417.               --maximum;
  418.               end = end.next;
  419.               }
  420.             }
  421.  
  422.           if ( previous == null )
  423.             buckets[ probe ] = end;
  424.           else
  425.             previous.next = end;
  426.  
  427.           size -= count;
  428.           return new Pair( value, new Integer( count ) );
  429.           }
  430.       }
  431.     return new Pair( null, new Integer( 0 ) );
  432.     }
  433.  
  434.   /**
  435.    * Remove the element at a particular position.
  436.    * @param e An Enumeration positioned at the element to remove.
  437.    * @exception IllegalArgumentException is the Enumeration isn't a
  438.    * HashMapIterator for this HashMap object.
  439.    * @return Return the value associated with the enumeration.
  440.    */
  441.   public synchronized Object remove( Enumeration e )
  442.     {
  443.     if ( ! (e instanceof HashMapIterator) )
  444.       throw new IllegalArgumentException( "Enumeration not a HashMapIterator" );
  445.  
  446.     if ( ((HashMapIterator)e).myHashMap != this )
  447.       throw new IllegalArgumentException( "Enumeration not for this HashMap" );
  448.  
  449.     HashMapNode target = ( (HashMapIterator)e ).myNode;
  450.     int probe = target.hash % length;
  451.     HashMapNode node = buckets[ probe ];
  452.  
  453.     if ( target == node )
  454.       {
  455.       buckets[ probe ] = target.next;
  456.       }
  457.     else
  458.       {
  459.       while ( node.next != target )
  460.         node = node.next;
  461.  
  462.       node.next = target.next;
  463.       }
  464.  
  465.     --size;
  466.     return target == null
  467.       ? null
  468.       : target.value;
  469.     }
  470.  
  471.   /**
  472.    * Remove the elements within a specified range.
  473.    * @param first An Enumeration positioned at the first element to remove.
  474.    * @param last An Enumeration positioned immediately after the last element to remove.
  475.    * @exception IllegalArgumentException is the Enumeration isn't a
  476.    * HashMapIterator for this HashMap object.
  477.    * @return Return the number of pairs removed.
  478.    */
  479.   public synchronized int remove( Enumeration first, Enumeration last )
  480.     {
  481.     if ( ( ! (first instanceof HashMapIterator) ) ||
  482.         ( ! (last instanceof HashMapIterator) ) )
  483.       throw new IllegalArgumentException( "Enumeration not a HashMapIterator" );
  484.  
  485.     if ( ( ((HashMapIterator)first).myHashMap != this ) ||
  486.         ( ((HashMapIterator)last).myHashMap != this ) )
  487.       throw new IllegalArgumentException( "Enumeration not for this HashMap" );
  488.  
  489.     HashMapIterator begin = (HashMapIterator)first;
  490.     HashMapIterator end = (HashMapIterator)last;
  491.  
  492.     int count = 0;
  493.     while ( !begin.equals( end ) )
  494.       {
  495.       HashMapIterator next = new HashMapIterator( begin );
  496.       next.advance();
  497.       remove( begin );
  498.       begin = next;
  499.       ++count;
  500.       }
  501.     return count;
  502.     }
  503.  
  504.   /**
  505.    * Find the first key/value pair based on its key and return its position.
  506.    * If the key is not found, return end().
  507.    * @param key The key to locate.
  508.    */
  509.   public synchronized HashMapIterator find( Object key )
  510.     {
  511.     int hash = key.hashCode() & 0x7FFFFFFF;
  512.  
  513.     for ( HashMapNode node = buckets[ hash % length ]; node != null; node = node.next )
  514.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  515.         return new HashMapIterator( node, this, HashMapIterator.PAIR );
  516.  
  517.     return new HashMapIterator( null, this, HashMapIterator.PAIR );
  518.     }
  519.  
  520.   /**
  521.    * Return the number of key/value pairs that match a particular key.
  522.    * @param key The key to match against.
  523.    */
  524.   public synchronized int count( Object key )
  525.     {
  526.     int hash = key.hashCode() & 0x7FFFFFFF;
  527.  
  528.     for ( HashMapNode node = buckets[ hash % length ]; node != null; node = node.next )
  529.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  530.         {
  531.         if ( allowDups )
  532.           {
  533.           int n = 1;
  534.           node = node.next;
  535.  
  536.           while ( node != null && hash == node.hash && comparator.execute( node.key, key ) )
  537.             {
  538.             ++n;
  539.             node = node.next;
  540.             }
  541.  
  542.           return n;
  543.           }
  544.         else
  545.           {
  546.           return 1;
  547.           }
  548.         }
  549.  
  550.     return 0;
  551.     }
  552.  
  553.   /**
  554.    * Return the number of values that match a given object.
  555.    * @param value The value to match against.
  556.    */
  557.   public synchronized int countValues( Object value )
  558.     {
  559.     return Counting.count
  560.       (
  561.       new HashMapIterator( first(), this, HashMapIterator.VALUE ),
  562.       new HashMapIterator( null, this, HashMapIterator.VALUE ),
  563.       value
  564.       );
  565.     }
  566.  
  567.   /**
  568.    * Return the value associated with key, or null if the key does not exist.
  569.    * @param key The key to search against.
  570.    */
  571.   public synchronized Object get( Object key )
  572.     {
  573.     int hash = key.hashCode() & 0x7FFFFFFF;
  574.  
  575.     for ( HashMapNode node = buckets[ hash % length ]; node != null; node = node.next )
  576.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  577.         return node.value;
  578.  
  579.     return null;
  580.     }
  581.  
  582.   /**
  583.    * If the key doesn't exist, associate the value with the key and return null,
  584.    * otherwise replace the first value associated with the key and return the old value.
  585.    * @param key The key.
  586.    * @param value The value.
  587.    * @exception NullPointerException If the key or value are equal to null
  588.    */
  589.   public synchronized Object put( Object key, Object value )
  590.     {
  591.     if ( key == null || value == null )
  592.       throw new NullPointerException();
  593.  
  594.     int hash = key.hashCode() & 0x7FFFFFFF;
  595.     int probe = hash % length;
  596.  
  597.     // find if key already exists first
  598.     for ( HashMapNode node = buckets[ probe ]; node != null; node = node.next )
  599.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  600.         {
  601.         // replace old version & return it
  602.         node.key = key;
  603.         Object previous = node.value;
  604.         node.value = value;
  605.         return previous;
  606.         }
  607.  
  608.     // key doesn't exists, add appropriately
  609.     HashMapNode newNode = new HashMapNode();
  610.     newNode.key = key;
  611.     newNode.value = value;
  612.     newNode.hash = hash;
  613.     newNode.next = buckets[ probe ];
  614.     buckets[ probe ] = newNode;
  615.  
  616.     if ( ++size > limit )
  617.       expand();
  618.  
  619.     return null;
  620.     }
  621.  
  622.   /**
  623.    * Assume that the specified object is a Pair whose first field is a key and whose
  624.    * second field is a value. If the key doesn't exist or duplicates are allowed,
  625.    * associate the value with the key and return null, otherwise don't modify the map and
  626.    * return the current value associated with the key.
  627.    * @param object The pair to add.
  628.    * @exception java.lang.IllegalArgumentException If the object is not a Pair
  629.    * @exception java.lang.NullPointerException If the object is null or if the first
  630.    * or second items in the pair are null.
  631.    */
  632.   public Object add( Object object )
  633.     {
  634.     if ( object == null )
  635.       throw new NullPointerException();
  636.  
  637.     if ( !(object instanceof Pair) )
  638.       throw new IllegalArgumentException( "object is not pair" );
  639.  
  640.     if ( ((Pair)object).first == null || ((Pair)object).second == null )
  641.       throw new NullPointerException();
  642.  
  643.     Pair pair = (Pair) object;
  644.     return add( pair.first, pair.second );
  645.     }
  646.  
  647.   /**
  648.    * If the key doesn't exist or duplicates are allowed, associate the value with the
  649.    * key and return null, otherwise don't modify the map and return the current value
  650.    * associated with the key.
  651.    * @param key The key.
  652.    * @param value The value.
  653.    * @exception java.lang.NullPointerException If the key or value is null.
  654.    */
  655.   public synchronized Object add( Object key, Object value )
  656.     {
  657.     if ( key == null || value == null )
  658.       throw new NullPointerException();
  659.  
  660.     int hash = key.hashCode() & 0x7FFFFFFF;
  661.     int probe = hash % length;
  662.  
  663.     // find if key already exists first
  664.     for ( HashMapNode node = buckets[ probe ]; node != null; node = node.next )
  665.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  666.         {
  667.         if ( allowDups )
  668.           {
  669.           // duplicate key, add this pair to end and return success.
  670.           HashMapNode newNode = new HashMapNode();
  671.           newNode.key = key;
  672.           newNode.value = value;
  673.           newNode.hash = hash;
  674.           newNode.next = node.next;
  675.           node.next = newNode;
  676.  
  677.           if ( ++size > limit )
  678.             expand();
  679.  
  680.           return null;
  681.           }
  682.         else
  683.           {
  684.           // return the value of the key/value that already exists. DO NOT add
  685.           return node.value;
  686.           }
  687.         }
  688.  
  689.     // key doesn't exists, add appropriately
  690.     HashMapNode newNode = new HashMapNode();
  691.     newNode.key = key;
  692.     newNode.value = value;
  693.     newNode.hash = hash;
  694.     newNode.next = buckets[ probe ];
  695.     buckets[ probe ] = newNode;
  696.  
  697.     if ( ++size > limit )
  698.       expand();
  699.  
  700.     return null;
  701.     }
  702.  
  703.   /**
  704.    * Return an Enumeration of all my keys.
  705.    */
  706.   public synchronized Enumeration keys()
  707.     {
  708.     return new HashMapIterator( first(), this, HashMapIterator.KEY );
  709.     }
  710.  
  711.   /**
  712.    * Return an Enumeration of all my keys that are associated with a particular value.
  713.    * @param value The value to match.
  714.    */
  715.   public synchronized Enumeration keys( Object value )
  716.     {
  717.     Array array = new Array();
  718.  
  719.     for ( HashMapIterator iterator = begin(); iterator.hasMoreElements(); iterator.advance() )
  720.       if ( iterator.value().equals( value ) )
  721.         array.pushBack( iterator.key() );
  722.  
  723.     return array.elements();
  724.     }
  725.  
  726.   /**
  727.    * Return an Enumeration of all my values that are associated with a particular key.
  728.    * @param key The key to match.
  729.    */
  730.   public synchronized Enumeration values( Object key )
  731.     {
  732.     Array array = new Array();
  733.  
  734.     for ( HashMapIterator iterator = begin(); iterator.hasMoreElements(); iterator.advance() )
  735.       if ( comparator.execute( iterator.key(), key ) )
  736.         array.pushBack( iterator.value() );
  737.  
  738.     return array.elements();
  739.     }
  740.  
  741.   /**
  742.    * Return an iterator positioned at the first location that a
  743.    * pair with a specified key could be inserted without violating the ordering
  744.    * criteria. If no such location is found, return an iterator positioned at end().
  745.    * @param key The key.
  746.    */
  747.   public synchronized HashMapIterator lowerBound( Object key )
  748.     {
  749.     return (HashMapIterator)equalRange( key ).begin;
  750.     }
  751.  
  752.   /**
  753.    * Return an iterator positioned at the last location that
  754.    * a pair with a specified key could be inserted without violating the ordering
  755.    * criteria. If no such location is found, return an iterator positioned at end().
  756.    * @param key The key.
  757.    */
  758.   public synchronized HashMapIterator upperBound( Object key )
  759.     {
  760.     return (HashMapIterator)equalRange( key ).end;
  761.     }
  762.  
  763.   /**
  764.    * Return a range whose first element is an iterator positioned
  765.    * at the first occurence of a specific key and whose second element is an
  766.    * iterator positioned immediately after the last occurence of that key.
  767.    * Note that all key inbetween these iterators will also match the specified
  768.    * key. If no matching key is found, both ends of the range will be the
  769.    * same.
  770.    * @param object The key whose bounds are to be found.
  771.    */
  772.   public synchronized Range equalRange( Object key )
  773.     {
  774.     int hash = key.hashCode() & 0x7FFFFFFF;
  775.  
  776.     for ( HashMapNode node = buckets[ hash % length ]; node != null; node = node.next )
  777.       if ( hash == node.hash && comparator.execute( node.key, key ) )
  778.         {
  779.         HashMapNode begin = node;
  780.         node = node.next == null ? next( node ) : node.next;  // fixed 8/9/96 pdj
  781.  
  782.         while ( node != null && hash == node.hash && comparator.execute( node.key, key ) )
  783.           node = node.next == null ? next( node ) : node.next;
  784.  
  785.         return new Range( new HashMapIterator( begin, this, HashMapIterator.PAIR ),
  786.                           new HashMapIterator( node, this, HashMapIterator.PAIR ) );
  787.         }
  788.  
  789.     return new Range( end(), end() );
  790.     }
  791.  
  792.   private HashMapNode first()
  793.     {
  794.     if ( size > 0 )
  795.       for ( int i = 0; i < length; i++ )
  796.         if ( buckets[ i ] != null )
  797.           return buckets[ i ];
  798.  
  799.     return null;
  800.     }
  801.  
  802.   private HashMapNode next( HashMapNode node )
  803.     {
  804.     for ( int i = ( node.hash % length ) + 1; i < length; i++ )
  805.       if ( buckets[ i ] != null )
  806.         return buckets[ i ];
  807.  
  808.     return null;
  809.     }
  810.  
  811.   /**
  812.    * Return true if adding an object to myself could result in an expansion
  813.    * of the number of hash buckets I currently use.
  814.    */
  815.   public boolean expansionAllowed()
  816.     {
  817.     return expandActive;
  818.     }
  819.  
  820.   /**
  821.    * Enable or disable the current expansion mode.  If disabled, no new
  822.    * hash buckets will ever be created regardless of my size.
  823.    * @param allow The new expansion mode.
  824.    */
  825.   public synchronized void allowExpansion( boolean allow )
  826.     {
  827.     expandActive = allow;
  828.     }
  829.  
  830.   private void expand()
  831.     {
  832.     if ( !expansionAllowed() )
  833.       return;
  834.  
  835.     int newLength = length * 2 + 1;
  836.     HashMapNode[] newBuckets = new HashMapNode[ newLength ];
  837.  
  838.     for ( int i = 0; i < length; i++ )
  839.       {
  840.       HashMapNode node = buckets[ i ];
  841.  
  842.       while ( node != null )
  843.         {
  844.         HashMapNode current = node;
  845.         node = node.next;
  846.         int probe = current.hash % newLength;
  847.         current.next = newBuckets[ probe ];
  848.         newBuckets[ probe ] = current;
  849.         }
  850.       }
  851.  
  852.     buckets = newBuckets;
  853.     length = newLength;
  854.     limit = (int)( length * ratio );
  855.     }
  856.  
  857.   private boolean same( Enumeration src, Enumeration dst )
  858.     {
  859.     Array srcValues = new Array();
  860.     Array dstValues = new Array();
  861.  
  862.     while ( src.hasMoreElements() )
  863.       srcValues.add( src.nextElement() );
  864.  
  865.     while ( dst.hasMoreElements() )
  866.       dstValues.add( dst.nextElement() );
  867.  
  868.     if ( srcValues.size() != dstValues.size() )
  869.       return false;
  870.  
  871.     for ( int i = 0; i < srcValues.size(); i++ )
  872.       {
  873.       Object x = srcValues.at( i );
  874.       int srcCount = 0;
  875.       int dstCount = 0;
  876.  
  877.       // Count the number of times 'x' occurs in each array of values.
  878.       for ( int j = 0; j < dstValues.size(); j++ )
  879.         {
  880.         if ( srcValues.at( j ).equals( x ) )
  881.           ++srcCount;
  882.  
  883.         if ( dstValues.at( j ).equals( x ) )
  884.           ++dstCount;
  885.         }
  886.  
  887.       if ( srcCount != dstCount )
  888.         return false;
  889.       }
  890.  
  891.     return true;
  892.     }
  893.  
  894.   private void size( int newsize )
  895.     {
  896.     size = newsize;
  897.     }
  898.  
  899.   private synchronized void writeObject( ObjectOutputStream stream ) throws IOException
  900.     {
  901.     stream.defaultWriteObject();
  902.     stream.writeInt( size );
  903.     Copying.copy( begin(), end(), new ObjectOutputStreamIterator( stream ) );
  904.     }
  905.  
  906.   private void readObject( ObjectInputStream stream ) throws IOException, ClassNotFoundException
  907.     {
  908.     stream.defaultReadObject();
  909.     buckets = new HashMapNode[ length ];
  910.     int count = stream.readInt();
  911.     while ( count-- > 0 )
  912.       add( stream.readObject() );
  913.     }
  914.  
  915.   final class HashMapNode
  916.     {
  917.     Object key;
  918.     Object value;
  919.     int hash;
  920.     HashMapNode next;
  921.     }
  922.   }
  923.