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 }