Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
WeakFastHashMap |
|
| 2.5;2.5 | ||||
WeakFastHashMap$1 |
|
| 2.5;2.5 | ||||
WeakFastHashMap$CollectionView |
|
| 2.5;2.5 | ||||
WeakFastHashMap$CollectionView$CollectionViewIterator |
|
| 2.5;2.5 | ||||
WeakFastHashMap$EntrySet |
|
| 2.5;2.5 | ||||
WeakFastHashMap$KeySet |
|
| 2.5;2.5 | ||||
WeakFastHashMap$Values |
|
| 2.5;2.5 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | package org.apache.commons.beanutils; | |
18 | ||
19 | import java.util.Collection; | |
20 | import java.util.ConcurrentModificationException; | |
21 | import java.util.HashMap; | |
22 | import java.util.Iterator; | |
23 | import java.util.Map; | |
24 | import java.util.Set; | |
25 | import java.util.WeakHashMap; | |
26 | ||
27 | /** | |
28 | * <p>A customized implementation of <code>java.util.HashMap</code> designed | |
29 | * to operate in a multithreaded environment where the large majority of | |
30 | * method calls are read-only, instead of structural changes. When operating | |
31 | * in "fast" mode, read calls are non-synchronized and write calls perform the | |
32 | * following steps:</p> | |
33 | * <ul> | |
34 | * <li>Clone the existing collection | |
35 | * <li>Perform the modification on the clone | |
36 | * <li>Replace the existing collection with the (modified) clone | |
37 | * </ul> | |
38 | * <p>When first created, objects of this class default to "slow" mode, where | |
39 | * all accesses of any type are synchronized but no cloning takes place. This | |
40 | * is appropriate for initially populating the collection, followed by a switch | |
41 | * to "fast" mode (by calling <code>setFast(true)</code>) after initialization | |
42 | * is complete.</p> | |
43 | * | |
44 | * <p><strong>NOTE</strong>: If you are creating and accessing a | |
45 | * <code>HashMap</code> only within a single thread, you should use | |
46 | * <code>java.util.HashMap</code> directly (with no synchronization), for | |
47 | * maximum performance.</p> | |
48 | * | |
49 | * <p><strong>NOTE</strong>: <i>This class is not cross-platform. | |
50 | * Using it may cause unexpected failures on some architectures.</i> | |
51 | * It suffers from the same problems as the double-checked locking idiom. | |
52 | * In particular, the instruction that clones the internal collection and the | |
53 | * instruction that sets the internal reference to the clone can be executed | |
54 | * or perceived out-of-order. This means that any read operation might fail | |
55 | * unexpectedly, as it may be reading the state of the internal collection | |
56 | * before the internal collection is fully formed. | |
57 | * For more information on the double-checked locking idiom, see the | |
58 | * <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html"> | |
59 | * Double-Checked Locking Idiom Is Broken Declaration</a>.</p> | |
60 | * | |
61 | * @since Commons Collections 1.0 | |
62 | * @version $Revision: 687089 $ $Date: 2008-08-19 12:33:30 -0400 (Tue, 19 Aug 2008) $ | |
63 | * | |
64 | * @author Craig R. McClanahan | |
65 | * @author Stephen Colebourne | |
66 | */ | |
67 | 0 | class WeakFastHashMap extends HashMap { |
68 | ||
69 | /** | |
70 | * The underlying map we are managing. | |
71 | */ | |
72 | 627 | private Map map = null; |
73 | ||
74 | /** | |
75 | * Are we currently operating in "fast" mode? | |
76 | */ | |
77 | 627 | private boolean fast = false; |
78 | ||
79 | // Constructors | |
80 | // ---------------------------------------------------------------------- | |
81 | ||
82 | /** | |
83 | * Construct an empty map. | |
84 | */ | |
85 | public WeakFastHashMap() { | |
86 | 627 | super(); |
87 | 627 | this.map = createMap(); |
88 | 627 | } |
89 | ||
90 | /** | |
91 | * Construct an empty map with the specified capacity. | |
92 | * | |
93 | * @param capacity the initial capacity of the empty map | |
94 | */ | |
95 | public WeakFastHashMap(int capacity) { | |
96 | 0 | super(); |
97 | 0 | this.map = createMap(capacity); |
98 | 0 | } |
99 | ||
100 | /** | |
101 | * Construct an empty map with the specified capacity and load factor. | |
102 | * | |
103 | * @param capacity the initial capacity of the empty map | |
104 | * @param factor the load factor of the new map | |
105 | */ | |
106 | public WeakFastHashMap(int capacity, float factor) { | |
107 | 0 | super(); |
108 | 0 | this.map = createMap(capacity, factor); |
109 | 0 | } |
110 | ||
111 | /** | |
112 | * Construct a new map with the same mappings as the specified map. | |
113 | * | |
114 | * @param map the map whose mappings are to be copied | |
115 | */ | |
116 | public WeakFastHashMap(Map map) { | |
117 | 0 | super(); |
118 | 0 | this.map = createMap(map); |
119 | 0 | } |
120 | ||
121 | ||
122 | // Property access | |
123 | // ---------------------------------------------------------------------- | |
124 | ||
125 | /** | |
126 | * Returns true if this map is operating in fast mode. | |
127 | * | |
128 | * @return true if this map is operating in fast mode | |
129 | */ | |
130 | public boolean getFast() { | |
131 | 0 | return (this.fast); |
132 | } | |
133 | ||
134 | /** | |
135 | * Sets whether this map is operating in fast mode. | |
136 | * | |
137 | * @param fast true if this map should operate in fast mode | |
138 | */ | |
139 | public void setFast(boolean fast) { | |
140 | 942 | this.fast = fast; |
141 | 942 | } |
142 | ||
143 | ||
144 | // Map access | |
145 | // ---------------------------------------------------------------------- | |
146 | // These methods can forward straight to the wrapped Map in 'fast' mode. | |
147 | // (because they are query methods) | |
148 | ||
149 | /** | |
150 | * Return the value to which this map maps the specified key. Returns | |
151 | * <code>null</code> if the map contains no mapping for this key, or if | |
152 | * there is a mapping with a value of <code>null</code>. Use the | |
153 | * <code>containsKey()</code> method to disambiguate these cases. | |
154 | * | |
155 | * @param key the key whose value is to be returned | |
156 | * @return the value mapped to that key, or null | |
157 | */ | |
158 | public Object get(Object key) { | |
159 | 3052 | if (fast) { |
160 | 3029 | return (map.get(key)); |
161 | } else { | |
162 | 23 | synchronized (map) { |
163 | 23 | return (map.get(key)); |
164 | 0 | } |
165 | } | |
166 | } | |
167 | ||
168 | /** | |
169 | * Return the number of key-value mappings in this map. | |
170 | * | |
171 | * @return the current size of the map | |
172 | */ | |
173 | public int size() { | |
174 | 0 | if (fast) { |
175 | 0 | return (map.size()); |
176 | } else { | |
177 | 0 | synchronized (map) { |
178 | 0 | return (map.size()); |
179 | 0 | } |
180 | } | |
181 | } | |
182 | ||
183 | /** | |
184 | * Return <code>true</code> if this map contains no mappings. | |
185 | * | |
186 | * @return is the map currently empty | |
187 | */ | |
188 | public boolean isEmpty() { | |
189 | 0 | if (fast) { |
190 | 0 | return (map.isEmpty()); |
191 | } else { | |
192 | 0 | synchronized (map) { |
193 | 0 | return (map.isEmpty()); |
194 | 0 | } |
195 | } | |
196 | } | |
197 | ||
198 | /** | |
199 | * Return <code>true</code> if this map contains a mapping for the | |
200 | * specified key. | |
201 | * | |
202 | * @param key the key to be searched for | |
203 | * @return true if the map contains the key | |
204 | */ | |
205 | public boolean containsKey(Object key) { | |
206 | 0 | if (fast) { |
207 | 0 | return (map.containsKey(key)); |
208 | } else { | |
209 | 0 | synchronized (map) { |
210 | 0 | return (map.containsKey(key)); |
211 | 0 | } |
212 | } | |
213 | } | |
214 | ||
215 | /** | |
216 | * Return <code>true</code> if this map contains one or more keys mapping | |
217 | * to the specified value. | |
218 | * | |
219 | * @param value the value to be searched for | |
220 | * @return true if the map contains the value | |
221 | */ | |
222 | public boolean containsValue(Object value) { | |
223 | 0 | if (fast) { |
224 | 0 | return (map.containsValue(value)); |
225 | } else { | |
226 | 0 | synchronized (map) { |
227 | 0 | return (map.containsValue(value)); |
228 | 0 | } |
229 | } | |
230 | } | |
231 | ||
232 | // Map modification | |
233 | // ---------------------------------------------------------------------- | |
234 | // These methods perform special behaviour in 'fast' mode. | |
235 | // The map is cloned, updated and then assigned back. | |
236 | // See the comments at the top as to why this won't always work. | |
237 | ||
238 | /** | |
239 | * Associate the specified value with the specified key in this map. | |
240 | * If the map previously contained a mapping for this key, the old | |
241 | * value is replaced and returned. | |
242 | * | |
243 | * @param key the key with which the value is to be associated | |
244 | * @param value the value to be associated with this key | |
245 | * @return the value previously mapped to the key, or null | |
246 | */ | |
247 | public Object put(Object key, Object value) { | |
248 | 20861 | if (fast) { |
249 | 9467 | synchronized (this) { |
250 | 9467 | Map temp = cloneMap(map); |
251 | 9467 | Object result = temp.put(key, value); |
252 | 9467 | map = temp; |
253 | 9467 | return (result); |
254 | 0 | } |
255 | } else { | |
256 | 11394 | synchronized (map) { |
257 | 11394 | return (map.put(key, value)); |
258 | 0 | } |
259 | } | |
260 | } | |
261 | ||
262 | /** | |
263 | * Copy all of the mappings from the specified map to this one, replacing | |
264 | * any mappings with the same keys. | |
265 | * | |
266 | * @param in the map whose mappings are to be copied | |
267 | */ | |
268 | public void putAll(Map in) { | |
269 | 0 | if (fast) { |
270 | 0 | synchronized (this) { |
271 | 0 | Map temp = cloneMap(map); |
272 | 0 | temp.putAll(in); |
273 | 0 | map = temp; |
274 | 0 | } |
275 | } else { | |
276 | 0 | synchronized (map) { |
277 | 0 | map.putAll(in); |
278 | 0 | } |
279 | } | |
280 | 0 | } |
281 | ||
282 | /** | |
283 | * Remove any mapping for this key, and return any previously | |
284 | * mapped value. | |
285 | * | |
286 | * @param key the key whose mapping is to be removed | |
287 | * @return the value removed, or null | |
288 | */ | |
289 | public Object remove(Object key) { | |
290 | 3 | if (fast) { |
291 | 3 | synchronized (this) { |
292 | 3 | Map temp = cloneMap(map); |
293 | 3 | Object result = temp.remove(key); |
294 | 3 | map = temp; |
295 | 3 | return (result); |
296 | 0 | } |
297 | } else { | |
298 | 0 | synchronized (map) { |
299 | 0 | return (map.remove(key)); |
300 | 0 | } |
301 | } | |
302 | } | |
303 | ||
304 | /** | |
305 | * Remove all mappings from this map. | |
306 | */ | |
307 | public void clear() { | |
308 | 457 | if (fast) { |
309 | 225 | synchronized (this) { |
310 | 225 | map = createMap(); |
311 | 225 | } |
312 | } else { | |
313 | 232 | synchronized (map) { |
314 | 232 | map.clear(); |
315 | 232 | } |
316 | } | |
317 | 457 | } |
318 | ||
319 | // Basic object methods | |
320 | // ---------------------------------------------------------------------- | |
321 | ||
322 | /** | |
323 | * Compare the specified object with this list for equality. This | |
324 | * implementation uses exactly the code that is used to define the | |
325 | * list equals function in the documentation for the | |
326 | * <code>Map.equals</code> method. | |
327 | * | |
328 | * @param o the object to be compared to this list | |
329 | * @return true if the two maps are equal | |
330 | */ | |
331 | public boolean equals(Object o) { | |
332 | // Simple tests that require no synchronization | |
333 | 0 | if (o == this) { |
334 | 0 | return (true); |
335 | 0 | } else if (!(o instanceof Map)) { |
336 | 0 | return (false); |
337 | } | |
338 | 0 | Map mo = (Map) o; |
339 | ||
340 | // Compare the two maps for equality | |
341 | 0 | if (fast) { |
342 | 0 | if (mo.size() != map.size()) { |
343 | 0 | return (false); |
344 | } | |
345 | 0 | Iterator i = map.entrySet().iterator(); |
346 | 0 | while (i.hasNext()) { |
347 | 0 | Map.Entry e = (Map.Entry) i.next(); |
348 | 0 | Object key = e.getKey(); |
349 | 0 | Object value = e.getValue(); |
350 | 0 | if (value == null) { |
351 | 0 | if (!(mo.get(key) == null && mo.containsKey(key))) { |
352 | 0 | return (false); |
353 | } | |
354 | } else { | |
355 | 0 | if (!value.equals(mo.get(key))) { |
356 | 0 | return (false); |
357 | } | |
358 | } | |
359 | 0 | } |
360 | 0 | return (true); |
361 | ||
362 | } else { | |
363 | 0 | synchronized (map) { |
364 | 0 | if (mo.size() != map.size()) { |
365 | 0 | return (false); |
366 | } | |
367 | 0 | Iterator i = map.entrySet().iterator(); |
368 | 0 | while (i.hasNext()) { |
369 | 0 | Map.Entry e = (Map.Entry) i.next(); |
370 | 0 | Object key = e.getKey(); |
371 | 0 | Object value = e.getValue(); |
372 | 0 | if (value == null) { |
373 | 0 | if (!(mo.get(key) == null && mo.containsKey(key))) { |
374 | 0 | return (false); |
375 | } | |
376 | } else { | |
377 | 0 | if (!value.equals(mo.get(key))) { |
378 | 0 | return (false); |
379 | } | |
380 | } | |
381 | 0 | } |
382 | 0 | return (true); |
383 | 0 | } |
384 | } | |
385 | } | |
386 | ||
387 | /** | |
388 | * Return the hash code value for this map. This implementation uses | |
389 | * exactly the code that is used to define the list hash function in the | |
390 | * documentation for the <code>Map.hashCode</code> method. | |
391 | * | |
392 | * @return suitable integer hash code | |
393 | */ | |
394 | public int hashCode() { | |
395 | 0 | if (fast) { |
396 | 0 | int h = 0; |
397 | 0 | Iterator i = map.entrySet().iterator(); |
398 | 0 | while (i.hasNext()) { |
399 | 0 | h += i.next().hashCode(); |
400 | } | |
401 | 0 | return (h); |
402 | } else { | |
403 | 0 | synchronized (map) { |
404 | 0 | int h = 0; |
405 | 0 | Iterator i = map.entrySet().iterator(); |
406 | 0 | while (i.hasNext()) { |
407 | 0 | h += i.next().hashCode(); |
408 | } | |
409 | 0 | return (h); |
410 | 0 | } |
411 | } | |
412 | } | |
413 | ||
414 | /** | |
415 | * Return a shallow copy of this <code>FastHashMap</code> instance. | |
416 | * The keys and values themselves are not copied. | |
417 | * | |
418 | * @return a clone of this map | |
419 | */ | |
420 | public Object clone() { | |
421 | 0 | WeakFastHashMap results = null; |
422 | 0 | if (fast) { |
423 | 0 | results = new WeakFastHashMap(map); |
424 | } else { | |
425 | 0 | synchronized (map) { |
426 | 0 | results = new WeakFastHashMap(map); |
427 | 0 | } |
428 | } | |
429 | 0 | results.setFast(getFast()); |
430 | 0 | return (results); |
431 | } | |
432 | ||
433 | // Map views | |
434 | // ---------------------------------------------------------------------- | |
435 | ||
436 | /** | |
437 | * Return a collection view of the mappings contained in this map. Each | |
438 | * element in the returned collection is a <code>Map.Entry</code>. | |
439 | * @return the set of map Map entries | |
440 | */ | |
441 | public Set entrySet() { | |
442 | 0 | return new EntrySet(); |
443 | } | |
444 | ||
445 | /** | |
446 | * Return a set view of the keys contained in this map. | |
447 | * @return the set of the Map's keys | |
448 | */ | |
449 | public Set keySet() { | |
450 | 0 | return new KeySet(); |
451 | } | |
452 | ||
453 | /** | |
454 | * Return a collection view of the values contained in this map. | |
455 | * @return the set of the Map's values | |
456 | */ | |
457 | public Collection values() { | |
458 | 0 | return new Values(); |
459 | } | |
460 | ||
461 | // Abstractions on Map creations (for subclasses such as WeakFastHashMap) | |
462 | // ---------------------------------------------------------------------- | |
463 | ||
464 | protected Map createMap() { | |
465 | 852 | return new WeakHashMap(); |
466 | } | |
467 | ||
468 | protected Map createMap(int capacity) { | |
469 | 0 | return new WeakHashMap(capacity); |
470 | } | |
471 | ||
472 | protected Map createMap(int capacity, float factor) { | |
473 | 0 | return new WeakHashMap(capacity, factor); |
474 | } | |
475 | ||
476 | protected Map createMap(Map map) { | |
477 | 9470 | return new WeakHashMap(map); |
478 | } | |
479 | ||
480 | protected Map cloneMap(Map map) { | |
481 | 9470 | return createMap(map); |
482 | } | |
483 | ||
484 | // Map view inner classes | |
485 | // ---------------------------------------------------------------------- | |
486 | ||
487 | /** | |
488 | * Abstract collection implementation shared by keySet(), values() and entrySet(). | |
489 | */ | |
490 | private abstract class CollectionView implements Collection { | |
491 | ||
492 | 0 | public CollectionView() { |
493 | 0 | } |
494 | ||
495 | protected abstract Collection get(Map map); | |
496 | protected abstract Object iteratorNext(Map.Entry entry); | |
497 | ||
498 | ||
499 | public void clear() { | |
500 | 0 | if (fast) { |
501 | 0 | synchronized (WeakFastHashMap.this) { |
502 | 0 | map = createMap(); |
503 | 0 | } |
504 | } else { | |
505 | 0 | synchronized (map) { |
506 | 0 | get(map).clear(); |
507 | 0 | } |
508 | } | |
509 | 0 | } |
510 | ||
511 | public boolean remove(Object o) { | |
512 | 0 | if (fast) { |
513 | 0 | synchronized (WeakFastHashMap.this) { |
514 | 0 | Map temp = cloneMap(map); |
515 | 0 | boolean r = get(temp).remove(o); |
516 | 0 | map = temp; |
517 | 0 | return r; |
518 | 0 | } |
519 | } else { | |
520 | 0 | synchronized (map) { |
521 | 0 | return get(map).remove(o); |
522 | 0 | } |
523 | } | |
524 | } | |
525 | ||
526 | public boolean removeAll(Collection o) { | |
527 | 0 | if (fast) { |
528 | 0 | synchronized (WeakFastHashMap.this) { |
529 | 0 | Map temp = cloneMap(map); |
530 | 0 | boolean r = get(temp).removeAll(o); |
531 | 0 | map = temp; |
532 | 0 | return r; |
533 | 0 | } |
534 | } else { | |
535 | 0 | synchronized (map) { |
536 | 0 | return get(map).removeAll(o); |
537 | 0 | } |
538 | } | |
539 | } | |
540 | ||
541 | public boolean retainAll(Collection o) { | |
542 | 0 | if (fast) { |
543 | 0 | synchronized (WeakFastHashMap.this) { |
544 | 0 | Map temp = cloneMap(map); |
545 | 0 | boolean r = get(temp).retainAll(o); |
546 | 0 | map = temp; |
547 | 0 | return r; |
548 | 0 | } |
549 | } else { | |
550 | 0 | synchronized (map) { |
551 | 0 | return get(map).retainAll(o); |
552 | 0 | } |
553 | } | |
554 | } | |
555 | ||
556 | public int size() { | |
557 | 0 | if (fast) { |
558 | 0 | return get(map).size(); |
559 | } else { | |
560 | 0 | synchronized (map) { |
561 | 0 | return get(map).size(); |
562 | 0 | } |
563 | } | |
564 | } | |
565 | ||
566 | ||
567 | public boolean isEmpty() { | |
568 | 0 | if (fast) { |
569 | 0 | return get(map).isEmpty(); |
570 | } else { | |
571 | 0 | synchronized (map) { |
572 | 0 | return get(map).isEmpty(); |
573 | 0 | } |
574 | } | |
575 | } | |
576 | ||
577 | public boolean contains(Object o) { | |
578 | 0 | if (fast) { |
579 | 0 | return get(map).contains(o); |
580 | } else { | |
581 | 0 | synchronized (map) { |
582 | 0 | return get(map).contains(o); |
583 | 0 | } |
584 | } | |
585 | } | |
586 | ||
587 | public boolean containsAll(Collection o) { | |
588 | 0 | if (fast) { |
589 | 0 | return get(map).containsAll(o); |
590 | } else { | |
591 | 0 | synchronized (map) { |
592 | 0 | return get(map).containsAll(o); |
593 | 0 | } |
594 | } | |
595 | } | |
596 | ||
597 | public Object[] toArray(Object[] o) { | |
598 | 0 | if (fast) { |
599 | 0 | return get(map).toArray(o); |
600 | } else { | |
601 | 0 | synchronized (map) { |
602 | 0 | return get(map).toArray(o); |
603 | 0 | } |
604 | } | |
605 | } | |
606 | ||
607 | public Object[] toArray() { | |
608 | 0 | if (fast) { |
609 | 0 | return get(map).toArray(); |
610 | } else { | |
611 | 0 | synchronized (map) { |
612 | 0 | return get(map).toArray(); |
613 | 0 | } |
614 | } | |
615 | } | |
616 | ||
617 | ||
618 | public boolean equals(Object o) { | |
619 | 0 | if (o == this) { |
620 | 0 | return true; |
621 | } | |
622 | 0 | if (fast) { |
623 | 0 | return get(map).equals(o); |
624 | } else { | |
625 | 0 | synchronized (map) { |
626 | 0 | return get(map).equals(o); |
627 | 0 | } |
628 | } | |
629 | } | |
630 | ||
631 | public int hashCode() { | |
632 | 0 | if (fast) { |
633 | 0 | return get(map).hashCode(); |
634 | } else { | |
635 | 0 | synchronized (map) { |
636 | 0 | return get(map).hashCode(); |
637 | 0 | } |
638 | } | |
639 | } | |
640 | ||
641 | public boolean add(Object o) { | |
642 | 0 | throw new UnsupportedOperationException(); |
643 | } | |
644 | ||
645 | public boolean addAll(Collection c) { | |
646 | 0 | throw new UnsupportedOperationException(); |
647 | } | |
648 | ||
649 | public Iterator iterator() { | |
650 | 0 | return new CollectionViewIterator(); |
651 | } | |
652 | ||
653 | private class CollectionViewIterator implements Iterator { | |
654 | ||
655 | private Map expected; | |
656 | 0 | private Map.Entry lastReturned = null; |
657 | private Iterator iterator; | |
658 | ||
659 | 0 | public CollectionViewIterator() { |
660 | 0 | this.expected = map; |
661 | 0 | this.iterator = expected.entrySet().iterator(); |
662 | 0 | } |
663 | ||
664 | public boolean hasNext() { | |
665 | 0 | if (expected != map) { |
666 | 0 | throw new ConcurrentModificationException(); |
667 | } | |
668 | 0 | return iterator.hasNext(); |
669 | } | |
670 | ||
671 | public Object next() { | |
672 | 0 | if (expected != map) { |
673 | 0 | throw new ConcurrentModificationException(); |
674 | } | |
675 | 0 | lastReturned = (Map.Entry)iterator.next(); |
676 | 0 | return iteratorNext(lastReturned); |
677 | } | |
678 | ||
679 | public void remove() { | |
680 | 0 | if (lastReturned == null) { |
681 | 0 | throw new IllegalStateException(); |
682 | } | |
683 | 0 | if (fast) { |
684 | 0 | synchronized (WeakFastHashMap.this) { |
685 | 0 | if (expected != map) { |
686 | 0 | throw new ConcurrentModificationException(); |
687 | } | |
688 | 0 | WeakFastHashMap.this.remove(lastReturned.getKey()); |
689 | 0 | lastReturned = null; |
690 | 0 | expected = map; |
691 | 0 | } |
692 | } else { | |
693 | 0 | iterator.remove(); |
694 | 0 | lastReturned = null; |
695 | } | |
696 | 0 | } |
697 | } | |
698 | } | |
699 | ||
700 | /** | |
701 | * Set implementation over the keys of the FastHashMap | |
702 | */ | |
703 | 0 | private class KeySet extends CollectionView implements Set { |
704 | ||
705 | protected Collection get(Map map) { | |
706 | 0 | return map.keySet(); |
707 | } | |
708 | ||
709 | protected Object iteratorNext(Map.Entry entry) { | |
710 | 0 | return entry.getKey(); |
711 | } | |
712 | ||
713 | } | |
714 | ||
715 | /** | |
716 | * Collection implementation over the values of the FastHashMap | |
717 | */ | |
718 | 0 | private class Values extends CollectionView { |
719 | ||
720 | protected Collection get(Map map) { | |
721 | 0 | return map.values(); |
722 | } | |
723 | ||
724 | protected Object iteratorNext(Map.Entry entry) { | |
725 | 0 | return entry.getValue(); |
726 | } | |
727 | } | |
728 | ||
729 | /** | |
730 | * Set implementation over the entries of the FastHashMap | |
731 | */ | |
732 | 0 | private class EntrySet extends CollectionView implements Set { |
733 | ||
734 | protected Collection get(Map map) { | |
735 | 0 | return map.entrySet(); |
736 | } | |
737 | ||
738 | protected Object iteratorNext(Map.Entry entry) { | |
739 | 0 | return entry; |
740 | } | |
741 | ||
742 | } | |
743 | ||
744 | } |