home *** CD-ROM | disk | FTP | other *** search
/ ActiveX Programming Unleashed CD / AXU.iso / jgl_1_1 / jgl_1_1.exe / src / HashMap.java < prev    next >
Encoding:
Java Source  |  1996-09-10  |  22.4 KB  |  808 lines

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