View Javadoc
1   /*
2    * Copyright 2005 Ralf Joachim
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.castor.core.util;
15  
16  import java.util.Collection;
17  import java.util.Iterator;
18  import java.util.NoSuchElementException;
19  import java.util.Set;
20  
21  /**
22   * An IdentitySet that uses reference-equality instead of object-equality. According to its special
23   * function it violates some design contracts of the <code>Set</code> interface.
24   * 
25   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
26   * @version $Revision: 7491 $ $Date: 2006-04-13 10:49:49 -0600 (Thu, 13 Apr 2006) $
27   * @since 0.9.9
28   */
29  public final class IdentitySet implements Set {
30    // --------------------------------------------------------------------------
31  
32    /** Default number of buckets. */
33    private static final int DEFAULT_CAPACITY = 17;
34  
35    /** Default load factor. */
36    private static final float DEFAULT_LOAD = 0.75f;
37  
38    /** Default number of entries. */
39    private static final int DEFAULT_ENTRIES = (int) (DEFAULT_CAPACITY * DEFAULT_LOAD);
40  
41    /** Default factor to increment capacity. */
42    private static final int DEFAULT_INCREMENT = 2;
43  
44    /** First prime number to check is 3 as we prevent 2 by design. */
45    private static final int FIRST_PRIME_TO_CHECK = 3;
46  
47    // --------------------------------------------------------------------------
48  
49    /** Number of buckets. */
50    private int _capacity;
51  
52    /** Maximum number of entries before rehashing. */
53    private int _maximum;
54  
55    /** Buckets. */
56    private Entry[] _buckets;
57  
58    /** Number of map entries. */
59    private int _entries = 0;
60  
61    // --------------------------------------------------------------------------
62  
63    /**
64     * Construct a set with default capacity.
65     */
66    public IdentitySet() {
67      _capacity = DEFAULT_CAPACITY;
68      _maximum = DEFAULT_ENTRIES;
69      _buckets = new Entry[DEFAULT_CAPACITY];
70    }
71  
72    /**
73     * Construct a set with given capacity.
74     * 
75     * @param capacity The capacity of entries this set should be initialized with.
76     */
77    public IdentitySet(final int capacity) {
78      _capacity = capacity;
79      _maximum = (int) (capacity * DEFAULT_LOAD);
80      _buckets = new Entry[capacity];
81    }
82  
83    /**
84     * {@inheritDoc}
85     * 
86     * @see java.util.Collection#clear()
87     */
88    public void clear() {
89      _capacity = DEFAULT_CAPACITY;
90      _maximum = DEFAULT_ENTRIES;
91      _buckets = new Entry[DEFAULT_CAPACITY];
92      _entries = 0;
93    }
94  
95    /**
96     * {@inheritDoc}
97     * 
98     * @see java.util.Collection#size()
99     */
100   public int size() {
101     return _entries;
102   }
103 
104   /**
105    * {@inheritDoc}
106    * 
107    * @see java.util.Collection#isEmpty()
108    */
109   public boolean isEmpty() {
110     return (_entries == 0);
111   }
112 
113   /**
114    * {@inheritDoc}
115    * 
116    * @see java.util.Collection#add(java.lang.Object)
117    */
118   public boolean add(final Object key) {
119     int hash = System.identityHashCode(key);
120     int index = hash % _capacity;
121     if (index < 0) {
122       index = -index;
123     }
124 
125     Entry entry = _buckets[index];
126     Entry prev = null;
127     while (entry != null) {
128       if (entry.getKey() == key) {
129         // There is already a mapping for this key.
130         return false;
131       }
132       prev = entry;
133       entry = entry.getNext();
134     }
135     if (prev == null) {
136       // There is no previous entry in this bucket.
137       _buckets[index] = new Entry(key, hash);
138     } else {
139       // Next entry is empty so we have no mapping for this key.
140       prev.setNext(new Entry(key, hash));
141     }
142     _entries++;
143     if (_entries > _maximum) {
144       rehash();
145     }
146     return true;
147   }
148 
149   /**
150    * Rehash the map into a new array with increased capacity.
151    */
152   private void rehash() {
153     long nextCapacity = _capacity * DEFAULT_INCREMENT;
154     if (nextCapacity > Integer.MAX_VALUE) {
155       return;
156     }
157     nextCapacity = nextPrime(nextCapacity);
158     if (nextCapacity > Integer.MAX_VALUE) {
159       return;
160     }
161 
162     int newCapacity = (int) nextCapacity;
163     Entry[] newBuckets = new Entry[newCapacity];
164 
165     Entry entry = null;
166     Entry temp = null;
167     Entry next = null;
168     int newIndex = 0;
169 
170     for (int index = 0; index < _capacity; index++) {
171       entry = _buckets[index];
172       while (entry != null) {
173         next = entry.getNext();
174 
175         newIndex = entry.getHash() % newCapacity;
176         if (newIndex < 0) {
177           newIndex = -newIndex;
178         }
179 
180         temp = newBuckets[newIndex];
181         if (temp == null) {
182           // First entry of the bucket.
183           entry.setNext(null);
184         } else {
185           // Hook entry into beginning of the buckets chain.
186           entry.setNext(temp);
187         }
188         newBuckets[newIndex] = entry;
189 
190         entry = next;
191       }
192     }
193 
194     _capacity = newCapacity;
195     _maximum = (int) (newCapacity * DEFAULT_LOAD);
196     _buckets = newBuckets;
197   }
198 
199   /**
200    * Find next prime number greater than minimum.
201    * 
202    * @param minimum The minimum (exclusive) value of the next prime number.
203    * @return The next prime number greater than minimum.
204    */
205   private long nextPrime(final long minimum) {
206     long candidate = ((minimum + 1) / 2) * 2 + 1;
207     while (!isPrime(candidate)) {
208       candidate += 2;
209     }
210     return candidate;
211   }
212 
213   /**
214    * Check for prime number.
215    * 
216    * @param candidate Number to be checked for being a prime number.
217    * @return <code>true</code> if the given number is a prime number <code>false</code> otherwise.
218    */
219   private boolean isPrime(final long candidate) {
220     if ((candidate / 2) * 2 == candidate) {
221       return false;
222     }
223     long stop = candidate / 2;
224     for (long i = FIRST_PRIME_TO_CHECK; i < stop; i += 2) {
225       if ((candidate / i) * i == candidate) {
226         return false;
227       }
228     }
229     return true;
230   }
231 
232   /**
233    * {@inheritDoc}
234    * 
235    * @see java.util.Collection#contains(java.lang.Object)
236    */
237   public boolean contains(final Object key) {
238     int hash = System.identityHashCode(key);
239     int index = hash % _capacity;
240     if (index < 0) {
241       index = -index;
242     }
243 
244     Entry entry = _buckets[index];
245     while (entry != null) {
246       if (entry.getKey() == key) {
247         return true;
248       }
249       entry = entry.getNext();
250     }
251     return false;
252   }
253 
254   /**
255    * {@inheritDoc}
256    * 
257    * @see java.util.Collection#remove(java.lang.Object)
258    */
259   public boolean remove(final Object key) {
260     int hash = System.identityHashCode(key);
261     int index = hash % _capacity;
262     if (index < 0) {
263       index = -index;
264     }
265 
266     Entry entry = _buckets[index];
267     Entry prev = null;
268     while (entry != null) {
269       if (entry.getKey() == key) {
270         // Found the entry.
271         if (prev == null) {
272           // First element in bucket matches.
273           _buckets[index] = entry.getNext();
274         } else {
275           // Remove the entry from the chain.
276           prev.setNext(entry.getNext());
277         }
278         _entries--;
279         return true;
280       }
281       prev = entry;
282       entry = entry.getNext();
283     }
284     return false;
285   }
286 
287   /**
288    * {@inheritDoc}
289    * 
290    * @see java.util.Collection#iterator()
291    */
292   public Iterator iterator() {
293     return new IdentityIterator();
294   }
295 
296   /**
297    * {@inheritDoc}
298    * 
299    * @see java.util.Collection#toArray()
300    */
301   public Object[] toArray() {
302     Object[] result = new Object[_entries];
303 
304     int j = 0;
305     for (int i = 0; i < _capacity; i++) {
306       Entry entry = _buckets[i];
307       while (entry != null) {
308         result[j++] = entry.getKey();
309         entry = entry.getNext();
310       }
311     }
312 
313     return result;
314   }
315 
316   /**
317    * {@inheritDoc}
318    * 
319    * @see java.util.Collection#toArray(java.lang.Object[])
320    */
321   public Object[] toArray(final Object[] a) {
322     Object[] result = a;
323     if (result.length < _entries) {
324       result = (Object[]) java.lang.reflect.Array.newInstance(result.getClass().getComponentType(),
325           _entries);
326     }
327 
328     int j = 0;
329     for (int i = 0; i < _capacity; i++) {
330       Entry entry = _buckets[i];
331       while (entry != null) {
332         result[j++] = entry.getKey();
333         entry = entry.getNext();
334       }
335     }
336 
337     while (j < result.length) {
338       result[j++] = null;
339     }
340 
341     return result;
342   }
343 
344   /**
345    * In contrast with the design contract of the <code>Set</code> interface this method has not been
346    * implemented and throws a <code>UnsupportedOperationException</code>.
347    * 
348    * {@inheritDoc}
349    * 
350    * @see java.util.Set#containsAll
351    */
352   public boolean containsAll(final Collection c) {
353     throw new UnsupportedOperationException();
354   }
355 
356   /**
357    * This optional method has not been implemented for <code>IdentitySet</code> instead it throws a
358    * <code>UnsupportedOperationException</code> as defined in the <code>Set</code> interface.
359    * 
360    * {@inheritDoc}
361    * 
362    * @see java.util.Set#addAll
363    */
364   public boolean addAll(final Collection c) {
365     throw new UnsupportedOperationException();
366   }
367 
368   /**
369    * This optional method has not been implemented for <code>IdentitySet</code> instead it throws a
370    * <code>UnsupportedOperationException</code> as defined in the <code>Set</code> interface.
371    * 
372    * {@inheritDoc}
373    * 
374    * @see java.util.Set#removeAll
375    */
376   public boolean removeAll(final Collection c) {
377     throw new UnsupportedOperationException();
378   }
379 
380   /**
381    * This optional method has not been implemented for <code>IdentitySet</code> instead it throws a
382    * <code>UnsupportedOperationException</code> as defined in the <code>Set</code> interface.
383    * 
384    * {@inheritDoc}
385    * 
386    * @see java.util.Set#retainAll
387    */
388   public boolean retainAll(final Collection c) {
389     throw new UnsupportedOperationException();
390   }
391 
392   // --------------------------------------------------------------------------
393 
394   /**
395    * An entry of the <code>IdentitySet</code>.
396    */
397   public final class Entry {
398     /** Key of entry. */
399     private Object _key;
400 
401     /** Identity hashcode of key. */
402     private int _hash;
403 
404     /** Reference to next entry. */
405     private Entry _next = null;
406 
407     /**
408      * Construct an entry.
409      * 
410      * @param key Key of entry.
411      * @param hash Identity hashcode of key.
412      */
413     public Entry(final Object key, final int hash) {
414       _key = key;
415       _hash = hash;
416     }
417 
418     /**
419      * Get key of entry.
420      *
421      * @return Key of entry.
422      */
423     public Object getKey() {
424       return _key;
425     }
426 
427     /**
428      * Get identity hashcode of key.
429      *
430      * @return Identity hashcode of key.
431      */
432     public int getHash() {
433       return _hash;
434     }
435 
436     /**
437      * Set reference to next entry.
438      *
439      * @param next New reference to next entry.
440      */
441     public void setNext(final Entry next) {
442       _next = next;
443     }
444 
445     /**
446      * Get reference to next entry.
447      *
448      * @return Reference to next entry.
449      */
450     public Entry getNext() {
451       return _next;
452     }
453   }
454 
455   // --------------------------------------------------------------------------
456 
457   /**
458    * An iterator over all entries of the <code>IdentitySet</code>.
459    */
460   private class IdentityIterator implements Iterator {
461     /** Index of the current bucket. */
462     private int _index = 0;
463 
464     /** The next entry to be returned. <code>null</code> when there is none. */
465     private Entry _next = _buckets[0];
466 
467     /**
468      * Construct a iterator over all entries of the <code>IdentitySet</code>.
469      */
470     public IdentityIterator() {
471       if (_entries > 0) {
472         while ((_next == null) && (++_index < _capacity)) {
473           _next = _buckets[_index];
474         }
475       }
476     }
477 
478     /**
479      * {@inheritDoc}
480      * 
481      * @see java.util.Iterator#hasNext()
482      */
483     public boolean hasNext() {
484       return (_next != null);
485     }
486 
487     /**
488      * {@inheritDoc}
489      * 
490      * @see java.util.Iterator#next()
491      */
492     public Object next() {
493       Entry entry = _next;
494       if (entry == null) {
495         throw new NoSuchElementException();
496       }
497 
498       _next = entry.getNext();
499       while ((_next == null) && (++_index < _capacity)) {
500         _next = _buckets[_index];
501       }
502 
503       return entry.getKey();
504     }
505 
506     /**
507      * This optional method is not implemented for <code>IdentityIterator</code> instead it throws a
508      * <code>UnsupportedOperationException</code> as defined in the <code>Iterator</code> interface.
509      * 
510      * @see java.util.Iterator#remove()
511      */
512     public void remove() {
513       throw new UnsupportedOperationException();
514     }
515   }
516 
517   // --------------------------------------------------------------------------
518 }