ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentMap.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/ConcurrentMap.java (file contents):
Revision 1.45 by jsr166, Tue Jun 18 17:46:16 2013 UTC vs.
Revision 1.46 by dl, Sat Dec 7 12:20:12 2013 UTC

# Line 6 | Line 6
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
# Line 21 | Line 40 | import java.util.function.BiFunction;
40   *
41   * <p>This interface is a member of the
42   * <a href="{@docRoot}/../technotes/guides/collections/index.html">
43 < * Java Collections Framework</a>.
43 > * Java Caollections 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> {
50 > public interface ConcurrentMap<K, V> extends Map<K, V> {
51  
52      /**
53       * {@inheritDoc}
54       *
55 <     * @implNote This implementation assumes that the ConcurrentMap cannot
56 <     * contain null values and get() returning null unambiguously means the key
57 <     * is absent. Implementations which support null values must override this
58 <     * default implementation.
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) {
# Line 44 | Line 66 | public interface ConcurrentMap<K,V> exte
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());
77 +     * }</pre>
78 +     *
79 +     * @implNote The default implementation assumes that {@code
80 +     * IllegalStateException} thrown by {@code getKey()} or {@code
81 +     * getValue()} indicates that the entry no longer exists.
82 +     * Operation continues for subsequent entries.
83 +     *
84 +     * @throws NullPointerException {@inheritDoc}
85 +     * @since 1.8
86 +     */
87 +    @Override
88 +    default void forEach(BiConsumer<? super K, ? super V> action) {
89 +        Objects.requireNonNull(action);
90 +        for (Map.Entry<K, V> entry : entrySet()) {
91 +            K k;
92 +            V v;
93 +            try {
94 +                k = entry.getKey();
95 +                v = entry.getValue();
96 +            } catch(IllegalStateException ise) {
97 +                // this usually means the entry is no longer in the map.
98 +                continue;
99 +            }
100 +            action.accept(k, v);
101 +        }
102 +    }
103 +
104      /**
105       * If the specified key is not already associated
106 <     * with a value, associate it with the given value.
106 >     * with a value, associates it with the given value.
107       * This is equivalent to
108       *  <pre> {@code
109       * if (!map.containsKey(key))
110       *   return map.put(key, value);
111       * else
112 <     *   return map.get(key);}</pre>
112 >     *   return map.get(key);
113 >     * }</pre>
114       *
115       * except that the action is performed atomically.
116       *
117 +     * @implNote There is no default implementation.
118 +     *
119       * @param key key with which the specified value is to be associated
120       * @param value value to be associated with the specified key
121       * @return the previous value associated with the specified key, or
# Line 72 | Line 132 | public interface ConcurrentMap<K,V> exte
132       * @throws IllegalArgumentException if some property of the specified key
133       *         or value prevents it from being stored in this map
134       */
135 <    V putIfAbsent(K key, V value);
135 >     V putIfAbsent(K key, V value);
136  
137      /**
138       * Removes the entry for a key only if currently mapped to a given value.
# Line 82 | Line 142 | public interface ConcurrentMap<K,V> exte
142       *   map.remove(key);
143       *   return true;
144       * } else
145 <     *   return false;}</pre>
145 >     *   return false;
146 >     * }</pre>
147       *
148       * except that the action is performed atomically.
149       *
150 +     * @implNote There is no default implementation.
151 +     *
152       * @param key key with which the specified value is associated
153       * @param value value expected to be associated with the specified key
154       * @return {@code true} if the value was removed
# Line 108 | Line 171 | public interface ConcurrentMap<K,V> exte
171       *   map.put(key, newValue);
172       *   return true;
173       * } else
174 <     *   return false;}</pre>
174 >     *   return false;
175 >     * }</pre>
176       *
177       * except that the action is performed atomically.
178       *
179 +     * @implNote There is no default implementation.
180 +     *
181       * @param key key with which the specified value is associated
182       * @param oldValue value expected to be associated with the specified key
183       * @param newValue value to be associated with the specified key
# Line 134 | Line 200 | public interface ConcurrentMap<K,V> exte
200       * if (map.containsKey(key)) {
201       *   return map.put(key, value);
202       * } else
203 <     *   return null;}</pre>
203 >     *   return null;
204 >     * }</pre>
205       *
206       * except that the action is performed atomically.
207       *
208 +     * @implNote There is no default implementation.
209 +     *
210       * @param key key with which the specified value is associated
211       * @param value value to be associated with the specified key
212       * @return the previous value associated with the specified key, or
# Line 159 | Line 228 | public interface ConcurrentMap<K,V> exte
228      /**
229       * {@inheritDoc}
230       *
231 <     * @implNote This implementation assumes that the ConcurrentMap cannot
232 <     * contain null values and get() returning null unambiguously means the key
233 <     * is absent. Implementations which support null values
234 <     * <strong>must</strong> override this default implementation.
231 >     * @implSpec
232 >     * <p>The default implementation is equivalent to, for this {@code map}:
233 >     * <pre> {@code
234 >     * for ((Map.Entry<K, V> entry : map.entrySet())
235 >     *     do {
236 >     *        K k = entry.getKey();
237 >     *        V v = entry.getValue();
238 >     *     } while(!replace(k, v, function.apply(k, v)));
239 >     * }</pre>
240 >     *
241 >     * The default implementation may retry these steps when multiple
242 >     * threads attempt updates, and may call the function multiple
243 >     * times.
244 >     *
245 >     * @throws UnsupportedOperationException {@inheritDoc}
246 >     * @throws NullPointerException {@inheritDoc}
247 >     * @throws ClassCastException {@inheritDoc}
248 >     * @throws IllegalArgumentException {@inheritDoc}
249 >     * @since 1.8
250       */
251      @Override
252      default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
253 <        if (function == null) throw new NullPointerException();
254 <        for (Map.Entry<K,V> entry : entrySet()) {
255 <            K k; V v;
256 <            try {
257 <                k = entry.getKey();
258 <                v = entry.getValue();
259 <            } catch (IllegalStateException ise) {
260 <                continue; // skip if entry was removed
253 >        Objects.requireNonNull(function);
254 >        forEach((k,v) -> {
255 >            while(!replace(k, v, function.apply(k, v))) {
256 >                // v changed or k is gone
257 >                if ( (v = get(k)) == null) {
258 >                    // k is no longer in the map.
259 >                    break;
260 >                }
261              }
262 +        });
263 +    }
264  
265 <            while (!replace(k, v, function.apply(k, v))) {
266 <                if ((v = get(k)) == null) // give up if k now removed
267 <                    break;
265 >    /**
266 >     * {@inheritDoc}
267 >     *
268 >     * @implSpec
269 >     * The default implementation is equivalent to the following steps for this
270 >     * {@code map}, then returning the current value or {@code null} if now
271 >     * absent:
272 >     *
273 >     * <pre> {@code
274 >     * if (map.get(key) == null) {
275 >     *     V newValue = mappingFunction.apply(key);
276 >     *     if (newValue != null)
277 >     *         return map.putIfAbsent(key, newValue);
278 >     * }
279 >     * }</pre>
280 >     *
281 >     * The default implementation may retry these steps when multiple
282 >     * threads attempt updates, and may call the mapping function
283 >     * multiple times.
284 >     *
285 >     * @throws UnsupportedOperationException {@inheritDoc}
286 >     * @throws ClassCastException {@inheritDoc}
287 >     * @throws NullPointerException {@inheritDoc}
288 >     * @since 1.8
289 >     */
290 >    @Override
291 >    default V computeIfAbsent(K key,
292 >            Function<? super K, ? extends V> mappingFunction) {
293 >        Objects.requireNonNull(mappingFunction);
294 >        V v, newValue;
295 >        return ((v = get(key)) == null &&
296 >                (newValue = mappingFunction.apply(key)) != null &&
297 >                (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
298 >    }
299 >
300 >    /**
301 >     * {@inheritDoc}
302 >     *
303 >     * @implSpec
304 >     * The default implementation is equivalent to performing the following
305 >     * steps for this {@code map}, then returning the current value or
306 >     * {@code null} if now absent. :
307 >     *
308 >     * <pre> {@code
309 >     * if (map.get(key) != null) {
310 >     *     V oldValue = map.get(key);
311 >     *     V newValue = remappingFunction.apply(key, oldValue);
312 >     *     if (newValue != null)
313 >     *         map.replace(key, oldValue, newValue);
314 >     *     else
315 >     *         map.remove(key, oldValue);
316 >     * }
317 >     * }</pre>
318 >     *
319 >     * The default implementation may retry these steps when multiple
320 >     * threads attempt updates, and may call the remapping function
321 >     * multiple times.
322 >     *
323 >     * @throws UnsupportedOperationException {@inheritDoc}
324 >     * @throws ClassCastException {@inheritDoc}
325 >     * @throws NullPointerException {@inheritDoc}
326 >     * @since 1.8
327 >     */
328 >    @Override
329 >    default V computeIfPresent(K key,
330 >            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
331 >        Objects.requireNonNull(remappingFunction);
332 >        V oldValue;
333 >        while((oldValue = get(key)) != null) {
334 >            V newValue = remappingFunction.apply(key, oldValue);
335 >            if (newValue != null) {
336 >                if (replace(key, oldValue, newValue))
337 >                    return newValue;
338 >            } else if (remove(key, oldValue))
339 >               return null;
340 >        }
341 >        return oldValue;
342 >    }
343 >
344 >    /**
345 >     * {@inheritDoc}
346 >     *
347 >     * @implSpec
348 >     * The default implementation is equivalent to performing the following
349 >     * steps for this {@code map}, then returning the current value or
350 >     * {@code null} if absent:
351 >     *
352 >     * <pre> {@code
353 >     * V oldValue = map.get(key);
354 >     * V newValue = remappingFunction.apply(key, oldValue);
355 >     * if (oldValue != null ) {
356 >     *    if (newValue != null)
357 >     *       map.replace(key, oldValue, newValue);
358 >     *    else
359 >     *       map.remove(key, oldValue);
360 >     * } else {
361 >     *    if (newValue != null)
362 >     *       map.putIfAbsent(key, newValue);
363 >     *    else
364 >     *       return null;
365 >     * }
366 >     * }</pre>
367 >     *
368 >     * The default implementation may retry these steps when multiple
369 >     * threads attempt updates, and may call the remapping function
370 >     * multiple times.
371 >     *
372 >     * @throws UnsupportedOperationException {@inheritDoc}
373 >     * @throws ClassCastException {@inheritDoc}
374 >     * @throws NullPointerException {@inheritDoc}
375 >     * @since 1.8
376 >     */
377 >    @Override
378 >    default V compute(K key,
379 >            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
380 >        Objects.requireNonNull(remappingFunction);
381 >        V oldValue = get(key);
382 >        for(;;) {
383 >            V newValue = remappingFunction.apply(key, oldValue);
384 >            if (newValue == null) {
385 >                // delete mapping
386 >                if (oldValue != null || containsKey(key)) {
387 >                    // something to remove
388 >                    if (remove(key, oldValue)) {
389 >                        // removed the old value as expected
390 >                        return null;
391 >                    }
392 >
393 >                    // some other value replaced old value. try again.
394 >                    oldValue = get(key);
395 >                } else {
396 >                    // nothing to do. Leave things as they were.
397 >                    return null;
398 >                }
399 >            } else {
400 >                // add or replace old mapping
401 >                if (oldValue != null) {
402 >                    // replace
403 >                    if (replace(key, oldValue, newValue)) {
404 >                        // replaced as expected.
405 >                        return newValue;
406 >                    }
407 >
408 >                    // some other value replaced old value. try again.
409 >                    oldValue = get(key);
410 >                } else {
411 >                    // add (replace if oldValue was null)
412 >                    if ((oldValue = putIfAbsent(key, newValue)) == null) {
413 >                        // replaced
414 >                        return newValue;
415 >                    }
416 >
417 >                    // some other value replaced old value. try again.
418 >                }
419              }
420          }
421      }
422  
423 +
424 +    /**
425 +     * {@inheritDoc}
426 +     *
427 +     * @implSpec
428 +     * The default implementation is equivalent to performing the following
429 +     * steps for this {@code map}, then returning the current value or
430 +     * {@code null} if absent:
431 +     *
432 +     * <pre> {@code
433 +     * V oldValue = map.get(key);
434 +     * V newValue = (oldValue == null) ? value :
435 +     *              remappingFunction.apply(oldValue, value);
436 +     * if (newValue == null)
437 +     *     map.remove(key);
438 +     * else
439 +     *     map.put(key, newValue);
440 +     * }</pre>
441 +     *
442 +     * The default implementation may retry these steps when multiple
443 +     * threads attempt updates, and may call the remapping function
444 +     * multiple times.
445 +     *
446 +     * @throws UnsupportedOperationException {@inheritDoc}
447 +     * @throws ClassCastException {@inheritDoc}
448 +     * @throws NullPointerException {@inheritDoc}
449 +     * @since 1.8
450 +     */
451 +    @Override
452 +    default V merge(K key, V value,
453 +            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
454 +        Objects.requireNonNull(remappingFunction);
455 +        Objects.requireNonNull(value);
456 +        V oldValue = get(key);
457 +        for (;;) {
458 +            if (oldValue != null) {
459 +                V newValue = remappingFunction.apply(oldValue, value);
460 +                if (newValue != null) {
461 +                    if (replace(key, oldValue, newValue))
462 +                        return newValue;
463 +                } else if (remove(key, oldValue)) {
464 +                    return null;
465 +                }
466 +                oldValue = get(key);
467 +            } else {
468 +                if ((oldValue = putIfAbsent(key, value)) == null) {
469 +                    return value;
470 +                }
471 +            }
472 +        }
473 +    }
474   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines