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