ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentMap.java
Revision: 1.52
Committed: Mon Dec 9 17:00:23 2013 UTC (10 years, 5 months ago) by jsr166
Branch: MAIN
Changes since 1.51: +20 -24 lines
Log Message:
use the one true code snippet style

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/publicdomain/zero/1.0/
5 */
6
7 package java.util.concurrent;
8 import java.util.Map;
9 import java.util.Objects;
10 import java.util.function.BiConsumer;
11 import java.util.function.BiFunction;
12 import java.util.function.Function;
13
14 /**
15 * A {@link java.util.Map} providing thread safety and atomicity
16 * guarantees.
17 *
18 * <p>To support atomic usages, ConcurrentMaps are expected not to
19 * allow {@code null} as a legal value (and to throw exceptions upon
20 * attempted insertions). This enables a return value of {@code null}
21 * to unambiguously indicate the absence of a mapping. This interface
22 * does not strictly forbid implementations that may hold {@code null}
23 * values. However, in any that do so, a {@code null} value must bear
24 * the same interpretation as the absence of a mapping in order to
25 * conform to method atomicity requirements. Further, any that do so
26 * must override all default method implementations.
27 *
28 * <p>Several methods (for example {@link #putIfAbsent}) inherited
29 * from {@link Map} do not have default implementations, and so must
30 * be provided by implementations of this interface, even though they
31 * have (non-atomic) default implementations in the {@link Map}
32 * interface.
33 *
34 * <p>Memory consistency effects: As with other concurrent
35 * collections, actions in a thread prior to placing an object into a
36 * {@code ConcurrentMap} as a key or value
37 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
38 * actions subsequent to the access or removal of that object from
39 * the {@code ConcurrentMap} in another thread.
40 *
41 * <p>This interface is a member of the
42 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
43 * Java Collections Framework</a>.
44 *
45 * @since 1.5
46 * @author Doug Lea
47 * @param <K> the type of keys maintained by this map
48 * @param <V> the type of mapped values
49 */
50 public interface ConcurrentMap<K,V> extends Map<K,V> {
51
52 /**
53 * {@inheritDoc}
54 *
55 * @implNote The default implementation returns the result of
56 * {@code get(key)} unless {@code null}, in which case it returns
57 * the given defaultValue.
58 *
59 * @throws ClassCastException {@inheritDoc}
60 * @throws NullPointerException {@inheritDoc}
61 * @since 1.8
62 */
63 @Override
64 default V getOrDefault(Object key, V defaultValue) {
65 V v;
66 return ((v = get(key)) != null) ? v : defaultValue;
67 }
68
69 /**
70 * {@inheritDoc}
71 *
72 * @implSpec The default implementation is equivalent to, for this
73 * {@code map}:
74 * <pre> {@code
75 * for (Map.Entry<K,V> entry : map.entrySet())
76 * action.accept(entry.getKey(), entry.getValue());}</pre>
77 *
78 * @implNote The default implementation assumes that {@code
79 * IllegalStateException} thrown by {@code getKey()} or {@code
80 * getValue()} indicates that the entry no longer exists.
81 * Operation continues for subsequent entries.
82 *
83 * @throws NullPointerException {@inheritDoc}
84 * @since 1.8
85 */
86 @Override
87 default void forEach(BiConsumer<? super K, ? super V> action) {
88 Objects.requireNonNull(action);
89 for (Map.Entry<K,V> entry : entrySet()) {
90 K k;
91 V v;
92 try {
93 k = entry.getKey();
94 v = entry.getValue();
95 } catch (IllegalStateException ise) {
96 // this usually means the entry is no longer in the map.
97 continue;
98 }
99 action.accept(k, v);
100 }
101 }
102
103 /**
104 * If the specified key is not already associated
105 * with a value, associates it with the given value.
106 * This is equivalent to, for this {@code map}:
107 * <pre> {@code
108 * if (map.containsKey(key))
109 * return map.get(key);
110 * else
111 * return map.put(key, value);}</pre>
112 *
113 * except that the action is performed atomically.
114 *
115 * @implNote There is no default implementation.
116 *
117 * @param key key with which the specified value is to be associated
118 * @param value value to be associated with the specified key
119 * @return the previous value associated with the specified key, or
120 * {@code null} if there was no mapping for the key.
121 * (A {@code null} return can also indicate that the map
122 * previously associated {@code null} with the key,
123 * if the implementation supports null values.)
124 * @throws UnsupportedOperationException if the {@code put} operation
125 * is not supported by this map
126 * @throws ClassCastException if the class of the specified key or value
127 * prevents it from being stored in this map
128 * @throws NullPointerException if the specified key or value is null,
129 * and this map does not permit null keys or values
130 * @throws IllegalArgumentException if some property of the specified key
131 * or value prevents it from being stored in this map
132 */
133 V putIfAbsent(K key, V value);
134
135 /**
136 * Removes the entry for a key only if currently mapped to a given value.
137 * This is equivalent to, for this {@code map}:
138 * <pre> {@code
139 * if (map.containsKey(key)
140 * && Objects.equals(map.get(key), value)) {
141 * map.remove(key);
142 * return true;
143 * } else
144 * return false;}</pre>
145 *
146 * except that the action is performed atomically.
147 *
148 * @implNote There is no default implementation.
149 *
150 * @param key key with which the specified value is associated
151 * @param value value expected to be associated with the specified key
152 * @return {@code true} if the value was removed
153 * @throws UnsupportedOperationException if the {@code remove} operation
154 * is not supported by this map
155 * @throws ClassCastException if the key or value is of an inappropriate
156 * type for this map
157 * (<a href="../Collection.html#optional-restrictions">optional</a>)
158 * @throws NullPointerException if the specified key or value is null,
159 * and this map does not permit null keys or values
160 * (<a href="../Collection.html#optional-restrictions">optional</a>)
161 */
162 boolean remove(Object key, Object value);
163
164 /**
165 * Replaces the entry for a key only if currently mapped to a given value.
166 * This is equivalent to, for this {@code map}:
167 * <pre> {@code
168 * if (map.containsKey(key)
169 * && Objects.equals(map.get(key), oldValue)) {
170 * map.put(key, newValue);
171 * return true;
172 * } else
173 * return false;}</pre>
174 *
175 * except that the action is performed atomically.
176 *
177 * @implNote There is no default implementation.
178 *
179 * @param key key with which the specified value is associated
180 * @param oldValue value expected to be associated with the specified key
181 * @param newValue value to be associated with the specified key
182 * @return {@code true} if the value was replaced
183 * @throws UnsupportedOperationException if the {@code put} operation
184 * is not supported by this map
185 * @throws ClassCastException if the class of a specified key or value
186 * prevents it from being stored in this map
187 * @throws NullPointerException if a specified key or value is null,
188 * and this map does not permit null keys or values
189 * @throws IllegalArgumentException if some property of a specified key
190 * or value prevents it from being stored in this map
191 */
192 boolean replace(K key, V oldValue, V newValue);
193
194 /**
195 * Replaces the entry for a key only if currently mapped to some value.
196 * This is equivalent to, for this {@code map}:
197 * <pre> {@code
198 * if (map.containsKey(key))
199 * return map.put(key, value);
200 * else
201 * return null;}</pre>
202 *
203 * except that the action is performed atomically.
204 *
205 * @implNote There is no default implementation.
206 *
207 * @param key key with which the specified value is associated
208 * @param value value to be associated with the specified key
209 * @return the previous value associated with the specified key, or
210 * {@code null} if there was no mapping for the key.
211 * (A {@code null} return can also indicate that the map
212 * previously associated {@code null} with the key,
213 * if the implementation supports null values.)
214 * @throws UnsupportedOperationException if the {@code put} operation
215 * is not supported by this map
216 * @throws ClassCastException if the class of the specified key or value
217 * prevents it from being stored in this map
218 * @throws NullPointerException if the specified key or value is null,
219 * and this map does not permit null keys or values
220 * @throws IllegalArgumentException if some property of the specified key
221 * or value prevents it from being stored in this map
222 */
223 V replace(K key, V value);
224
225 /**
226 * {@inheritDoc}
227 *
228 * @implSpec
229 * <p>The default implementation is equivalent to, for this {@code map}:
230 * <pre> {@code
231 * for (Map.Entry<K,V> entry : map.entrySet()) {
232 * K k;
233 * V v;
234 * do {
235 * k = entry.getKey();
236 * v = entry.getValue();
237 * } while (!map.replace(k, v, function.apply(k, v)));
238 * }}</pre>
239 *
240 * The default implementation may retry these steps when multiple
241 * threads attempt updates, and may call the function multiple
242 * times.
243 *
244 * @throws UnsupportedOperationException {@inheritDoc}
245 * @throws NullPointerException {@inheritDoc}
246 * @throws ClassCastException {@inheritDoc}
247 * @throws IllegalArgumentException {@inheritDoc}
248 * @since 1.8
249 */
250 @Override
251 default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
252 Objects.requireNonNull(function);
253 forEach((k,v) -> {
254 while (!replace(k, v, function.apply(k, v))) {
255 // v changed or k is gone
256 if ( (v = get(k)) == null) {
257 // k is no longer in the map.
258 break;
259 }
260 }
261 });
262 }
263
264 /**
265 * {@inheritDoc}
266 *
267 * @implSpec
268 * The default implementation is equivalent to the following steps for this
269 * {@code map}, then returning the current value or {@code null} if now
270 * absent:
271 *
272 * <pre> {@code
273 * if (map.get(key) == null) {
274 * V newValue = mappingFunction.apply(key);
275 * if (newValue != null)
276 * return map.putIfAbsent(key, newValue);
277 * }}</pre>
278 *
279 * The default implementation may retry these steps when multiple
280 * threads attempt updates, and may call the mapping function
281 * multiple times.
282 *
283 * @throws UnsupportedOperationException {@inheritDoc}
284 * @throws ClassCastException {@inheritDoc}
285 * @throws NullPointerException {@inheritDoc}
286 * @since 1.8
287 */
288 @Override
289 default V computeIfAbsent(K key,
290 Function<? super K, ? extends V> mappingFunction) {
291 Objects.requireNonNull(mappingFunction);
292 V v, newValue;
293 return ((v = get(key)) == null &&
294 (newValue = mappingFunction.apply(key)) != null &&
295 (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
296 }
297
298 /**
299 * {@inheritDoc}
300 *
301 * @implSpec
302 * The default implementation is equivalent to performing the following
303 * steps for this {@code map}, then returning the current value or
304 * {@code null} if now absent:
305 *
306 * <pre> {@code
307 * if (map.get(key) != null) {
308 * V oldValue = map.get(key);
309 * V newValue = remappingFunction.apply(key, oldValue);
310 * if (newValue != null)
311 * map.replace(key, oldValue, newValue);
312 * else
313 * map.remove(key, oldValue);
314 * }}</pre>
315 *
316 * The default implementation may retry these steps when multiple
317 * threads attempt updates, and may call the remapping function
318 * multiple times.
319 *
320 * @throws UnsupportedOperationException {@inheritDoc}
321 * @throws ClassCastException {@inheritDoc}
322 * @throws NullPointerException {@inheritDoc}
323 * @since 1.8
324 */
325 @Override
326 default V computeIfPresent(K key,
327 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
328 Objects.requireNonNull(remappingFunction);
329 V oldValue;
330 while ((oldValue = get(key)) != null) {
331 V newValue = remappingFunction.apply(key, oldValue);
332 if (newValue != null) {
333 if (replace(key, oldValue, newValue))
334 return newValue;
335 } else if (remove(key, oldValue))
336 return null;
337 }
338 return oldValue;
339 }
340
341 /**
342 * {@inheritDoc}
343 *
344 * @implSpec
345 * The default implementation is equivalent to performing the following
346 * steps for this {@code map}, then returning the current value or
347 * {@code null} if absent:
348 *
349 * <pre> {@code
350 * V oldValue = map.get(key);
351 * V newValue = remappingFunction.apply(key, oldValue);
352 * if (oldValue != null ) {
353 * if (newValue != null)
354 * map.replace(key, oldValue, newValue);
355 * else
356 * map.remove(key, oldValue);
357 * } else {
358 * if (newValue != null)
359 * map.putIfAbsent(key, newValue);
360 * else
361 * return null;
362 * }}</pre>
363 *
364 * The default implementation may retry these steps when multiple
365 * threads attempt updates, and may call the remapping function
366 * multiple times.
367 *
368 * @throws UnsupportedOperationException {@inheritDoc}
369 * @throws ClassCastException {@inheritDoc}
370 * @throws NullPointerException {@inheritDoc}
371 * @since 1.8
372 */
373 @Override
374 default V compute(K key,
375 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
376 Objects.requireNonNull(remappingFunction);
377 V oldValue = get(key);
378 for (;;) {
379 V newValue = remappingFunction.apply(key, oldValue);
380 if (newValue == null) {
381 // delete mapping
382 if (oldValue != null || containsKey(key)) {
383 // something to remove
384 if (remove(key, oldValue)) {
385 // removed the old value as expected
386 return null;
387 }
388
389 // some other value replaced old value. try again.
390 oldValue = get(key);
391 } else {
392 // nothing to do. Leave things as they were.
393 return null;
394 }
395 } else {
396 // add or replace old mapping
397 if (oldValue != null) {
398 // replace
399 if (replace(key, oldValue, newValue)) {
400 // replaced as expected.
401 return newValue;
402 }
403
404 // some other value replaced old value. try again.
405 oldValue = get(key);
406 } else {
407 // add (replace if oldValue was null)
408 if ((oldValue = putIfAbsent(key, newValue)) == null) {
409 // replaced
410 return newValue;
411 }
412
413 // some other value replaced old value. try again.
414 }
415 }
416 }
417 }
418
419 /**
420 * {@inheritDoc}
421 *
422 * @implSpec
423 * The default implementation is equivalent to performing the following
424 * steps for this {@code map}, then returning the current value or
425 * {@code null} if absent:
426 *
427 * <pre> {@code
428 * V oldValue = map.get(key);
429 * V newValue = (oldValue == null) ? value :
430 * remappingFunction.apply(oldValue, value);
431 * if (newValue == null)
432 * map.remove(key);
433 * else
434 * map.put(key, newValue);}</pre>
435 *
436 * The default implementation may retry these steps when multiple
437 * threads attempt updates, and may call the remapping function
438 * multiple times.
439 *
440 * @throws UnsupportedOperationException {@inheritDoc}
441 * @throws ClassCastException {@inheritDoc}
442 * @throws NullPointerException {@inheritDoc}
443 * @since 1.8
444 */
445 @Override
446 default V merge(K key, V value,
447 BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
448 Objects.requireNonNull(remappingFunction);
449 Objects.requireNonNull(value);
450 V oldValue = get(key);
451 for (;;) {
452 if (oldValue != null) {
453 V newValue = remappingFunction.apply(oldValue, value);
454 if (newValue != null) {
455 if (replace(key, oldValue, newValue))
456 return newValue;
457 } else if (remove(key, oldValue)) {
458 return null;
459 }
460 oldValue = get(key);
461 } else {
462 if ((oldValue = putIfAbsent(key, value)) == null) {
463 return value;
464 }
465 }
466 }
467 }
468 }