View Javadoc

1   /*
2    * J.A.D.E. Java(TM) Addition to Default Environment.
3    * Latest release available at http://jade.dautelle.com/
4    * This class is public domain (not copyrighted).
5    */
6   package ch.twiddlefinger.inet.rewinder.model.parser.conversion;
7   
8   import java.io.IOException;
9   import java.io.ObjectInputStream;
10  import java.io.ObjectOutputStream;
11  import java.io.Serializable;
12  
13  import java.util.AbstractCollection;
14  import java.util.AbstractSet;
15  import java.util.Collection;
16  import java.util.Iterator;
17  import java.util.ListIterator;
18  import java.util.Map;
19  import java.util.NoSuchElementException;
20  import java.util.Set;
21  
22  
23  /***
24   * <p> This class represents a <code>Map</code> collection with real-time
25   *     behavior. Unless the map's size exceeds its current capacity,
26   *     no dynamic memory allocation is ever performed and response time is
27   *     <b>extremely fast</b> and <b>consistent</b>.</p>
28   *
29   * <p> Our <a href="http://jade.dautelle.com/doc/benchmark.txt">benchmark</a>
30   *     indicates that {@link FastMap#put FastMap.put(key, value)} is up to
31   *     <b>5x faster</b> than <code>java.util.HashMap.put(key, value)</code>.
32   *     This difference is mostly due to the cost of the <code>Map.Entry</code>
33   *     allocations that {@link FastMap} avoids by recycling its entries
34   *     (see note below).</p>
35   *
36   * <p> {@link FastMap} has a predictable iteration order, which is the order
37   *     in which keys were inserted into the map (similar to
38   *     <code>java.util.LinkedHashMap</code> collection class).
39   *     A bi-directional list iterator over the map entries is also
40   *     {@link #fastIterator provided}, this iterator can be moved
41   *     to the {@link FastIterator#toFirst first} or to the
42   *     {@link FastIterator#toLast last} entry for unlimited reuse.</p>
43   *
44   * <p> Applications may change the resizing policy  of {@link FastMap}
45   *     by overriding the {@link #sizeChanged} method. For example, to reduce
46   *     memory footprint, the map's capacity could be maitained at ±50% of
47   *     the current map's size.</p>
48   *
49   * <p> This implementation is not synchronized. Multiple threads accessing
50   *     or modifying the collection must be synchronized externally.</p>
51   *
52   * <p> <b>Note:</b> To avoid dynamic memory allocations, {@link FastMap}
53   *     maintains an internal pool of <code>Map.Entry</code> objects. The size
54   *     of the pool is determined by the map's capacity. When an entry is
55   *     removed from the map, it is automatically restored to the pool.</p>
56   *
57   * <p><i> This class is <b>public domain</b> (not copyrighted).</i></p>
58   *
59   * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
60   * @version 6.0, January 18 2004
61   */
62  public class FastMap implements Map, Cloneable, Serializable {
63      /***
64       * Holds the map's hash table.
65       */
66      private transient EntryImpl[] _entries;
67  
68      /***
69       * Holds the map's current capacity.
70       */
71      private transient int _capacity;
72  
73      /***
74       * Holds the hash code mask.
75       */
76      private transient int _mask;
77  
78      /***
79       * Holds the first pool entry (linked list).
80       */
81      private transient EntryImpl _poolFirst;
82  
83      /***
84       * Holds the first map entry (linked list).
85       */
86      private transient EntryImpl _mapFirst;
87  
88      /***
89       * Holds the last map entry (linked list).
90       */
91      private transient EntryImpl _mapLast;
92  
93      /***
94       * Holds the current size.
95       */
96      private transient int _size;
97      private final FastIterator _fastIterator = new FastIterator();
98      private transient Values _values;
99      private transient EntrySet _entrySet;
100     private transient KeySet _keySet;
101 
102     /***
103      * Creates a {@link FastMap} with a capacity of <code>16</code> entries.
104      */
105     public FastMap() {
106         initialize(16);
107     }
108 
109     /***
110      * Creates a {@link FastMap}, copy of the specified <code>Map</code>.
111      * If the specified map is not an instance of {@link FastMap}, the
112      * newly created map has a capacity set to the specified map's size.
113      * The copy has the same order as the original, regardless of the original
114      * map's implementation:<pre>
115      *     TreeMap dictionary = ...;
116      *     FastMap dictionaryLookup = new FastMap(dictionary);
117      * </pre>
118      *
119      * @param  map the map whose mappings are to be placed in this map.
120      */
121     public FastMap(Map map) {
122         int capacity = (map instanceof FastMap) ? ((FastMap) map).capacity()
123                                                 : map.size();
124         initialize(capacity);
125         putAll(map);
126     }
127 
128     /***
129      * Creates a {@link FastMap} with the specified capacity. Unless the
130      * capacity is exceeded, operations on this map do not allocate entries.
131      * For optimum performance, the capacity should be of the same order
132      * of magnitude or larger than the expected map's size.
133      *
134      * @param  capacity the number of buckets in the hash table; it also
135      *         defines the number of pre-allocated entries.
136      */
137     public FastMap(int capacity) {
138         initialize(capacity);
139     }
140 
141     /***
142      * Returns the number of key-value mappings in this {@link FastMap}.
143      *
144      * @return this map's size.
145      */
146     public int size() {
147         return _size;
148     }
149 
150     /***
151      * Returns the capacity of this {@link FastMap}. The capacity defines
152      * the number of buckets in the hash table, as well as the maximum number
153      * of entries the map may contain without allocating memory.
154      *
155      * @return this map's capacity.
156      */
157     public int capacity() {
158         return _capacity;
159     }
160 
161     /***
162      * Indicates if this {@link FastMap} contains no key-value mappings.
163      *
164      * @return <code>true</code> if this map contains no key-value mappings;
165      *         <code>false</code> otherwise.
166      */
167     public boolean isEmpty() {
168         return _size == 0;
169     }
170 
171     /***
172      * Indicates if this {@link FastMap} contains a mapping for the specified
173      * key.
174      *
175      * @param   key the key whose presence in this map is to be tested.
176      * @return <code>true</code> if this map contains a mapping for the
177      *         specified key; <code>false</code> otherwise.
178      * @throws NullPointerException if the key is <code>null</code>.
179      */
180     public boolean containsKey(Object key) {
181         EntryImpl entry = _entries[keyHash(key) & _mask];
182 
183         while (entry != null) {
184             if (key.equals(entry._key)) {
185                 return true;
186             }
187 
188             entry = entry._next;
189         }
190 
191         return false;
192     }
193 
194     /***
195      * Indicates if this {@link FastMap} maps one or more keys to the
196      * specified value.
197      *
198      * @param  value the value whose presence in this map is to be tested.
199      * @return <code>true</code> if this map maps one or more keys to the
200      *         specified value.
201      * @throws NullPointerException if the key is <code>null</code>.
202      */
203     public boolean containsValue(Object value) {
204         EntryImpl entry = _mapFirst;
205 
206         while (entry != null) {
207             if (value.equals(entry._value)) {
208                 return true;
209             }
210 
211             entry = entry._after;
212         }
213 
214         return false;
215     }
216 
217     /***
218      * Returns the value to which this {@link FastMap} maps the specified key.
219      *
220      * @param  key the key whose associated value is to be returned.
221      * @return the value to which this map maps the specified key,
222      *         or <code>null</code> if there is no mapping for the key.
223      * @throws NullPointerException if key is <code>null</code>.
224      */
225     public Object get(Object key) {
226         EntryImpl entry = _entries[keyHash(key) & _mask];
227 
228         while (entry != null) {
229             if (key.equals(entry._key)) {
230                 return entry._value;
231             }
232 
233             entry = entry._next;
234         }
235 
236         return null;
237     }
238 
239     /***
240      * Returns the entry with the specified key.
241      *
242      * @param key the key whose associated entry is to be returned.
243      * @return the entry for the specified key or <code>null</code> if none.
244      */
245     public Entry getEntry(Object key) {
246         EntryImpl entry = _entries[keyHash(key) & _mask];
247 
248         while (entry != null) {
249             if (key.equals(entry._key)) {
250                 return entry;
251             }
252 
253             entry = entry._next;
254         }
255 
256         return null;
257     }
258 
259     /***
260      * Associates the specified value with the specified key in this
261      * {@link FastMap}. If the {@link FastMap} previously contained a mapping
262      * for this key, the old value is replaced.
263      *
264      * @param  key the key with which the specified value is to be associated.
265      * @param  value the value to be associated with the specified key.
266      * @return the previous value associated with specified key,
267      *         or <code>null</code> if there was no mapping for key.
268      *         A <code>null</code> return can also indicate that the map
269      *         previously associated <code>null</code> with the specified key.
270      * @throws NullPointerException if the key is <code>null</code>.
271      */
272     public Object put(Object key, Object value) {
273         EntryImpl entry = _entries[keyHash(key) & _mask];
274 
275         while (entry != null) {
276             if (key.equals(entry._key)) {
277                 Object prevValue = entry._value;
278                 entry._value = value;
279 
280                 return prevValue;
281             }
282 
283             entry = entry._next;
284         }
285 
286         // No previous mapping.
287         addEntry(key, value);
288 
289         return null;
290     }
291 
292     /***
293      * Returns a reusable {@link FastIterator} over this {@link FastMap} entries
294      * (unique instance per map). For example:<pre>
295      *     // Iteration without memory allocation!
296      *     for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
297      *         Entry entry = i.nextEntry();
298      *         ...
299      *     }</pre>
300      *
301      * @return an iterator which can be reset for reuse over and over.
302      * @see    FastMap.FastIterator
303      */
304     public FastIterator fastIterator() {
305         return _fastIterator;
306     }
307 
308     /***
309      * Copies all of the mappings from the specified map to this
310      * {@link FastMap}.
311      *
312      * @param  map the mappings to be stored in this map.
313      * @throws NullPointerException the specified map is <code>null</code>, or
314      *         the specified map contains <code>null</code> keys.
315      */
316     public void putAll(Map map) {
317         for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
318             Entry e = (Entry) i.next();
319             addEntry(e.getKey(), e.getValue());
320         }
321     }
322 
323     /***
324      * Removes the mapping for this key from this {@link FastMap} if present.
325      *
326      * @param  key the key whose mapping is to be removed from the map.
327      * @return previous value associated with specified key,
328      *         or <code>null</code> if there was no mapping for key.
329      *         A <code>null</code> return can also indicate that the map
330      *         previously associated <code>null</code> with the specified key.
331      * @throws NullPointerException if the key is <code>null</code>.
332      */
333     public Object remove(Object key) {
334         EntryImpl entry = _entries[keyHash(key) & _mask];
335 
336         while (entry != null) {
337             if (key.equals(entry._key)) {
338                 Object prevValue = entry._value;
339                 removeEntry(entry);
340 
341                 return prevValue;
342             }
343 
344             entry = entry._next;
345         }
346 
347         return null;
348     }
349 
350     /***
351      * Removes all mappings from this {@link FastMap}.
352      */
353     public void clear() {
354         // Clears all keys, values and buckets linked lists.
355         for (EntryImpl entry = _mapFirst; entry != null;
356                 entry = entry._after) {
357             entry._key = null;
358             entry._value = null;
359             entry._before = null;
360             entry._next = null;
361 
362             if (entry._previous == null) { // First in bucket.
363                 _entries[entry._index] = null;
364             } else {
365                 entry._previous = null;
366             }
367         }
368 
369         // Recycles all entries.
370         if (_mapLast != null) {
371             _mapLast._after = _poolFirst; // Connects to pool.
372             _poolFirst = _mapFirst;
373             _mapFirst = null;
374             _mapLast = null;
375             _size = 0;
376             sizeChanged();
377         }
378     }
379 
380     /***
381      * Changes the current capacity of this {@link FastMap}. If the capacity
382      * is increased, new entries are allocated and added to the pool.
383      * If the capacity is decreased, entries from the pool are deallocated
384      * (and are garbage collected eventually). The capacity also determined
385      * the number of buckets for the hash table.
386      *
387      * @param newCapacity the new capacity of this map.
388      */
389     public void setCapacity(int newCapacity) {
390         if (newCapacity > _capacity) { // Capacity increases.
391 
392             for (int i = _capacity; i < newCapacity; i++) {
393                 EntryImpl entry = new EntryImpl();
394                 entry._after = _poolFirst;
395                 _poolFirst = entry;
396             }
397         } else if (newCapacity < _capacity) { // Capacity decreases.
398 
399             for (int i = newCapacity; (i < _capacity) && (_poolFirst != null);
400                     i++) {
401                 // Disconnects the entry for gc to do its work.
402                 EntryImpl entry = _poolFirst;
403                 _poolFirst = entry._after;
404                 entry._after = null; // All pointers are now null!
405             }
406         }
407 
408         // Find a power of 2 >= capacity
409         int tableLength = 16;
410 
411         while (tableLength < newCapacity) {
412             tableLength <<= 1;
413         }
414 
415         // Checks if the hash table has to be re-sized.
416         if (_entries.length != tableLength) {
417             _entries = new EntryImpl[tableLength];
418             _mask = tableLength - 1;
419 
420             // Repopulates the hash table.
421             EntryImpl entry = _mapFirst;
422 
423             while (entry != null) {
424                 int index = keyHash(entry._key) & _mask;
425                 entry._index = index;
426 
427                 // Connects to bucket.
428                 entry._previous = null; // Resets previous.
429 
430                 EntryImpl next = _entries[index];
431                 entry._next = next;
432 
433                 if (next != null) {
434                     next._previous = entry;
435                 }
436 
437                 _entries[index] = entry;
438 
439                 entry = entry._after;
440             }
441         }
442 
443         _capacity = newCapacity;
444     }
445 
446     /***
447      * Returns a shallow copy of this {@link FastMap}. The keys and
448      * the values themselves are not cloned.
449      *
450      * @return a shallow copy of this map.
451      */
452     public Object clone() {
453         try {
454             FastMap clone = (FastMap) super.clone();
455             clone.initialize(_capacity);
456             clone.putAll(this);
457 
458             return clone;
459         } catch (CloneNotSupportedException e) {
460             // Should not happen, since we are Cloneable.
461             throw new InternalError();
462         }
463     }
464 
465     /***
466      * Compares the specified object with this {@link FastMap} for equality.
467      * Returns <code>true</code> if the given object is also a map and the two
468      * maps represent the same mappings (regardless of collection iteration
469      * order).
470      *
471      * @param obj the object to be compared for equality with this map.
472      * @return <code>true</code> if the specified object is equal to this map;
473      *         <code>false</code> otherwise.
474      */
475     public boolean equals(Object obj) {
476         if (obj == this) {
477             return true;
478         } else if (obj instanceof Map) {
479             Map that = (Map) obj;
480 
481             if (this.size() == that.size()) {
482                 EntryImpl entry = _mapFirst;
483 
484                 while (entry != null) {
485                     if (!that.entrySet().contains(entry)) {
486                         return false;
487                     }
488 
489                     entry = entry._after;
490                 }
491 
492                 return true;
493             } else {
494                 return false;
495             }
496         } else {
497             return false;
498         }
499     }
500 
501     /***
502      * Returns the hash code value for this {@link FastMap}.
503      *
504      * @return the hash code value for this map.
505      */
506     public int hashCode() {
507         int code = 0;
508         EntryImpl entry = _mapFirst;
509 
510         while (entry != null) {
511             code += entry.hashCode();
512             entry = entry._after;
513         }
514 
515         return code;
516     }
517 
518     /***
519      * Returns a <code>String</code> representation of this {@link FastMap}.
520      *
521      * @return <code>this.entrySet().toString();</code>
522      */
523     public String toString() {
524         return entrySet().toString();
525     }
526 
527     /***
528      * Returns a collection view of the values contained in this
529      * {@link FastMap}.  The collection is backed by the map, so changes to
530      * the map are reflected in the collection, and vice-versa.
531      * The collection supports element removal, which removes the corresponding
532      * mapping from this map, via the
533      * <code>Iterator.remove</code>, <code>Collection.remove</code>,
534      * <code>removeAll</code>, <code>retainAll</code>,
535      * and <code>clear</code> operations. It does not support the
536      * <code>add</code> or <code>addAll</code> operations.
537      *
538      * @return a collection view of the values contained in this map.
539      */
540     public Collection values() {
541         return _values;
542     }
543 
544     /***
545      * Returns a collection view of the mappings contained in this
546      * {@link FastMap}. Each element in the returned collection is a
547      * <code>Map.Entry</code>.  The collection is backed by the map,
548      * so changes to the map are reflected in the collection, and vice-versa.
549      * The collection supports element removal, which removes the corresponding
550      * mapping from this map, via the
551      * <code>Iterator.remove</code>, <code>Collection.remove</code>,
552      * <code>removeAll</code>, <code>retainAll</code>,
553      * and <code>clear</code> operations. It does not support the
554      * <code>add</code> or <code>addAll</code> operations.
555      *
556      * @return a collection view of the mappings contained in this map.
557      */
558     public Set entrySet() {
559         return _entrySet;
560     }
561 
562     /***
563      * Returns a set view of the keys contained in this {@link FastMap}.
564      * The set is backed by the map, so changes to the map are reflected
565      * in the set, and vice-versa.  The set supports element removal,
566      * which removes the corresponding mapping from this map, via the
567      * <code>Iterator.remove</code>, <code>Collection.remove</code>,
568      * <code>removeAll</code>, <code>retainAll</code>,
569      * and <code>clear</code> operations. It does not support the
570      * <code>add</code> or <code>addAll</code> operations.
571      *
572      * @return a set view of the keys contained in this map.
573      */
574     public Set keySet() {
575         return _keySet;
576     }
577 
578     /***
579      * This methods is being called when the size of this {@link FastMap}
580      * has changed. The default behavior is to double the map's capacity
581      * when the map's size exceeds the current map's capacity.
582      * Sub-class may override this method to implement custom resizing
583      * policies or to disable automatic resizing. For example:<pre>
584      *     Map fixedCapacityMap = new FastMap(256) {
585      *           protected sizeChanged() {
586      *               // Do nothing, automatic resizing disabled.
587      *           }
588      *     };</pre>
589      * @see #setCapacity
590      */
591     protected void sizeChanged() {
592         if (size() > capacity()) {
593             setCapacity(capacity() * 2);
594         }
595     }
596 
597     /***
598      * Returns the hash code for the specified key. The formula being used
599      * is identical to the formula used by <code>java.util.HashMap</code>
600      * (ensures similar behavior for ill-conditioned hashcode keys).
601      *
602      * @param key the key to calculate the hashcode for.
603      * @return the hash code for the specified key.
604      */
605     private static int keyHash(Object key) {
606         // From HashMap.hash(Object) function.
607         int hashCode = key.hashCode();
608         hashCode += ~(hashCode << 9);
609         hashCode ^= (hashCode >>> 14);
610         hashCode += (hashCode << 4);
611         hashCode ^= (hashCode >>> 10);
612 
613         return hashCode;
614     }
615 
616     /***
617      * Adds a new entry for the specified key and value.
618      * @param key the entry's key.
619      * @param value the entry's value.
620      */
621     private void addEntry(Object key, Object value) {
622         EntryImpl entry = _poolFirst;
623 
624         if (entry != null) {
625             _poolFirst = entry._after;
626             entry._after = null;
627         } else { // Pool empty.
628             entry = new EntryImpl();
629         }
630 
631         // Setup entry parameters.
632         entry._key = key;
633         entry._value = value;
634 
635         int index = keyHash(key) & _mask;
636         entry._index = index;
637 
638         // Connects to bucket.
639         EntryImpl next = _entries[index];
640         entry._next = next;
641 
642         if (next != null) {
643             next._previous = entry;
644         }
645 
646         _entries[index] = entry;
647 
648         // Connects to collection.
649         if (_mapLast != null) {
650             entry._before = _mapLast;
651             _mapLast._after = entry;
652         } else {
653             _mapFirst = entry;
654         }
655 
656         _mapLast = entry;
657 
658         // Updates size.
659         _size++;
660         sizeChanged();
661     }
662 
663     /***
664      * Removes the specified entry from the map.
665      *
666      * @param entry the entry to be removed.
667      */
668     private void removeEntry(EntryImpl entry) {
669         // Removes from bucket.
670         EntryImpl previous = entry._previous;
671         EntryImpl next = entry._next;
672 
673         if (previous != null) {
674             previous._next = next;
675             entry._previous = null;
676         } else { // First in bucket.
677             _entries[entry._index] = next;
678         }
679 
680         if (next != null) {
681             next._previous = previous;
682             entry._next = null;
683         }
684          // Else do nothing, no last pointer.
685 
686         // Removes from collection.
687         EntryImpl before = entry._before;
688         EntryImpl after = entry._after;
689 
690         if (before != null) {
691             before._after = after;
692             entry._before = null;
693         } else { // First in collection.
694             _mapFirst = after;
695         }
696 
697         if (after != null) {
698             after._before = before;
699         } else { // Last in collection.
700             _mapLast = before;
701         }
702 
703         // Clears value and key.
704         entry._key = null;
705         entry._value = null;
706 
707         // Recycles.
708         entry._after = _poolFirst;
709         _poolFirst = entry;
710 
711         // Updates size.
712         _size--;
713         sizeChanged();
714     }
715 
716     /***
717      * Initializes this instance for the specified capacity.
718      * Once initialized, operations on this map should not create new objects
719      * (unless the map's size exceeds the specified capacity).
720      *
721      * @param capacity the initial capacity.
722      */
723     private void initialize(int capacity) {
724         // Find a power of 2 >= capacity
725         int tableLength = 16;
726 
727         while (tableLength < capacity) {
728             tableLength <<= 1;
729         }
730 
731         // Allocates hash table.
732         _entries = new EntryImpl[tableLength];
733         _mask = tableLength - 1;
734         _capacity = capacity;
735         _size = 0;
736 
737         // Allocates views.
738         _values = new Values();
739         _entrySet = new EntrySet();
740         _keySet = new KeySet();
741 
742         // Resets pointers.
743         _poolFirst = null;
744         _mapFirst = null;
745         _mapLast = null;
746 
747         // Allocates entries.
748         for (int i = 0; i < capacity; i++) {
749             EntryImpl entry = new EntryImpl();
750             entry._after = _poolFirst;
751             _poolFirst = entry;
752         }
753     }
754 
755     /***
756      * Requires special handling during de-serialization process.
757      *
758      * @param  stream the object input stream.
759      * @throws IOException if an I/O error occurs.
760      * @throws ClassNotFoundException if the class for the object de-serialized
761      *         is not found.
762      */
763     private void readObject(ObjectInputStream stream)
764         throws IOException, ClassNotFoundException {
765         int capacity = stream.readInt();
766         initialize(capacity);
767 
768         int size = stream.readInt();
769 
770         for (int i = 0; i < size; i++) {
771             Object key = stream.readObject();
772             Object value = stream.readObject();
773             addEntry(key, value);
774         }
775     }
776 
777     /***
778      * Requires special handling during serialization process.
779      *
780      * @param  stream the object output stream.
781      * @throws IOException if an I/O error occurs.
782      */
783     private void writeObject(ObjectOutputStream stream)
784         throws IOException {
785         stream.writeInt(_capacity);
786         stream.writeInt(_size);
787 
788         int count = 0;
789         EntryImpl entry = _mapFirst;
790 
791         while (entry != null) {
792             stream.writeObject(entry._key);
793             stream.writeObject(entry._value);
794             count++;
795             entry = entry._after;
796         }
797 
798         if (count != _size) {
799             throw new IOException("FastMap Corrupted");
800         }
801     }
802 
803     private class Values extends AbstractCollection {
804         public Iterator iterator() {
805             return new Iterator() {
806                     EntryImpl after = _mapFirst;
807                     EntryImpl before;
808 
809                     public void remove() {
810                         if (before != null) {
811                             removeEntry(before);
812                         } else {
813                             throw new IllegalStateException();
814                         }
815                     }
816 
817                     public boolean hasNext() {
818                         return after != null;
819                     }
820 
821                     public Object next() {
822                         if (after != null) {
823                             before = after;
824                             after = after._after;
825 
826                             return before._value;
827                         } else {
828                             throw new NoSuchElementException();
829                         }
830                     }
831                 };
832         }
833 
834         public int size() {
835             return _size;
836         }
837 
838         public boolean contains(Object o) {
839             return containsValue(o);
840         }
841 
842         public void clear() {
843             FastMap.this.clear();
844         }
845     }
846 
847     private class EntrySet extends AbstractSet {
848         public Iterator iterator() {
849             return new Iterator() {
850                     EntryImpl after = _mapFirst;
851                     EntryImpl before;
852 
853                     public void remove() {
854                         if (before != null) {
855                             removeEntry(before);
856                         } else {
857                             throw new IllegalStateException();
858                         }
859                     }
860 
861                     public boolean hasNext() {
862                         return after != null;
863                     }
864 
865                     public Object next() {
866                         if (after != null) {
867                             before = after;
868                             after = after._after;
869 
870                             return before;
871                         } else {
872                             throw new NoSuchElementException();
873                         }
874                     }
875                 };
876         }
877 
878         public int size() {
879             return _size;
880         }
881 
882         public boolean contains(Object obj) { // Optimization.
883 
884             if (obj instanceof Entry) {
885                 Entry entry = (Entry) obj;
886                 Entry mapEntry = getEntry(entry.getKey());
887 
888                 return entry.equals(mapEntry);
889             } else {
890                 return false;
891             }
892         }
893 
894         public boolean remove(Object obj) { // Optimization.
895 
896             if (obj instanceof Entry) {
897                 Entry entry = (Entry) obj;
898                 EntryImpl mapEntry = (EntryImpl) getEntry(entry.getKey());
899 
900                 if ((mapEntry != null) &&
901                         (entry.getValue()).equals(mapEntry._value)) {
902                     removeEntry(mapEntry);
903 
904                     return true;
905                 }
906             }
907 
908             return false;
909         }
910     }
911 
912     private class KeySet extends AbstractSet {
913         public Iterator iterator() {
914             return new Iterator() {
915                     EntryImpl after = _mapFirst;
916                     EntryImpl before;
917 
918                     public void remove() {
919                         if (before != null) {
920                             removeEntry(before);
921                         } else {
922                             throw new IllegalStateException();
923                         }
924                     }
925 
926                     public boolean hasNext() {
927                         return after != null;
928                     }
929 
930                     public Object next() {
931                         if (after != null) {
932                             before = after;
933                             after = after._after;
934 
935                             return before._key;
936                         } else {
937                             throw new NoSuchElementException();
938                         }
939                     }
940                 };
941         }
942 
943         public int size() {
944             return _size;
945         }
946 
947         public boolean contains(Object obj) { // Optimization.
948 
949             return FastMap.this.containsKey(obj);
950         }
951 
952         public boolean remove(Object obj) { // Optimization.
953 
954             return FastMap.this.remove(obj) != null;
955         }
956 
957         public void clear() { // Optimization.
958             FastMap.this.clear();
959         }
960     }
961 
962     /***
963      * This inner class represents a reusable list iterator over
964      * {@link FastMap} entries. This iterator is bi-directional and can be
965      * directly moved to the {@link #toFirst first} or {@link #toLast last}
966      * entry. For example:<pre>
967      *     for (FastIterator i=map.fastIterator().toFirst(); i.hasNext();) {
968      *         Entry entry = i.nextEntry();
969      *         ...
970      *     }</pre>
971      * {@link #set setting} or {@link #add adding} new entries is not
972      * supported.
973      */
974     public final class FastIterator implements ListIterator {
975         EntryImpl after = _mapFirst;
976         EntryImpl before;
977         int nextIndex = 0;
978 
979         public FastIterator toFirst() {
980             after = _mapFirst;
981             before = null;
982             nextIndex = 0;
983 
984             return this;
985         }
986 
987         public FastIterator toLast() {
988             after = null;
989             before = _mapLast;
990             nextIndex = _size;
991 
992             return this;
993         }
994 
995         public boolean hasNext() {
996             return after != null;
997         }
998 
999         public Entry nextEntry() {
1000             if (after != null) {
1001                 nextIndex++;
1002                 before = after;
1003                 after = after._after;
1004 
1005                 return before;
1006             } else {
1007                 throw new NoSuchElementException();
1008             }
1009         }
1010 
1011         public Object next() {
1012             return nextEntry();
1013         }
1014 
1015         public boolean hasPrevious() {
1016             return before != null;
1017         }
1018 
1019         public Entry previousEntry() {
1020             if (before != null) {
1021                 nextIndex--;
1022                 after = before;
1023                 before = before._after;
1024 
1025                 return after;
1026             } else {
1027                 throw new NoSuchElementException();
1028             }
1029         }
1030 
1031         public Object previous() {
1032             return previousEntry();
1033         }
1034 
1035         public int nextIndex() {
1036             return nextIndex;
1037         }
1038 
1039         public int previousIndex() {
1040             return nextIndex - 1;
1041         }
1042 
1043         public void remove() {
1044             if (before != null) {
1045                 removeEntry(before);
1046             } else {
1047                 throw new IllegalStateException();
1048             }
1049         }
1050 
1051         public void set(Object o) {
1052             throw new UnsupportedOperationException();
1053         }
1054 
1055         public void add(Object o) {
1056             throw new UnsupportedOperationException();
1057         }
1058     }
1059 
1060     /***
1061      * This class represents a {@link FastMap} entry.
1062      */
1063     private static final class EntryImpl implements Entry {
1064         /***
1065          * Holds the entry key (null when in pool).
1066          */
1067         private Object _key;
1068 
1069         /***
1070          * Holds the entry value (null when in pool).
1071          */
1072         private Object _value;
1073 
1074         /***
1075          * Holds the bucket index (undefined when in pool).
1076          */
1077         private int _index;
1078 
1079         /***
1080          * Holds the previous entry in the same bucket (null when in pool).
1081          */
1082         private EntryImpl _previous;
1083 
1084         /***
1085          * Holds the next entry in the same bucket (null when in pool).
1086          */
1087         private EntryImpl _next;
1088 
1089         /***
1090          * Holds the entry added before this entry (null when in pool).
1091          */
1092         private EntryImpl _before;
1093 
1094         /***
1095          * Holds the entry added after this entry
1096          * or the next available entry when in pool.
1097          */
1098         private EntryImpl _after;
1099 
1100         /***
1101          * Returns the key for this entry.
1102          *
1103          * @return the entry's key.
1104          */
1105         public Object getKey() {
1106             return _key;
1107         }
1108 
1109         /***
1110          * Returns the value for this entry.
1111          *
1112          * @return the entry's value.
1113          */
1114         public Object getValue() {
1115             return _value;
1116         }
1117 
1118         /***
1119          * Sets the value for this entry.
1120          *
1121          * @param value the new value.
1122          * @return the previous value.
1123          */
1124         public Object setValue(Object value) {
1125             Object old = _value;
1126             _value = value;
1127 
1128             return old;
1129         }
1130 
1131         /***
1132          * Indicates if this entry is considered equals to the specified
1133          * entry.
1134          *
1135          * @param that the object to test for equality.
1136          * @return <code>true<code> if both entry are considered equal;
1137          *         <code>false<code> otherwise.
1138          */
1139         public boolean equals(Object that) {
1140             if (that instanceof Entry) {
1141                 Entry entry = (Entry) that;
1142 
1143                 return (_key.equals(entry.getKey())) &&
1144                 ((_value != null) ? _value.equals(entry.getValue())
1145                                   : (entry.getValue() == null));
1146             } else {
1147                 return false;
1148             }
1149         }
1150 
1151         /***
1152          * Returns the hash code for this entry.
1153          *
1154          * @return this entry's hash code.
1155          */
1156         public int hashCode() {
1157             return _key.hashCode() ^
1158             ((_value != null) ? _value.hashCode() : 0);
1159         }
1160 
1161         /***
1162          * Returns the text representation of this entry.
1163          *
1164          * @return this entry's textual representation.
1165          */
1166         public String toString() {
1167             return _key + "=" + _value;
1168         }
1169     }
1170 }