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