KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > MultiHashMap


1 /*
2  * Copyright 2001-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;
17
18 import java.io.IOException JavaDoc;
19 import java.io.ObjectInputStream JavaDoc;
20 import java.util.AbstractCollection JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.NoSuchElementException JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import org.apache.commons.collections.iterators.EmptyIterator;
30
31 /**
32  * <code>MultiHashMap</code> is the default implementation of the
33  * {@link org.apache.commons.collections.MultiMap MultiMap} interface.
34  * <p>
35  * A <code>MultiMap</code> is a Map with slightly different semantics.
36  * Putting a value into the map will add the value to a Collection at that key.
37  * Getting a value will return a Collection, holding all the values put to that key.
38  * <p>
39  * This implementation uses an <code>ArrayList</code> as the collection.
40  * The internal storage list is made available without cloning via the
41  * <code>get(Object)</code> and <code>entrySet()</code> methods.
42  * The implementation returns <code>null</code> when there are no values mapped to a key.
43  * <p>
44  * For example:
45  * <pre>
46  * MultiMap mhm = new MultiHashMap();
47  * mhm.put(key, "A");
48  * mhm.put(key, "B");
49  * mhm.put(key, "C");
50  * List list = (List) mhm.get(key);</pre>
51  * <p>
52  * <code>list</code> will be a list containing "A", "B", "C".
53  *
54  * @since Commons Collections 2.0
55  * @version $Revision: 1.20 $ $Date: 2004/06/09 22:11:54 $
56  *
57  * @author Christopher Berry
58  * @author James Strachan
59  * @author Steve Downey
60  * @author Stephen Colebourne
61  * @author Julien Buret
62  * @author Serhiy Yevtushenko
63  */

64 public class MultiHashMap extends HashMap JavaDoc implements MultiMap {
65     
66     // backed values collection
67
private transient Collection JavaDoc values = null;
68     
69     // compatibility with commons-collection releases 2.0/2.1
70
private static final long serialVersionUID = 1943563828307035349L;
71
72     /**
73      * Constructor.
74      */

75     public MultiHashMap() {
76         super();
77     }
78
79     /**
80      * Constructor.
81      *
82      * @param initialCapacity the initial map capacity
83      */

84     public MultiHashMap(int initialCapacity) {
85         super(initialCapacity);
86     }
87
88     /**
89      * Constructor.
90      *
91      * @param initialCapacity the initial map capacity
92      * @param loadFactor the amount 0.0-1.0 at which to resize the map
93      */

94     public MultiHashMap(int initialCapacity, float loadFactor) {
95         super(initialCapacity, loadFactor);
96     }
97
98     /**
99      * Constructor that copies the input map creating an independent copy.
100      * <p>
101      * This method performs different behaviour depending on whether the map
102      * specified is a MultiMap or not. If a MultiMap is specified, each internal
103      * collection is also cloned. If the specified map only implements Map, then
104      * the values are not cloned.
105      * <p>
106      * NOTE: From Commons Collections 3.1 this method correctly copies a MultiMap
107      * to form a truly independent new map.
108      *
109      * @param mapToCopy a Map to copy
110      */

111     public MultiHashMap(Map JavaDoc mapToCopy) {
112         // be careful of JDK 1.3 vs 1.4 differences
113
super((int) (mapToCopy.size() * 1.4f));
114         if (mapToCopy instanceof MultiMap) {
115             for (Iterator JavaDoc it = mapToCopy.entrySet().iterator(); it.hasNext();) {
116                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
117                 Collection JavaDoc coll = (Collection JavaDoc) entry.getValue();
118                 Collection JavaDoc newColl = createCollection(coll);
119                 super.put(entry.getKey(), newColl);
120             }
121         } else {
122             putAll(mapToCopy);
123         }
124     }
125
126     /**
127      * Read the object during deserialization.
128      */

129     private void readObject(ObjectInputStream JavaDoc s) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
130         // This method is needed because the 1.2/1.3 Java deserialisation called
131
// put and thus messed up that method
132

133         // default read object
134
s.defaultReadObject();
135
136         // problem only with jvm <1.4
137
String JavaDoc version = "1.2";
138         try {
139             version = System.getProperty("java.version");
140         } catch (SecurityException JavaDoc ex) {
141             // ignore and treat as 1.2/1.3
142
}
143
144         if (version.startsWith("1.2") || version.startsWith("1.3")) {
145             for (Iterator JavaDoc iterator = entrySet().iterator(); iterator.hasNext();) {
146                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iterator.next();
147                 // put has created a extra collection level, remove it
148
super.put(entry.getKey(), ((Collection JavaDoc) entry.getValue()).iterator().next());
149             }
150         }
151     }
152
153     //-----------------------------------------------------------------------
154
/**
155      * Gets the total size of the map by counting all the values.
156      *
157      * @return the total size of the map counting all values
158      * @since Commons Collections 3.1
159      */

160     public int totalSize() {
161         int total = 0;
162         Collection JavaDoc values = super.values();
163         for (Iterator JavaDoc it = values.iterator(); it.hasNext();) {
164             Collection JavaDoc coll = (Collection JavaDoc) it.next();
165             total += coll.size();
166         }
167         return total;
168     }
169
170     /**
171      * Gets the collection mapped to the specified key.
172      * This method is a convenience method to typecast the result of <code>get(key)</code>.
173      *
174      * @param key the key to retrieve
175      * @return the collection mapped to the key, null if no mapping
176      * @since Commons Collections 3.1
177      */

178     public Collection JavaDoc getCollection(Object JavaDoc key) {
179         return (Collection JavaDoc) get(key);
180     }
181
182     /**
183      * Gets the size of the collection mapped to the specified key.
184      *
185      * @param key the key to get size for
186      * @return the size of the collection at the key, zero if key not in map
187      * @since Commons Collections 3.1
188      */

189     public int size(Object JavaDoc key) {
190         Collection JavaDoc coll = getCollection(key);
191         if (coll == null) {
192             return 0;
193         }
194         return coll.size();
195     }
196
197     /**
198      * Gets an iterator for the collection mapped to the specified key.
199      *
200      * @param key the key to get an iterator for
201      * @return the iterator of the collection at the key, empty iterator if key not in map
202      * @since Commons Collections 3.1
203      */

204     public Iterator JavaDoc iterator(Object JavaDoc key) {
205         Collection JavaDoc coll = getCollection(key);
206         if (coll == null) {
207             return EmptyIterator.INSTANCE;
208         }
209         return coll.iterator();
210     }
211
212     /**
213      * Adds the value to the collection associated with the specified key.
214      * <p>
215      * Unlike a normal <code>Map</code> the previous value is not replaced.
216      * Instead the new value is added to the collection stored against the key.
217      *
218      * @param key the key to store against
219      * @param value the value to add to the collection at the key
220      * @return the value added if the map changed and null if the map did not change
221      */

222     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
223         // NOTE:: put is called during deserialization in JDK < 1.4 !!!!!!
224
// so we must have a readObject()
225
Collection JavaDoc coll = getCollection(key);
226         if (coll == null) {
227             coll = createCollection(null);
228             super.put(key, coll);
229         }
230         boolean results = coll.add(value);
231         return (results ? value : null);
232     }
233
234     /**
235      * Adds a collection of values to the collection associated with the specified key.
236      *
237      * @param key the key to store against
238      * @param values the values to add to the collection at the key, null ignored
239      * @return true if this map changed
240      * @since Commons Collections 3.1
241      */

242     public boolean putAll(Object JavaDoc key, Collection JavaDoc values) {
243         if (values == null || values.size() == 0) {
244             return false;
245         }
246         Collection JavaDoc coll = getCollection(key);
247         if (coll == null) {
248             coll = createCollection(values);
249             if (coll.size() == 0) {
250                 return false;
251             }
252             super.put(key, coll);
253             return true;
254         } else {
255             return coll.addAll(values);
256         }
257     }
258
259     /**
260      * Checks whether the map contains the value specified.
261      * <p>
262      * This checks all collections against all keys for the value, and thus could be slow.
263      *
264      * @param value the value to search for
265      * @return true if the map contains the value
266      */

267     public boolean containsValue(Object JavaDoc value) {
268         Set JavaDoc pairs = super.entrySet();
269
270         if (pairs == null) {
271             return false;
272         }
273         Iterator JavaDoc pairsIterator = pairs.iterator();
274         while (pairsIterator.hasNext()) {
275             Map.Entry JavaDoc keyValuePair = (Map.Entry JavaDoc) pairsIterator.next();
276             Collection JavaDoc coll = (Collection JavaDoc) keyValuePair.getValue();
277             if (coll.contains(value)) {
278                 return true;
279             }
280         }
281         return false;
282     }
283
284     /**
285      * Checks whether the collection at the specified key contains the value.
286      *
287      * @param value the value to search for
288      * @return true if the map contains the value
289      * @since Commons Collections 3.1
290      */

291     public boolean containsValue(Object JavaDoc key, Object JavaDoc value) {
292         Collection JavaDoc coll = getCollection(key);
293         if (coll == null) {
294             return false;
295         }
296         return coll.contains(value);
297     }
298
299     /**
300      * Removes a specific value from map.
301      * <p>
302      * The item is removed from the collection mapped to the specified key.
303      * Other values attached to that key are unaffected.
304      * <p>
305      * If the last value for a key is removed, <code>null</code> will be returned
306      * from a subsequant <code>get(key)</code>.
307      *
308      * @param key the key to remove from
309      * @param item the value to remove
310      * @return the value removed (which was passed in), null if nothing removed
311      */

312     public Object JavaDoc remove(Object JavaDoc key, Object JavaDoc item) {
313         Collection JavaDoc valuesForKey = getCollection(key);
314         if (valuesForKey == null) {
315             return null;
316         }
317         valuesForKey.remove(item);
318
319         // remove the list if it is now empty
320
// (saves space, and allows equals to work)
321
if (valuesForKey.isEmpty()){
322             remove(key);
323         }
324         return item;
325     }
326
327     /**
328      * Clear the map.
329      * <p>
330      * This clears each collection in the map, and so may be slow.
331      */

332     public void clear() {
333         // For gc, clear each list in the map
334
Set JavaDoc pairs = super.entrySet();
335         Iterator JavaDoc pairsIterator = pairs.iterator();
336         while (pairsIterator.hasNext()) {
337             Map.Entry JavaDoc keyValuePair = (Map.Entry JavaDoc) pairsIterator.next();
338             Collection JavaDoc coll = (Collection JavaDoc) keyValuePair.getValue();
339             coll.clear();
340         }
341         super.clear();
342     }
343
344     /**
345      * Gets a collection containing all the values in the map.
346      * <p>
347      * This returns a collection containing the combination of values from all keys.
348      *
349      * @return a collection view of the values contained in this map
350      */

351     public Collection JavaDoc values() {
352         Collection JavaDoc vs = values;
353         return (vs != null ? vs : (values = new Values()));
354     }
355
356     //-----------------------------------------------------------------------
357
/**
358      * Inner class to view the elements.
359      */

360     private class Values extends AbstractCollection JavaDoc {
361
362         public Iterator JavaDoc iterator() {
363             return new ValueIterator();
364         }
365
366         public int size() {
367             int compt = 0;
368             Iterator JavaDoc it = iterator();
369             while (it.hasNext()) {
370                 it.next();
371                 compt++;
372             }
373             return compt;
374         }
375
376         public void clear() {
377             MultiHashMap.this.clear();
378         }
379
380     }
381
382     /**
383      * Inner iterator to view the elements.
384      */

385     private class ValueIterator implements Iterator JavaDoc {
386         private Iterator JavaDoc backedIterator;
387         private Iterator JavaDoc tempIterator;
388
389         private ValueIterator() {
390             backedIterator = MultiHashMap.super.values().iterator();
391         }
392
393         private boolean searchNextIterator() {
394             while (tempIterator == null || tempIterator.hasNext() == false) {
395                 if (backedIterator.hasNext() == false) {
396                     return false;
397                 }
398                 tempIterator = ((Collection JavaDoc) backedIterator.next()).iterator();
399             }
400             return true;
401         }
402
403         public boolean hasNext() {
404             return searchNextIterator();
405         }
406
407         public Object JavaDoc next() {
408             if (searchNextIterator() == false) {
409                 throw new NoSuchElementException JavaDoc();
410             }
411             return tempIterator.next();
412         }
413
414         public void remove() {
415             if (tempIterator == null) {
416                 throw new IllegalStateException JavaDoc();
417             }
418             tempIterator.remove();
419         }
420
421     }
422
423     //-----------------------------------------------------------------------
424
/**
425      * Clones the map creating an independent copy.
426      * <p>
427      * The clone will shallow clone the collections as well as the map.
428      *
429      * @return the cloned map
430      */

431     public Object JavaDoc clone() {
432         MultiHashMap cloned = (MultiHashMap) super.clone();
433
434         // clone each Collection container
435
for (Iterator JavaDoc it = cloned.entrySet().iterator(); it.hasNext();) {
436             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
437             Collection JavaDoc coll = (Collection JavaDoc) entry.getValue();
438             Collection JavaDoc newColl = createCollection(coll);
439             entry.setValue(newColl);
440         }
441         return cloned;
442     }
443
444     /**
445      * Creates a new instance of the map value Collection container.
446      * <p>
447      * This method can be overridden to use your own collection type.
448      *
449      * @param coll the collection to copy, may be null
450      * @return the new collection
451      */

452     protected Collection JavaDoc createCollection(Collection JavaDoc coll) {
453         if (coll == null) {
454             return new ArrayList JavaDoc();
455         } else {
456             return new ArrayList JavaDoc(coll);
457         }
458     }
459
460 }
461
Popular Tags