1  import java.util.AbstractSet;
  2  import java.util.Iterator;
  3  import java.util.NoSuchElementException;
  4  
  5  /**
  6     A hash set stores an unordered collection of objects, using
  7     a hash table.
  8  */
  9  public class HashSet extends AbstractSet
 10  {
 11     private Node[] buckets;
 12     private int size;
 13  
 14     /**
 15        Constructs a hash table.
 16        @param bucketsLength the length of the buckets array
 17     */
 18     public HashSet(int bucketsLength)
 19     {
 20        buckets = new Node[bucketsLength];
 21        size = 0;
 22     }
 23  
 24     /**
 25        Tests for set membership.
 26        @param x an object
 27        @return true if x is an element of this set
 28     */
 29     public boolean contains(Object x)
 30     {
 31        int h = x.hashCode();
 32        if (h < 0) h = -h;
 33        h = h % buckets.length;
 34        
 35        Node current = buckets[h];
 36        while (current != null)
 37        {
 38           if (current.data.equals(x)) return true;
 39           current = current.next;
 40        }
 41        return false;
 42     }
 43  
 44     /**
 45        Adds an element to this set.
 46        @param x an object
 47        @return true if x is a new object, false if x was
 48        already in the set
 49     */
 50     public boolean add(Object x)
 51     {
 52        int h = x.hashCode();
 53        if (h < 0) h = -h;
 54        h = h % buckets.length;
 55        
 56        Node current = buckets[h];
 57        while (current != null)
 58        {
 59           if (current.data.equals(x)) 
 60              return false; // Already in the set
 61           current = current.next;
 62        }
 63        Node newNode = new Node();
 64        newNode.data = x;
 65        newNode.next = buckets[h];
 66        buckets[h] = newNode;
 67        size++;
 68        return true;
 69     }
 70  
 71     /**
 72        Removes an object from this set.
 73        @param x an object
 74        @return true if x was removed from this set, false
 75        if x was not an element of this set
 76     */
 77     public boolean remove(Object x)
 78     {
 79        int h = x.hashCode();
 80        if (h < 0) h = -h;
 81        h = h % buckets.length;
 82        
 83        Node current = buckets[h];
 84        Node previous = null;
 85        while (current != null)
 86        {
 87           if (current.data.equals(x)) 
 88           {
 89              if (previous == null) buckets[h] = current.next;
 90              else previous.next = current.next;
 91              size--;
 92              return true;
 93           }
 94           previous = current;
 95           current = current.next;
 96        }
 97        return false;
 98     }
 99  
100     /**
101        Returns an iterator that traverses the elements of this set.
102        @return a hash set iterator
103     */
104     public Iterator iterator()
105     {
106        return new HashSetIterator();
107     }
108  
109     /**
110        Gets the number of elements in this set.
111        @return the number of elements
112     */
113     public int size()
114     {
115        return size;
116     }
117  
118     class Node
119     {
120        public Object data;
121        public Node next;
122     }
123  
124     class HashSetIterator implements Iterator
125     {
126        private int bucket;
127        private Node current;
128        private int previousBucket;
129        private Node previous;
130  
131        /**
132           Constructs a hash set iterator that points to the
133           first element of the hash set.
134        */
135        public HashSetIterator()
136        {
137           current = null;
138           bucket = -1;
139           previous = null;
140           previousBucket = -1;
141        }
142        
143        public boolean hasNext()
144        {
145           if (current != null && current.next != null) 
146              return true;
147           for (int b = bucket + 1; b < buckets.length; b++)
148              if (buckets[b] != null) return true;
149           return false;
150        }
151         
152        public Object next()
153        {
154           previous = current;
155           previousBucket = bucket;
156           if (current == null || current.next == null) 
157           {
158              // Move to next bucket
159              bucket++;
160  
161              while (bucket < buckets.length 
162                    && buckets[bucket] == null)
163                 bucket++;
164              if (bucket < buckets.length) 
165                 current = buckets[bucket];
166              else
167                 throw new NoSuchElementException();
168           }
169           else // Move to next element in bucket
170              current = current.next; 
171           return current.data;
172        }
173  
174        public void remove()
175        {
176           if (previous != null && previous.next == current)
177              previous.next = current.next;
178           else if (previousBucket < bucket) 
179              buckets[bucket] = current.next; 
180           else
181              throw new IllegalStateException();
182           current = previous;
183           bucket = previousBucket;
184        }
185     }
186  }