KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > map > Flat3Map


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.collections.map;
17
18 import java.io.IOException JavaDoc;
19 import java.io.ObjectInputStream JavaDoc;
20 import java.io.ObjectOutputStream JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.AbstractCollection JavaDoc;
23 import java.util.AbstractSet JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.NoSuchElementException JavaDoc;
28 import java.util.Set JavaDoc;
29
30 import org.apache.commons.collections.IterableMap;
31 import org.apache.commons.collections.MapIterator;
32 import org.apache.commons.collections.ResettableIterator;
33 import org.apache.commons.collections.iterators.EmptyIterator;
34 import org.apache.commons.collections.iterators.EmptyMapIterator;
35
36 /**
37  * A <code>Map</code> implementation that stores data in simple fields until
38  * the size is greater than 3.
39  * <p>
40  * This map is designed for performance and can outstrip HashMap.
41  * It also has good garbage collection characteristics.
42  * <ul>
43  * <li>Optimised for operation at size 3 or less.
44  * <li>Still works well once size 3 exceeded.
45  * <li>Gets at size 3 or less are about 0-10% faster than HashMap,
46  * <li>Puts at size 3 or less are over 4 times faster than HashMap.
47  * <li>Performance 5% slower than HashMap once size 3 exceeded once.
48  * </ul>
49  * The design uses two distinct modes of operation - flat and delegate.
50  * While the map is size 3 or less, operations map straight onto fields using
51  * switch statements. Once size 4 is reached, the map switches to delegate mode
52  * and only switches back when cleared. In delegate mode, all operations are
53  * forwarded straight to a HashMap resulting in the 5% performance loss.
54  * <p>
55  * The performance gains on puts are due to not needing to create a Map Entry
56  * object. This is a large saving not only in performance but in garbage collection.
57  * <p>
58  * Whilst in flat mode this map is also easy for the garbage collector to dispatch.
59  * This is because it contains no complex objects or arrays which slow the progress.
60  * <p>
61  * Do not use <code>Flat3Map</code> if the size is likely to grow beyond 3.
62  *
63  * @since Commons Collections 3.0
64  * @version $Revision: 1.18 $ $Date: 2004/05/26 21:56:05 $
65  *
66  * @author Stephen Colebourne
67  */

68 public class Flat3Map implements IterableMap, Serializable JavaDoc, Cloneable JavaDoc {
69
70     /** Serialization version */
71     private static final long serialVersionUID = -6701087419741928296L;
72
73     /** The size of the map, used while in flat mode */
74     private transient int size;
75     /** Hash, used while in flat mode */
76     private transient int hash1;
77     /** Hash, used while in flat mode */
78     private transient int hash2;
79     /** Hash, used while in flat mode */
80     private transient int hash3;
81     /** Key, used while in flat mode */
82     private transient Object JavaDoc key1;
83     /** Key, used while in flat mode */
84     private transient Object JavaDoc key2;
85     /** Key, used while in flat mode */
86     private transient Object JavaDoc key3;
87     /** Value, used while in flat mode */
88     private transient Object JavaDoc value1;
89     /** Value, used while in flat mode */
90     private transient Object JavaDoc value2;
91     /** Value, used while in flat mode */
92     private transient Object JavaDoc value3;
93     /** Map, used while in delegate mode */
94     private transient AbstractHashedMap delegateMap;
95
96     /**
97      * Constructor.
98      */

99     public Flat3Map() {
100         super();
101     }
102
103     /**
104      * Constructor copying elements from another map.
105      *
106      * @param map the map to copy
107      * @throws NullPointerException if the map is null
108      */

109     public Flat3Map(Map JavaDoc map) {
110         super();
111         putAll(map);
112     }
113
114     //-----------------------------------------------------------------------
115
/**
116      * Gets the value mapped to the key specified.
117      *
118      * @param key the key
119      * @return the mapped value, null if no match
120      */

121     public Object JavaDoc get(Object JavaDoc key) {
122         if (delegateMap != null) {
123             return delegateMap.get(key);
124         }
125         if (key == null) {
126             switch (size) {
127                 // drop through
128
case 3:
129                     if (key3 == null) return value3;
130                 case 2:
131                     if (key2 == null) return value2;
132                 case 1:
133                     if (key1 == null) return value1;
134             }
135         } else {
136             if (size > 0) {
137                 int hashCode = key.hashCode();
138                 switch (size) {
139                     // drop through
140
case 3:
141                         if (hash3 == hashCode && key.equals(key3)) return value3;
142                     case 2:
143                         if (hash2 == hashCode && key.equals(key2)) return value2;
144                     case 1:
145                         if (hash1 == hashCode && key.equals(key1)) return value1;
146                 }
147             }
148         }
149         return null;
150     }
151
152     /**
153      * Gets the size of the map.
154      *
155      * @return the size
156      */

157     public int size() {
158         if (delegateMap != null) {
159             return delegateMap.size();
160         }
161         return size;
162     }
163
164     /**
165      * Checks whether the map is currently empty.
166      *
167      * @return true if the map is currently size zero
168      */

169     public boolean isEmpty() {
170         return (size() == 0);
171     }
172
173     //-----------------------------------------------------------------------
174
/**
175      * Checks whether the map contains the specified key.
176      *
177      * @param key the key to search for
178      * @return true if the map contains the key
179      */

180     public boolean containsKey(Object JavaDoc key) {
181         if (delegateMap != null) {
182             return delegateMap.containsKey(key);
183         }
184         if (key == null) {
185             switch (size) { // drop through
186
case 3:
187                     if (key3 == null) return true;
188                 case 2:
189                     if (key2 == null) return true;
190                 case 1:
191                     if (key1 == null) return true;
192             }
193         } else {
194             if (size > 0) {
195                 int hashCode = key.hashCode();
196                 switch (size) { // drop through
197
case 3:
198                         if (hash3 == hashCode && key.equals(key3)) return true;
199                     case 2:
200                         if (hash2 == hashCode && key.equals(key2)) return true;
201                     case 1:
202                         if (hash1 == hashCode && key.equals(key1)) return true;
203                 }
204             }
205         }
206         return false;
207     }
208
209     /**
210      * Checks whether the map contains the specified value.
211      *
212      * @param value the value to search for
213      * @return true if the map contains the key
214      */

215     public boolean containsValue(Object JavaDoc value) {
216         if (delegateMap != null) {
217             return delegateMap.containsValue(value);
218         }
219         if (value == null) { // drop through
220
switch (size) {
221                 case 3:
222                     if (value3 == null) return true;
223                 case 2:
224                     if (value2 == null) return true;
225                 case 1:
226                     if (value1 == null) return true;
227             }
228         } else {
229             switch (size) { // drop through
230
case 3:
231                     if (value.equals(value3)) return true;
232                 case 2:
233                     if (value.equals(value2)) return true;
234                 case 1:
235                     if (value.equals(value1)) return true;
236             }
237         }
238         return false;
239     }
240
241     //-----------------------------------------------------------------------
242
/**
243      * Puts a key-value mapping into this map.
244      *
245      * @param key the key to add
246      * @param value the value to add
247      * @return the value previously mapped to this key, null if none
248      */

249     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
250         if (delegateMap != null) {
251             return delegateMap.put(key, value);
252         }
253         // change existing mapping
254
if (key == null) {
255             switch (size) { // drop through
256
case 3:
257                     if (key3 == null) {
258                         Object JavaDoc old = value3;
259                         value3 = value;
260                         return old;
261                     }
262                 case 2:
263                     if (key2 == null) {
264                         Object JavaDoc old = value2;
265                         value2 = value;
266                         return old;
267                     }
268                 case 1:
269                     if (key1 == null) {
270                         Object JavaDoc old = value1;
271                         value1 = value;
272                         return old;
273                     }
274             }
275         } else {
276             if (size > 0) {
277                 int hashCode = key.hashCode();
278                 switch (size) { // drop through
279
case 3:
280                         if (hash3 == hashCode && key.equals(key3)) {
281                             Object JavaDoc old = value3;
282                             value3 = value;
283                             return old;
284                         }
285                     case 2:
286                         if (hash2 == hashCode && key.equals(key2)) {
287                             Object JavaDoc old = value2;
288                             value2 = value;
289                             return old;
290                         }
291                     case 1:
292                         if (hash1 == hashCode && key.equals(key1)) {
293                             Object JavaDoc old = value1;
294                             value1 = value;
295                             return old;
296                         }
297                 }
298             }
299         }
300         
301         // add new mapping
302
switch (size) {
303             default:
304                 convertToMap();
305                 delegateMap.put(key, value);
306                 return null;
307             case 2:
308                 hash3 = (key == null ? 0 : key.hashCode());
309                 key3 = key;
310                 value3 = value;
311                 break;
312             case 1:
313                 hash2 = (key == null ? 0 : key.hashCode());
314                 key2 = key;
315                 value2 = value;
316                 break;
317             case 0:
318                 hash1 = (key == null ? 0 : key.hashCode());
319                 key1 = key;
320                 value1 = value;
321                 break;
322         }
323         size++;
324         return null;
325     }
326
327     /**
328      * Puts all the values from the specified map into this map.
329      *
330      * @param map the map to add
331      * @throws NullPointerException if the map is null
332      */

333     public void putAll(Map JavaDoc map) {
334         int size = map.size();
335         if (size == 0) {
336             return;
337         }
338         if (delegateMap != null) {
339             delegateMap.putAll(map);
340             return;
341         }
342         if (size < 4) {
343             for (Iterator JavaDoc it = map.entrySet().iterator(); it.hasNext();) {
344                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
345                 put(entry.getKey(), entry.getValue());
346             }
347         } else {
348             convertToMap();
349             delegateMap.putAll(map);
350         }
351     }
352
353     /**
354      * Converts the flat map data to a map.
355      */

356     private void convertToMap() {
357         delegateMap = createDelegateMap();
358         switch (size) { // drop through
359
case 3:
360                 delegateMap.put(key3, value3);
361             case 2:
362                 delegateMap.put(key2, value2);
363             case 1:
364                 delegateMap.put(key1, value1);
365         }
366         
367         size = 0;
368         hash1 = hash2 = hash3 = 0;
369         key1 = key2 = key3 = null;
370         value1 = value2 = value3 = null;
371     }
372
373     /**
374      * Create an instance of the map used for storage when in delegation mode.
375      * <p>
376      * This can be overridden by subclasses to provide a different map implementation.
377      * Not every AbstractHashedMap is suitable, identity and reference based maps
378      * would be poor choices.
379      *
380      * @return a new AbstractHashedMap or subclass
381      * @since Commons Collections 3.1
382      */

383     protected AbstractHashedMap createDelegateMap() {
384         return new HashedMap();
385     }
386
387     /**
388      * Removes the specified mapping from this map.
389      *
390      * @param key the mapping to remove
391      * @return the value mapped to the removed key, null if key not in map
392      */

393     public Object JavaDoc remove(Object JavaDoc key) {
394         if (delegateMap != null) {
395             return delegateMap.remove(key);
396         }
397         if (size == 0) {
398             return null;
399         }
400         if (key == null) {
401             switch (size) { // drop through
402
case 3:
403                     if (key3 == null) {
404                         Object JavaDoc old = value3;
405                         hash3 = 0;
406                         key3 = null;
407                         value3 = null;
408                         size = 2;
409                         return old;
410                     }
411                     if (key2 == null) {
412                         Object JavaDoc old = value3;
413                         hash2 = hash3;
414                         key2 = key3;
415                         value2 = value3;
416                         hash3 = 0;
417                         key3 = null;
418                         value3 = null;
419                         size = 2;
420                         return old;
421                     }
422                     if (key1 == null) {
423                         Object JavaDoc old = value3;
424                         hash1 = hash3;
425                         key1 = key3;
426                         value1 = value3;
427                         hash3 = 0;
428                         key3 = null;
429                         value3 = null;
430                         size = 2;
431                         return old;
432                     }
433                     return null;
434                 case 2:
435                     if (key2 == null) {
436                         Object JavaDoc old = value2;
437                         hash2 = 0;
438                         key2 = null;
439                         value2 = null;
440                         size = 1;
441                         return old;
442                     }
443                     if (key1 == null) {
444                         Object JavaDoc old = value2;
445                         hash1 = hash2;
446                         key1 = key2;
447                         value1 = value2;
448                         hash2 = 0;
449                         key2 = null;
450                         value2 = null;
451                         size = 1;
452                         return old;
453                     }
454                     return null;
455                 case 1:
456                     if (key1 == null) {
457                         Object JavaDoc old = value1;
458                         hash1 = 0;
459                         key1 = null;
460                         value1 = null;
461                         size = 0;
462                         return old;
463                     }
464             }
465         } else {
466             if (size > 0) {
467                 int hashCode = key.hashCode();
468                 switch (size) { // drop through
469
case 3:
470                         if (hash3 == hashCode && key.equals(key3)) {
471                             Object JavaDoc old = value3;
472                             hash3 = 0;
473                             key3 = null;
474                             value3 = null;
475                             size = 2;
476                             return old;
477                         }
478                         if (hash2 == hashCode && key.equals(key2)) {
479                             Object JavaDoc old = value3;
480                             hash2 = hash3;
481                             key2 = key3;
482                             value2 = value3;
483                             hash3 = 0;
484                             key3 = null;
485                             value3 = null;
486                             size = 2;
487                             return old;
488                         }
489                         if (hash1 == hashCode && key.equals(key1)) {
490                             Object JavaDoc old = value3;
491                             hash1 = hash3;
492                             key1 = key3;
493                             value1 = value3;
494                             hash3 = 0;
495                             key3 = null;
496                             value3 = null;
497                             size = 2;
498                             return old;
499                         }
500                         return null;
501                     case 2:
502                         if (hash2 == hashCode && key.equals(key2)) {
503                             Object JavaDoc old = value2;
504                             hash2 = 0;
505                             key2 = null;
506                             value2 = null;
507                             size = 1;
508                             return old;
509                         }
510                         if (hash1 == hashCode && key.equals(key1)) {
511                             Object JavaDoc old = value2;
512                             hash1 = hash2;
513                             key1 = key2;
514                             value1 = value2;
515                             hash2 = 0;
516                             key2 = null;
517                             value2 = null;
518                             size = 1;
519                             return old;
520                         }
521                         return null;
522                     case 1:
523                         if (hash1 == hashCode && key.equals(key1)) {
524                             Object JavaDoc old = value1;
525                             hash1 = 0;
526                             key1 = null;
527                             value1 = null;
528                             size = 0;
529                             return old;
530                         }
531                 }
532             }
533         }
534         return null;
535     }
536
537     /**
538      * Clears the map, resetting the size to zero and nullifying references
539      * to avoid garbage collection issues.
540      */

541     public void clear() {
542         if (delegateMap != null) {
543             delegateMap.clear(); // should aid gc
544
delegateMap = null; // switch back to flat mode
545
} else {
546             size = 0;
547             hash1 = hash2 = hash3 = 0;
548             key1 = key2 = key3 = null;
549             value1 = value2 = value3 = null;
550         }
551     }
552
553     //-----------------------------------------------------------------------
554
/**
555      * Gets an iterator over the map.
556      * Changes made to the iterator affect this map.
557      * <p>
558      * A MapIterator returns the keys in the map. It also provides convenient
559      * methods to get the key and value, and set the value.
560      * It avoids the need to create an entrySet/keySet/values object.
561      * It also avoids creating the Map Entry object.
562      *
563      * @return the map iterator
564      */

565     public MapIterator mapIterator() {
566         if (delegateMap != null) {
567             return delegateMap.mapIterator();
568         }
569         if (size == 0) {
570             return EmptyMapIterator.INSTANCE;
571         }
572         return new FlatMapIterator(this);
573     }
574
575     /**
576      * FlatMapIterator
577      */

578     static class FlatMapIterator implements MapIterator, ResettableIterator {
579         private final Flat3Map parent;
580         private int nextIndex = 0;
581         private boolean canRemove = false;
582         
583         FlatMapIterator(Flat3Map parent) {
584             super();
585             this.parent = parent;
586         }
587
588         public boolean hasNext() {
589             return (nextIndex < parent.size);
590         }
591
592         public Object JavaDoc next() {
593             if (hasNext() == false) {
594                 throw new NoSuchElementException JavaDoc(AbstractHashedMap.NO_NEXT_ENTRY);
595             }
596             canRemove = true;
597             nextIndex++;
598             return getKey();
599         }
600
601         public void remove() {
602             if (canRemove == false) {
603                 throw new IllegalStateException JavaDoc(AbstractHashedMap.REMOVE_INVALID);
604             }
605             parent.remove(getKey());
606             nextIndex--;
607             canRemove = false;
608         }
609
610         public Object JavaDoc getKey() {
611             if (canRemove == false) {
612                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETKEY_INVALID);
613             }
614             switch (nextIndex) {
615                 case 3:
616                     return parent.key3;
617                 case 2:
618                     return parent.key2;
619                 case 1:
620                     return parent.key1;
621             }
622             throw new IllegalStateException JavaDoc("Invalid map index");
623         }
624
625         public Object JavaDoc getValue() {
626             if (canRemove == false) {
627                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETVALUE_INVALID);
628             }
629             switch (nextIndex) {
630                 case 3:
631                     return parent.value3;
632                 case 2:
633                     return parent.value2;
634                 case 1:
635                     return parent.value1;
636             }
637             throw new IllegalStateException JavaDoc("Invalid map index");
638         }
639
640         public Object JavaDoc setValue(Object JavaDoc value) {
641             if (canRemove == false) {
642                 throw new IllegalStateException JavaDoc(AbstractHashedMap.SETVALUE_INVALID);
643             }
644             Object JavaDoc old = getValue();
645             switch (nextIndex) {
646                 case 3:
647                     parent.value3 = value;
648                 case 2:
649                     parent.value2 = value;
650                 case 1:
651                     parent.value1 = value;
652             }
653             return old;
654         }
655         
656         public void reset() {
657             nextIndex = 0;
658             canRemove = false;
659         }
660         
661         public String JavaDoc toString() {
662             if (canRemove) {
663                 return "Iterator[" + getKey() + "=" + getValue() + "]";
664             } else {
665                 return "Iterator[]";
666             }
667         }
668     }
669     
670     /**
671      * Gets the entrySet view of the map.
672      * Changes made to the view affect this map.
673      * The Map Entry is not an independent object and changes as the
674      * iterator progresses.
675      * To simply iterate through the entries, use {@link #mapIterator()}.
676      *
677      * @return the entrySet view
678      */

679     public Set JavaDoc entrySet() {
680         if (delegateMap != null) {
681             return delegateMap.entrySet();
682         }
683         return new EntrySet(this);
684     }
685     
686     /**
687      * EntrySet
688      */

689     static class EntrySet extends AbstractSet JavaDoc {
690         private final Flat3Map parent;
691         
692         EntrySet(Flat3Map parent) {
693             super();
694             this.parent = parent;
695         }
696
697         public int size() {
698             return parent.size();
699         }
700         
701         public void clear() {
702             parent.clear();
703         }
704         
705         public boolean remove(Object JavaDoc obj) {
706             if (obj instanceof Map.Entry JavaDoc == false) {
707                 return false;
708             }
709             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) obj;
710             Object JavaDoc key = entry.getKey();
711             boolean result = parent.containsKey(key);
712             parent.remove(key);
713             return result;
714         }
715
716         public Iterator JavaDoc iterator() {
717             if (parent.delegateMap != null) {
718                 return parent.delegateMap.entrySet().iterator();
719             }
720             if (parent.size() == 0) {
721                 return EmptyIterator.INSTANCE;
722             }
723             return new EntrySetIterator(parent);
724         }
725     }
726
727     /**
728      * EntrySetIterator and MapEntry
729      */

730     static class EntrySetIterator implements Iterator JavaDoc, Map.Entry JavaDoc {
731         private final Flat3Map parent;
732         private int nextIndex = 0;
733         private boolean canRemove = false;
734         
735         EntrySetIterator(Flat3Map parent) {
736             super();
737             this.parent = parent;
738         }
739
740         public boolean hasNext() {
741             return (nextIndex < parent.size);
742         }
743
744         public Object JavaDoc next() {
745             if (hasNext() == false) {
746                 throw new NoSuchElementException JavaDoc(AbstractHashedMap.NO_NEXT_ENTRY);
747             }
748             canRemove = true;
749             nextIndex++;
750             return this;
751         }
752
753         public void remove() {
754             if (canRemove == false) {
755                 throw new IllegalStateException JavaDoc(AbstractHashedMap.REMOVE_INVALID);
756             }
757             parent.remove(getKey());
758             nextIndex--;
759             canRemove = false;
760         }
761
762         public Object JavaDoc getKey() {
763             if (canRemove == false) {
764                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETKEY_INVALID);
765             }
766             switch (nextIndex) {
767                 case 3:
768                     return parent.key3;
769                 case 2:
770                     return parent.key2;
771                 case 1:
772                     return parent.key1;
773             }
774             throw new IllegalStateException JavaDoc("Invalid map index");
775         }
776
777         public Object JavaDoc getValue() {
778             if (canRemove == false) {
779                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETVALUE_INVALID);
780             }
781             switch (nextIndex) {
782                 case 3:
783                     return parent.value3;
784                 case 2:
785                     return parent.value2;
786                 case 1:
787                     return parent.value1;
788             }
789             throw new IllegalStateException JavaDoc("Invalid map index");
790         }
791
792         public Object JavaDoc setValue(Object JavaDoc value) {
793             if (canRemove == false) {
794                 throw new IllegalStateException JavaDoc(AbstractHashedMap.SETVALUE_INVALID);
795             }
796             Object JavaDoc old = getValue();
797             switch (nextIndex) {
798                 case 3:
799                     parent.value3 = value;
800                 case 2:
801                     parent.value2 = value;
802                 case 1:
803                     parent.value1 = value;
804             }
805             return old;
806         }
807         
808         public boolean equals(Object JavaDoc obj) {
809             if (canRemove == false) {
810                 return false;
811             }
812             if (obj instanceof Map.Entry JavaDoc == false) {
813                 return false;
814             }
815             Map.Entry JavaDoc other = (Map.Entry JavaDoc) obj;
816             Object JavaDoc key = getKey();
817             Object JavaDoc value = getValue();
818             return (key == null ? other.getKey() == null : key.equals(other.getKey())) &&
819                    (value == null ? other.getValue() == null : value.equals(other.getValue()));
820         }
821         
822         public int hashCode() {
823             if (canRemove == false) {
824                 return 0;
825             }
826             Object JavaDoc key = getKey();
827             Object JavaDoc value = getValue();
828             return (key == null ? 0 : key.hashCode()) ^
829                    (value == null ? 0 : value.hashCode());
830         }
831         
832         public String JavaDoc toString() {
833             if (canRemove) {
834                 return getKey() + "=" + getValue();
835             } else {
836                 return "";
837             }
838         }
839     }
840     
841     /**
842      * Gets the keySet view of the map.
843      * Changes made to the view affect this map.
844      * To simply iterate through the keys, use {@link #mapIterator()}.
845      *
846      * @return the keySet view
847      */

848     public Set JavaDoc keySet() {
849         if (delegateMap != null) {
850             return delegateMap.keySet();
851         }
852         return new KeySet(this);
853     }
854
855     /**
856      * KeySet
857      */

858     static class KeySet extends AbstractSet JavaDoc {
859         private final Flat3Map parent;
860         
861         KeySet(Flat3Map parent) {
862             super();
863             this.parent = parent;
864         }
865
866         public int size() {
867             return parent.size();
868         }
869         
870         public void clear() {
871             parent.clear();
872         }
873         
874         public boolean contains(Object JavaDoc key) {
875             return parent.containsKey(key);
876         }
877
878         public boolean remove(Object JavaDoc key) {
879             boolean result = parent.containsKey(key);
880             parent.remove(key);
881             return result;
882         }
883
884         public Iterator JavaDoc iterator() {
885             if (parent.delegateMap != null) {
886                 return parent.delegateMap.keySet().iterator();
887             }
888             if (parent.size() == 0) {
889                 return EmptyIterator.INSTANCE;
890             }
891             return new KeySetIterator(parent);
892         }
893     }
894
895     /**
896      * KeySetIterator
897      */

898     static class KeySetIterator extends EntrySetIterator {
899         
900         KeySetIterator(Flat3Map parent) {
901             super(parent);
902         }
903
904         public Object JavaDoc next() {
905             super.next();
906             return getKey();
907         }
908     }
909     
910     /**
911      * Gets the values view of the map.
912      * Changes made to the view affect this map.
913      * To simply iterate through the values, use {@link #mapIterator()}.
914      *
915      * @return the values view
916      */

917     public Collection JavaDoc values() {
918         if (delegateMap != null) {
919             return delegateMap.values();
920         }
921         return new Values(this);
922     }
923
924     /**
925      * Values
926      */

927     static class Values extends AbstractCollection JavaDoc {
928         private final Flat3Map parent;
929         
930         Values(Flat3Map parent) {
931             super();
932             this.parent = parent;
933         }
934
935         public int size() {
936             return parent.size();
937         }
938         
939         public void clear() {
940             parent.clear();
941         }
942         
943         public boolean contains(Object JavaDoc value) {
944             return parent.containsValue(value);
945         }
946
947         public Iterator JavaDoc iterator() {
948             if (parent.delegateMap != null) {
949                 return parent.delegateMap.values().iterator();
950             }
951             if (parent.size() == 0) {
952                 return EmptyIterator.INSTANCE;
953             }
954             return new ValuesIterator(parent);
955         }
956     }
957
958     /**
959      * ValuesIterator
960      */

961     static class ValuesIterator extends EntrySetIterator {
962         
963         ValuesIterator(Flat3Map parent) {
964             super(parent);
965         }
966
967         public Object JavaDoc next() {
968             super.next();
969             return getValue();
970         }
971     }
972
973     //-----------------------------------------------------------------------
974
/**
975      * Write the map out using a custom routine.
976      */

977     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
978         out.defaultWriteObject();
979         out.writeInt(size());
980         for (MapIterator it = mapIterator(); it.hasNext();) {
981             out.writeObject(it.next()); // key
982
out.writeObject(it.getValue()); // value
983
}
984     }
985
986     /**
987      * Read the map in using a custom routine.
988      */

989     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
990         in.defaultReadObject();
991         int count = in.readInt();
992         if (count > 3) {
993             delegateMap = createDelegateMap();
994         }
995         for (int i = count; i > 0; i--) {
996             put(in.readObject(), in.readObject());
997         }
998     }
999
1000    //-----------------------------------------------------------------------
1001
/**
1002     * Clones the map without cloning the keys or values.
1003     *
1004     * @return a shallow clone
1005     * @since Commons Collections 3.1
1006     */

1007    public Object JavaDoc clone() {
1008        try {
1009            Flat3Map cloned = (Flat3Map) super.clone();
1010            if (cloned.delegateMap != null) {
1011                cloned.delegateMap = (HashedMap) cloned.delegateMap.clone();
1012            }
1013            return cloned;
1014        } catch (CloneNotSupportedException JavaDoc ex) {
1015            throw new InternalError JavaDoc();
1016        }
1017    }
1018
1019    /**
1020     * Compares this map with another.
1021     *
1022     * @param obj the object to compare to
1023     * @return true if equal
1024     */

1025    public boolean equals(Object JavaDoc obj) {
1026        if (obj == this) {
1027            return true;
1028        }
1029        if (delegateMap != null) {
1030            return delegateMap.equals(obj);
1031        }
1032        if (obj instanceof Map JavaDoc == false) {
1033            return false;
1034        }
1035        Map JavaDoc other = (Map JavaDoc) obj;
1036        if (size != other.size()) {
1037            return false;
1038        }
1039        if (size > 0) {
1040            Object JavaDoc otherValue = null;
1041            switch (size) { // drop through
1042
case 3:
1043                    if (other.containsKey(key3) == false) {
1044                        otherValue = other.get(key3);
1045                        if (value3 == null ? otherValue != null : !value3.equals(otherValue)) {
1046                            return false;
1047                        }
1048                    }
1049                case 2:
1050                    if (other.containsKey(key2) == false) {
1051                        otherValue = other.get(key2);
1052                        if (value2 == null ? otherValue != null : !value2.equals(otherValue)) {
1053                            return false;
1054                        }
1055                    }
1056                case 1:
1057                    if (other.containsKey(key1) == false) {
1058                        otherValue = other.get(key1);
1059                        if (value1 == null ? otherValue != null : !value1.equals(otherValue)) {
1060                            return false;
1061                        }
1062                    }
1063            }
1064        }
1065        return true;
1066    }
1067
1068    /**
1069     * Gets the standard Map hashCode.
1070     *
1071     * @return the hash code defined in the Map interface
1072     */

1073    public int hashCode() {
1074        if (delegateMap != null) {
1075            return delegateMap.hashCode();
1076        }
1077        int total = 0;
1078        switch (size) { // drop through
1079
case 3:
1080                total += (hash3 ^ (value3 == null ? 0 : value3.hashCode()));
1081            case 2:
1082                total += (hash2 ^ (value2 == null ? 0 : value2.hashCode()));
1083            case 1:
1084                total += (hash1 ^ (value1 == null ? 0 : value1.hashCode()));
1085        }
1086        return total;
1087    }
1088
1089    /**
1090     * Gets the map as a String.
1091     *
1092     * @return a string version of the map
1093     */

1094    public String JavaDoc toString() {
1095        if (delegateMap != null) {
1096            return delegateMap.toString();
1097        }
1098        if (size == 0) {
1099            return "{}";
1100        }
1101        StringBuffer JavaDoc buf = new StringBuffer JavaDoc(128);
1102        buf.append('{');
1103        switch (size) { // drop through
1104
case 3:
1105                buf.append((key3 == this ? "(this Map)" : key3));
1106                buf.append('=');
1107                buf.append((value3 == this ? "(this Map)" : value3));
1108                buf.append(',');
1109            case 2:
1110                buf.append((key2 == this ? "(this Map)" : key2));
1111                buf.append('=');
1112                buf.append((value2 == this ? "(this Map)" : value2));
1113                buf.append(',');
1114            case 1:
1115                buf.append((key1 == this ? "(this Map)" : key1));
1116                buf.append('=');
1117                buf.append((value1 == this ? "(this Map)" : value1));
1118        }
1119        buf.append('}');
1120        return buf.toString();
1121    }
1122
1123}
1124
Popular Tags