Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
QueueMap |
|
| 1.9642857142857142;1.964 |
1 | /* |
|
2 | Wotonomy: OpenStep design patterns for pure Java applications. |
|
3 | Copyright (C) 2000 Blacksmith, Inc. |
|
4 | ||
5 | This library is free software; you can redistribute it and/or |
|
6 | modify it under the terms of the GNU Lesser General Public |
|
7 | License as published by the Free Software Foundation; either |
|
8 | version 2.1 of the License, or (at your option) any later version. |
|
9 | ||
10 | This library is distributed in the hope that it will be useful, |
|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 | Lesser General Public License for more details. |
|
14 | ||
15 | You should have received a copy of the GNU Lesser General Public |
|
16 | License along with this library; if not, see http://www.gnu.org |
|
17 | */ |
|
18 | ||
19 | package net.wotonomy.foundation.internal; |
|
20 | ||
21 | import java.util.*; //collections |
|
22 | ||
23 | /** |
|
24 | * A queue based implementation of the Map interface. This class provides for |
|
25 | * all the opertions of a map, but keeps the entries in the same sequence as |
|
26 | * originally added. The first entry placed in the map will be the first |
|
27 | * entry retreived during iteration: first-in, first-out (FIFO). <BR><BR> |
|
28 | * |
|
29 | * Keys cannot be duplicated. If an entry is made using a key that already |
|
30 | * exists, the value for that key will be replaced with that new value. There |
|
31 | * are no such restrictions on the values. The values may be null. <BR><BR> |
|
32 | * |
|
33 | * Some convenience methods are provided for the queue type operations. The |
|
34 | * first key can be retreived as well as the last key. A key can be used |
|
35 | * to retreive its corresponding value from the map. A value can also be used |
|
36 | * to retreive its key from the map, however, since there may be multiple values |
|
37 | * of the same equality, the first key found will be returned. <BR><BR> |
|
38 | * |
|
39 | * @author rglista@blacksmith.com |
|
40 | * @author mpowers@blacksmith.com |
|
41 | * @date $Date: 2006-02-18 22:41:36 +0000 (Sat, 18 Feb 2006) $ |
|
42 | * @revision $Revision: 899 $ |
|
43 | */ |
|
44 | public class QueueMap implements Map |
|
45 | { |
|
46 | List values; |
|
47 | List keys; |
|
48 | Map keyToValue; |
|
49 | ||
50 | /** |
|
51 | * Creates an empty QueueMap. |
|
52 | */ |
|
53 | 0 | public QueueMap() |
54 | 0 | { |
55 | 0 | values = new LinkedList(); |
56 | 0 | keys = new LinkedList(); |
57 | 0 | keyToValue = new HashMap(); |
58 | 0 | } |
59 | ||
60 | /** |
|
61 | * Creates a QueueMap and places the entries from the passed in map into this map. |
|
62 | * The order of the entries is based on however the iterator iteratated through |
|
63 | * the map entries. |
|
64 | * @param t A map object. |
|
65 | */ |
|
66 | 0 | public QueueMap( Map t ) |
67 | 0 | { |
68 | 0 | values = new ArrayList(); |
69 | 0 | keys = new ArrayList(); |
70 | 0 | keyToValue = new HashMap(); |
71 | ||
72 | 0 | putAll( t ); |
73 | 0 | } |
74 | ||
75 | /** |
|
76 | * Removes all the entries from this map. |
|
77 | */ |
|
78 | public void clear() |
|
79 | { |
|
80 | 0 | values.clear(); |
81 | 0 | keys.clear(); |
82 | 0 | keyToValue.clear(); |
83 | 0 | } |
84 | ||
85 | /** |
|
86 | * Tests to see if the key is contained in the map. If the key is present in |
|
87 | * the map, then TRUE is returned, otherwise FALSE is returned. The equals() |
|
88 | * operation is used to determine equality. |
|
89 | * @return True if the map contains the given key, false otherwise. |
|
90 | */ |
|
91 | public boolean containsKey( Object key ) |
|
92 | { |
|
93 | 0 | return keyToValue.containsKey( key ); |
94 | } |
|
95 | ||
96 | /** |
|
97 | * Tests to see if the value is contained in the map. If the value is present in |
|
98 | * the map, then TRUE is returned, otherwise FALSE is returned. The equals() |
|
99 | * operation is used to determine equality. The value can be null and will |
|
100 | * return TRUE if null is a value in the map. If TRUE is returned, then there |
|
101 | * may be more than one values in the map. |
|
102 | * @return True if the map contains the given value, false otherwise. |
|
103 | */ |
|
104 | public boolean containsValue( Object value ) |
|
105 | { |
|
106 | 0 | return keyToValue.containsValue( value ); |
107 | } |
|
108 | ||
109 | /** |
|
110 | * Returns a set view of the mappings contained in this map. Each element |
|
111 | * in the returned set is a <tt>Map.Entry</tt>. The returned set is NOT backed |
|
112 | * by the map, so changes to the map are NOT reflected in the set, and vice-versa. |
|
113 | * The returned set is independent of this Map and its underlying structure. |
|
114 | * |
|
115 | * @return a set view of the mappings contained in this map. |
|
116 | */ |
|
117 | public Set entrySet() |
|
118 | { |
|
119 | 0 | Set result = new HashSet(keys.size(), 1F); |
120 | ||
121 | 0 | for ( int i = 0; i < keys.size(); i++ ) |
122 | { |
|
123 | 0 | result.add( new KeyValuePair( keys.get(i), values.get(i) ) ); |
124 | } |
|
125 | ||
126 | 0 | return result; |
127 | } |
|
128 | ||
129 | /** |
|
130 | * Compares the specified object with this map for equality. Returns |
|
131 | * <tt>true</tt> if the given object is also a map and the two Maps |
|
132 | * represent the same mappings. More formally, two maps <tt>t1</tt> and |
|
133 | * <tt>t2</tt> represent the same mappings if |
|
134 | * <tt>t1.entrySet().equals(t2.entrySet())</tt>. This ensures that the |
|
135 | * <tt>equals</tt> method works properly across different implementations |
|
136 | * of the <tt>Map</tt> interface. |
|
137 | * |
|
138 | * @param o object to be compared for equality with this map. |
|
139 | * @return <tt>true</tt> if the specified object is equal to this map. |
|
140 | */ |
|
141 | public boolean equals( Object o ) |
|
142 | { |
|
143 | 0 | return keyToValue.equals( o ); |
144 | } |
|
145 | ||
146 | /** |
|
147 | * Returns the corresponding value for the given key. The returned value may be |
|
148 | * null as that is a legal value in this map. However, if the key is not |
|
149 | * contained in this map then null is also returned. Use the containsKey() |
|
150 | * method to distinguish between the two cases. |
|
151 | * @param key A key into the map. |
|
152 | * @return The value corresponding to the key (can be null), or null if the key |
|
153 | * is not contained in the map. |
|
154 | */ |
|
155 | public Object get( Object key ) |
|
156 | { |
|
157 | 0 | return keyToValue.get( key ); |
158 | } |
|
159 | ||
160 | /** |
|
161 | * Returns the hash code value for this map. The hash code of a map |
|
162 | * is defined to be the sum of the hashCodes of each entry in the map's |
|
163 | * entrySet view. This ensures that <tt>t1.equals(t2)</tt> implies |
|
164 | * that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps |
|
165 | * <tt>t1</tt> and <tt>t2</tt>, as required by the general |
|
166 | * contract of Object.hashCode. |
|
167 | * |
|
168 | * @return the hash code value for this map. |
|
169 | */ |
|
170 | public int hashCode() |
|
171 | { |
|
172 | 0 | return keyToValue.hashCode(); |
173 | } |
|
174 | ||
175 | /** |
|
176 | * Returns true is this map contains no key-value mappings. |
|
177 | * @return True is this map contains no entries, false otherwise. |
|
178 | */ |
|
179 | public boolean isEmpty() |
|
180 | { |
|
181 | 0 | return keyToValue.isEmpty(); |
182 | } |
|
183 | ||
184 | /** |
|
185 | * Returns the keys used in the map. There is no order implied in the returned |
|
186 | * set and may be different than the the order in which they were inserted. |
|
187 | * @return A Set containing all the keys used in the map. |
|
188 | */ |
|
189 | public Set keySet() |
|
190 | { |
|
191 | 0 | Set result = new HashSet(keys.size(), 1F); |
192 | ||
193 | 0 | for ( int i = 0; i < keys.size(); i++ ) |
194 | { |
|
195 | 0 | result.add( keys.get(i) ); |
196 | } |
|
197 | ||
198 | 0 | return result; |
199 | } |
|
200 | ||
201 | /** |
|
202 | * Places the key/value entry into the map at the end position. If the key |
|
203 | * already exists in the map, then its value is replaced by the new given |
|
204 | * value. The mapping does not change order in this case. The specified |
|
205 | * key cannot be null. |
|
206 | * @param key The key to place into the map, cannot be null. |
|
207 | * @param value The value to associate with the key, may be null. |
|
208 | * @return Null if the key is new, the replaced value if the key already |
|
209 | * existed. (Note: If the key was null, then null is returned.) |
|
210 | */ |
|
211 | public Object put( Object key, Object value ) |
|
212 | { |
|
213 | 0 | if ( key == null ) return null; |
214 | ||
215 | 0 | if ( keys.contains( key ) ) |
216 | { |
|
217 | 0 | values.remove( keys.indexOf( key ) ); |
218 | 0 | values.add( keys.indexOf( key ), value ); |
219 | 0 | } |
220 | else |
|
221 | { |
|
222 | 0 | values.add( value ); |
223 | 0 | keys.add( key ); |
224 | } |
|
225 | ||
226 | 0 | return keyToValue.put( key, value ); |
227 | } |
|
228 | ||
229 | /** |
|
230 | * Places all the entries from this given map into this map. If the keys |
|
231 | * already exist, then there values are replaced. |
|
232 | * @param t A map of key/value entries. |
|
233 | */ |
|
234 | public void putAll( Map t ) |
|
235 | { |
|
236 | 0 | if ( t == null ) |
237 | { |
|
238 | // Nothing to do! |
|
239 | 0 | return; |
240 | } |
|
241 | ||
242 | // Place the entries from the passed in map into this map. |
|
243 | 0 | for ( Iterator it = t.keySet().iterator(); it.hasNext(); ) |
244 | { |
|
245 | 0 | Object aKey = it.next(); |
246 | 0 | put( aKey, t.get( aKey ) ); |
247 | 0 | } |
248 | 0 | } |
249 | ||
250 | /** |
|
251 | * Remove the mapping of the key and associated value from this map. |
|
252 | * Note: null is a valid value in the map. |
|
253 | * @param key A key to remove from the map, cannot be null. |
|
254 | * @return The value of the removed mapping, null if the mapping did not exist. |
|
255 | * Null could also be returned if the associated value of the key was null. |
|
256 | */ |
|
257 | public Object remove( Object key ) |
|
258 | { |
|
259 | 0 | if ( key == null ) return null; |
260 | ||
261 | 0 | Object value = null; |
262 | ||
263 | 0 | if ( containsKey( key ) ) |
264 | { |
|
265 | 0 | value = keyToValue.remove( key ); |
266 | 0 | int i = values.indexOf( value ); |
267 | 0 | if ( i != -1 ) |
268 | { |
|
269 | 0 | keys.remove( i ); |
270 | 0 | values.remove( i ); |
271 | } |
|
272 | } |
|
273 | ||
274 | 0 | return value; |
275 | } |
|
276 | ||
277 | /** |
|
278 | * Returns the number of key/value pairs in this map. |
|
279 | * @return The number of pairs. |
|
280 | */ |
|
281 | public int size() |
|
282 | { |
|
283 | 0 | return values.size(); |
284 | } |
|
285 | ||
286 | /** |
|
287 | * Returns an ordered list of keys from the map. The order is the same |
|
288 | * as the added order of the mapped items.<br> |
|
289 | * NOTE: The returned list is the underlying keys list used by this class. |
|
290 | * If modification are to be made to this list, it should be cloned first. |
|
291 | * @return An ordered list of keys. |
|
292 | */ |
|
293 | public List keys() |
|
294 | { |
|
295 | 0 | return keys; |
296 | } |
|
297 | ||
298 | /** |
|
299 | * Returns an ordered list of values from the map. The order is the same |
|
300 | * as the added order of the mapped items. |
|
301 | * NOTE: The returned list is the underlying keys list used by this class. |
|
302 | * If modification are to be made to this list, it should be cloned first. |
|
303 | * @return An ordered list of values. |
|
304 | */ |
|
305 | public Collection values() |
|
306 | { |
|
307 | 0 | return values; |
308 | } |
|
309 | ||
310 | /** |
|
311 | * Returns the corresponding value for the given key. The returned value may be |
|
312 | * null as that is a legal value in this map. However, if the key is not |
|
313 | * contained in this map then null is also returned. Use the containsKey() |
|
314 | * method to distinguish between the two cases. |
|
315 | * @param key A key into the map. |
|
316 | * @return The value corresponding to the key (can be null), or null if the key |
|
317 | * is not contained in the map. |
|
318 | */ |
|
319 | public Object getValueForKey( Object key ) |
|
320 | { |
|
321 | 0 | return keyToValue.get( key ); |
322 | } |
|
323 | ||
324 | /** |
|
325 | * Returns the associated key for this value. Since there may be more than one |
|
326 | * of the same value in the map, this returns the first key associated with this |
|
327 | * value. Null is returned if the value is not in the map. |
|
328 | * @param value A value that is contained in this map. |
|
329 | * @return A first key that corresponds to this value. |
|
330 | */ |
|
331 | public Object getKeyForValue( Object value ) |
|
332 | { |
|
333 | 0 | int i = values.indexOf( value ); |
334 | 0 | if ( i != -1 ) |
335 | { |
|
336 | 0 | return keys.get( i ); |
337 | } |
|
338 | 0 | return null; |
339 | } |
|
340 | ||
341 | /** |
|
342 | * Returns the first key in the map. If the map is empty, then null is |
|
343 | * returned. |
|
344 | * @return The first key in the map, null if there are no mappings. |
|
345 | */ |
|
346 | public Object getFirstKey() |
|
347 | { |
|
348 | 0 | if ( keys.size() < 1 ) |
349 | { |
|
350 | 0 | return null; |
351 | } |
|
352 | 0 | return keys.get(0); |
353 | } |
|
354 | ||
355 | /** |
|
356 | * Returns the last key in the map. If the map is empty, then null is |
|
357 | * returned. |
|
358 | * @return The last key in the map, null if there are no mappings. |
|
359 | */ |
|
360 | public Object getLastKey() |
|
361 | { |
|
362 | 0 | if ( keys.size() < 1 ) |
363 | { |
|
364 | 0 | return null; |
365 | } |
|
366 | 0 | return keys.get( keys.size() -1 ); |
367 | } |
|
368 | ||
369 | /** |
|
370 | * This class contains a key/value pair. The key must be a valid object, |
|
371 | * it cannot be null. The value can be a valid object or null. |
|
372 | */ |
|
373 | static public class KeyValuePair implements Map.Entry |
|
374 | { |
|
375 | Object key; |
|
376 | Object value; |
|
377 | ||
378 | /** |
|
379 | * Default constructor. The constructor takes the key and value as parameters. |
|
380 | * Since the key cannot be null, it must be specified during initialization. |
|
381 | * The value can be null. |
|
382 | * @param key The key object of this pair. The key cannot be null. |
|
383 | * @param value The value object of this pair. The value can be null. |
|
384 | */ |
|
385 | 0 | public KeyValuePair( Object aKey, Object aValue ) |
386 | 0 | { |
387 | 0 | key = aKey; |
388 | 0 | value = aValue; |
389 | 0 | } |
390 | ||
391 | /** |
|
392 | * Returns the key object of this pair. |
|
393 | * @return The key object. |
|
394 | */ |
|
395 | public Object getKey() |
|
396 | { |
|
397 | 0 | return key; |
398 | } |
|
399 | ||
400 | /** |
|
401 | * Returns the the value object of this pair. May be null. |
|
402 | * @return The value object. |
|
403 | */ |
|
404 | public Object getValue() |
|
405 | { |
|
406 | 0 | return value; |
407 | } |
|
408 | ||
409 | /** |
|
410 | * Sets the value object of this pair. May be an object or null. |
|
411 | * @param aValue The value object to place into this pair. |
|
412 | */ |
|
413 | public Object setValue( Object aValue ) |
|
414 | { |
|
415 | 0 | Object result = value; |
416 | 0 | value = aValue; |
417 | 0 | return result; |
418 | } |
|
419 | ||
420 | public boolean equals( Object o ) |
|
421 | { |
|
422 | 0 | if ( o instanceof KeyValuePair ) |
423 | { |
|
424 | 0 | KeyValuePair p = (KeyValuePair) o; |
425 | 0 | if ( ( key.equals( p.getKey() ) ) && ( value.equals( p.getValue() ) ) ) |
426 | { |
|
427 | 0 | return true; |
428 | } |
|
429 | } |
|
430 | 0 | return false; |
431 | } |
|
432 | } |
|
433 | ||
434 | /** |
|
435 | * Returns a string reprsentation of this class. The contents of the |
|
436 | * map are placed in the string in its proper order. |
|
437 | */ |
|
438 | public String toString() |
|
439 | { |
|
440 | 0 | int max = size() - 1; |
441 | 0 | StringBuffer buf = new StringBuffer(); |
442 | ||
443 | 0 | buf.append("{"); |
444 | 0 | for (int j = 0; j <= max; j++) |
445 | { |
|
446 | 0 | buf.append(keys.get(j) + "=" + values.get(j)); |
447 | 0 | if (j < max) |
448 | { |
|
449 | 0 | buf.append(", "); |
450 | } |
|
451 | } |
|
452 | 0 | buf.append("}"); |
453 | ||
454 | 0 | return buf.toString(); |
455 | } |
|
456 | ||
457 | // unit test |
|
458 | public static void main( String[] argv ) |
|
459 | { |
|
460 | QueueMap qMap; |
|
461 | ||
462 | 0 | qMap = new QueueMap(); |
463 | 0 | for (int i = 0; i < 5; i++) |
464 | { |
|
465 | 0 | qMap.put(Integer.toString(i), Integer.toString(i)); |
466 | } |
|
467 | 0 | System.out.println("\nMap = " + qMap); |
468 | 0 | System.out.println("Keys = " + qMap.keys()); |
469 | 0 | System.out.println("Values = " + qMap.values()); |
470 | ||
471 | 0 | qMap = new QueueMap(); |
472 | 0 | for (int i = 0; i < 5; i++) |
473 | { |
|
474 | 0 | qMap.put(Integer.toString(i), "A"); |
475 | } |
|
476 | 0 | System.out.println("\nMap = " + qMap); |
477 | 0 | System.out.println("Keys = " + qMap.keys()); |
478 | 0 | System.out.println("Values = " + qMap.values()); |
479 | ||
480 | 0 | qMap = new QueueMap(); |
481 | 0 | for (int i = 0; i < 5; i++) |
482 | { |
|
483 | 0 | qMap.put(Integer.toString(i), null); |
484 | } |
|
485 | 0 | System.out.println("\nMap = " + qMap); |
486 | 0 | System.out.println("Keys = " + qMap.keys()); |
487 | 0 | System.out.println("Values = " + qMap.values()); |
488 | ||
489 | 0 | Map aMap = new HashMap(); |
490 | 0 | for (int i = 0; i < 5; i++) |
491 | { |
|
492 | 0 | aMap.put(Integer.toString(i), Integer.toString(i)); |
493 | } |
|
494 | 0 | qMap = new QueueMap( aMap ); |
495 | 0 | System.out.println("\nHashMap = " + aMap); |
496 | 0 | System.out.println("Map = " + qMap); |
497 | 0 | System.out.println("Keys = " + qMap.keys()); |
498 | 0 | System.out.println("Values = " + qMap.values()); |
499 | ||
500 | 0 | qMap = new QueueMap(); |
501 | 0 | qMap.put( "Test1", "String1" ); |
502 | 0 | qMap.put( "Test2", "String2" ); |
503 | 0 | qMap.put( "Test3", "String3" ); |
504 | 0 | qMap.put( "Test4", "String4" ); |
505 | 0 | qMap.put( "Test5", "String5" ); |
506 | 0 | System.out.println("\nStandard Test, Map = " + qMap); |
507 | 0 | qMap.put( "Test6", "String6" ); |
508 | 0 | qMap.put( "Test7", "String7" ); |
509 | 0 | System.out.println("Put New Test, Map = " + qMap); |
510 | 0 | qMap.put( "Test2", "New String2" ); |
511 | 0 | qMap.put( "Test6", "New String6" ); |
512 | 0 | System.out.println("Put Existing Test, Map = " + qMap); |
513 | 0 | qMap.put( "Test5", null ); |
514 | 0 | qMap.put( "Test8", null ); |
515 | 0 | System.out.println("Put Null Test, Map = " + qMap); |
516 | 0 | qMap.remove( "Test1" ); |
517 | 0 | qMap.remove( "Test3" ); |
518 | 0 | qMap.remove( "Test5" ); |
519 | 0 | qMap.remove( "Test9" ); |
520 | 0 | System.out.println("Remove Test, Map = " + qMap); |
521 | 0 | System.out.println(" Keys = " + qMap.keys()); |
522 | 0 | System.out.println(" Values = " + qMap.values()); |
523 | 0 | qMap.clear(); |
524 | 0 | qMap.put( "Test10", "String10" ); |
525 | 0 | qMap.put( "Test11", "String11" ); |
526 | 0 | qMap.put( "Test12", "String12" ); |
527 | 0 | System.out.println("Clear Test, Map = " + qMap); |
528 | ||
529 | 0 | aMap = new HashMap(); |
530 | 0 | aMap.put( "Test10", "String10" ); |
531 | 0 | aMap.put( "Test11", "String11" ); |
532 | 0 | aMap.put( "Test12", "String12" ); |
533 | 0 | System.out.println("Equality Test, Equal = " + qMap.equals( aMap )); |
534 | 0 | } |
535 | ||
536 | } |
|
537 | ||
538 |