1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * Written by Doug Lea with assistance from members of JCP JSR-166 32 * Expert Group and released to the public domain, as explained at 33 * http://creativecommons.org/publicdomain/zero/1.0/ 34 */ 35 36 package java.util.concurrent; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.function.BiConsumer; 40 import java.util.function.BiFunction; 41 import java.util.function.Function; 42 43 /** 44 * A {@link java.util.Map} providing thread safety and atomicity 45 * guarantees. 46 * 47 * <p>Memory consistency effects: As with other concurrent 48 * collections, actions in a thread prior to placing an object into a 49 * {@code ConcurrentMap} as a key or value 50 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> 51 * actions subsequent to the access or removal of that object from 52 * the {@code ConcurrentMap} in another thread. 53 * 54 * <p>This interface is a member of the 55 * <a href="{@docRoot}/../technotes/guides/collections/index.html"> 56 * Java Collections Framework</a>. 57 * 58 * @since 1.5 59 * @author Doug Lea 60 * @param <K> the type of keys maintained by this map 61 * @param <V> the type of mapped values 62 */ 63 public interface ConcurrentMap<K, V> extends Map<K, V> { 64 65 /** 66 * {@inheritDoc} 67 * 68 * @implNote This implementation assumes that the ConcurrentMap cannot 69 * contain null values and {@code get()} returning null unambiguously means 70 * the key is absent. Implementations which support null values 71 * <strong>must</strong> override this default implementation. 72 * 73 * @throws ClassCastException {@inheritDoc} 74 * @throws NullPointerException {@inheritDoc} 75 * @since 1.8 76 */ 77 @Override 78 default V getOrDefault(Object key, V defaultValue) { 79 V v; 80 return ((v = get(key)) != null) ? v : defaultValue; 81 } 82 83 /** 84 * {@inheritDoc} 85 * 86 * @implSpec The default implementation is equivalent to, for this 87 * {@code map}: 88 * <pre> {@code 89 * for ((Map.Entry<K, V> entry : map.entrySet()) 90 * action.accept(entry.getKey(), entry.getValue()); 91 * }</pre> 92 * 93 * @implNote The default implementation assumes that 94 * {@code IllegalStateException} thrown by {@code getKey()} or 95 * {@code getValue()} indicates that the entry has been removed and cannot 96 * be processed. Operation continues for subsequent entries. 97 * 98 * @throws NullPointerException {@inheritDoc} 99 * @since 1.8 100 */ 101 @Override 102 default void forEach(BiConsumer<? super K, ? super V> action) { 103 Objects.requireNonNull(action); 104 for (Map.Entry<K, V> entry : entrySet()) { 105 K k; 106 V v; 107 try { 108 k = entry.getKey(); 109 v = entry.getValue(); 110 } catch(IllegalStateException ise) { 111 // this usually means the entry is no longer in the map. 112 continue; 113 } 114 action.accept(k, v); 115 } 116 } 117 118 /** 119 * If the specified key is not already associated 120 * with a value, associate it with the given value. 121 * This is equivalent to 122 * <pre> {@code 123 * if (!map.containsKey(key)) 124 * return map.put(key, value); 125 * else 126 * return map.get(key); 127 * }</pre> 128 * 129 * except that the action is performed atomically. 130 * 131 * @implNote This implementation intentionally re-abstracts the 132 * inappropriate default provided in {@code Map}. 133 * 134 * @param key key with which the specified value is to be associated 135 * @param value value to be associated with the specified key 136 * @return the previous value associated with the specified key, or 137 * {@code null} if there was no mapping for the key. 138 * (A {@code null} return can also indicate that the map 139 * previously associated {@code null} with the key, 140 * if the implementation supports null values.) 141 * @throws UnsupportedOperationException if the {@code put} operation 142 * is not supported by this map 143 * @throws ClassCastException if the class of the specified key or value 144 * prevents it from being stored in this map 145 * @throws NullPointerException if the specified key or value is null, 146 * and this map does not permit null keys or values 147 * @throws IllegalArgumentException if some property of the specified key 148 * or value prevents it from being stored in this map 149 */ 150 V putIfAbsent(K key, V value); 151 152 /** 153 * Removes the entry for a key only if currently mapped to a given value. 154 * This is equivalent to 155 * <pre> {@code 156 * if (map.containsKey(key) && Objects.equals(map.get(key), value)) { 157 * map.remove(key); 158 * return true; 159 * } else 160 * return false; 161 * }</pre> 162 * 163 * except that the action is performed atomically. 164 * 165 * @implNote This implementation intentionally re-abstracts the 166 * inappropriate default provided in {@code Map}. 167 * 168 * @param key key with which the specified value is associated 169 * @param value value expected to be associated with the specified key 170 * @return {@code true} if the value was removed 171 * @throws UnsupportedOperationException if the {@code remove} operation 172 * is not supported by this map 173 * @throws ClassCastException if the key or value is of an inappropriate 174 * type for this map 175 * (<a href="../Collection.html#optional-restrictions">optional</a>) 176 * @throws NullPointerException if the specified key or value is null, 177 * and this map does not permit null keys or values 178 * (<a href="../Collection.html#optional-restrictions">optional</a>) 179 */ 180 boolean remove(Object key, Object value); 181 182 /** 183 * Replaces the entry for a key only if currently mapped to a given value. 184 * This is equivalent to 185 * <pre> {@code 186 * if (map.containsKey(key) && Objects.equals(map.get(key), oldValue)) { 187 * map.put(key, newValue); 188 * return true; 189 * } else 190 * return false; 191 * }</pre> 192 * 193 * except that the action is performed atomically. 194 * 195 * @implNote This implementation intentionally re-abstracts the 196 * inappropriate default provided in {@code Map}. 197 * 198 * @param key key with which the specified value is associated 199 * @param oldValue value expected to be associated with the specified key 200 * @param newValue value to be associated with the specified key 201 * @return {@code true} if the value was replaced 202 * @throws UnsupportedOperationException if the {@code put} operation 203 * is not supported by this map 204 * @throws ClassCastException if the class of a specified key or value 205 * prevents it from being stored in this map 206 * @throws NullPointerException if a specified key or value is null, 207 * and this map does not permit null keys or values 208 * @throws IllegalArgumentException if some property of a specified key 209 * or value prevents it from being stored in this map 210 */ 211 boolean replace(K key, V oldValue, V newValue); 212 213 /** 214 * Replaces the entry for a key only if currently mapped to some value. 215 * This is equivalent to 216 * <pre> {@code 217 * if (map.containsKey(key)) { 218 * return map.put(key, value); 219 * } else 220 * return null; 221 * }</pre> 222 * 223 * except that the action is performed atomically. 224 * 225 * @implNote This implementation intentionally re-abstracts the 226 * inappropriate default provided in {@code Map}. 227 * 228 * @param key key with which the specified value is associated 229 * @param value value to be associated with the specified key 230 * @return the previous value associated with the specified key, or 231 * {@code null} if there was no mapping for the key. 232 * (A {@code null} return can also indicate that the map 233 * previously associated {@code null} with the key, 234 * if the implementation supports null values.) 235 * @throws UnsupportedOperationException if the {@code put} operation 236 * is not supported by this map 237 * @throws ClassCastException if the class of the specified key or value 238 * prevents it from being stored in this map 239 * @throws NullPointerException if the specified key or value is null, 240 * and this map does not permit null keys or values 241 * @throws IllegalArgumentException if some property of the specified key 242 * or value prevents it from being stored in this map 243 */ 244 V replace(K key, V value); 245 246 /** 247 * {@inheritDoc} 248 * 249 * @implSpec 250 * <p>The default implementation is equivalent to, for this {@code map}: 251 * <pre> {@code 252 * for ((Map.Entry<K, V> entry : map.entrySet()) 253 * do { 254 * K k = entry.getKey(); 255 * V v = entry.getValue(); 256 * } while(!replace(k, v, function.apply(k, v))); 257 * }</pre> 258 * 259 * The default implementation may retry these steps when multiple 260 * threads attempt updates including potentially calling the function 261 * repeatedly for a given key. 262 * 263 * <p>This implementation assumes that the ConcurrentMap cannot contain null 264 * values and {@code get()} returning null unambiguously means the key is 265 * absent. Implementations which support null values <strong>must</strong> 266 * override this default implementation. 267 * 268 * @throws UnsupportedOperationException {@inheritDoc} 269 * @throws NullPointerException {@inheritDoc} 270 * @throws ClassCastException {@inheritDoc} 271 * @throws IllegalArgumentException {@inheritDoc} 272 * @since 1.8 273 */ 274 @Override 275 default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { 276 Objects.requireNonNull(function); 277 forEach((k,v) -> { 278 while(!replace(k, v, function.apply(k, v))) { 279 // v changed or k is gone 280 if ( (v = get(k)) == null) { 281 // k is no longer in the map. 282 break; 283 } 284 } 285 }); 286 } 287 288 /** 289 * {@inheritDoc} 290 * 291 * @implSpec 292 * The default implementation is equivalent to the following steps for this 293 * {@code map}, then returning the current value or {@code null} if now 294 * absent: 295 * 296 * <pre> {@code 297 * if (map.get(key) == null) { 298 * V newValue = mappingFunction.apply(key); 299 * if (newValue != null) 300 * return map.putIfAbsent(key, newValue); 301 * } 302 * }</pre> 303 * 304 * The default implementation may retry these steps when multiple 305 * threads attempt updates including potentially calling the mapping 306 * function multiple times. 307 * 308 * <p>This implementation assumes that the ConcurrentMap cannot contain null 309 * values and {@code get()} returning null unambiguously means the key is 310 * absent. Implementations which support null values <strong>must</strong> 311 * override this default implementation. 312 * 313 * @throws UnsupportedOperationException {@inheritDoc} 314 * @throws ClassCastException {@inheritDoc} 315 * @throws NullPointerException {@inheritDoc} 316 * @since 1.8 317 */ 318 @Override 319 default V computeIfAbsent(K key, 320 Function<? super K, ? extends V> mappingFunction) { 321 Objects.requireNonNull(mappingFunction); 322 V v, newValue; 323 return ((v = get(key)) == null && 324 (newValue = mappingFunction.apply(key)) != null && 325 (v = putIfAbsent(key, newValue)) == null) ? newValue : v; 326 } 327 328 /** 329 * {@inheritDoc} 330 * 331 * @implSpec 332 * The default implementation is equivalent to performing the following 333 * steps for this {@code map}, then returning the current value or 334 * {@code null} if now absent. : 335 * 336 * <pre> {@code 337 * if (map.get(key) != null) { 338 * V oldValue = map.get(key); 339 * V newValue = remappingFunction.apply(key, oldValue); 340 * if (newValue != null) 341 * map.replace(key, oldValue, newValue); 342 * else 343 * map.remove(key, oldValue); 344 * } 345 * }</pre> 346 * 347 * The default implementation may retry these steps when multiple threads 348 * attempt updates including potentially calling the remapping function 349 * multiple times. 350 * 351 * <p>This implementation assumes that the ConcurrentMap cannot contain null 352 * values and {@code get()} returning null unambiguously means the key is 353 * absent. Implementations which support null values <strong>must</strong> 354 * override this default implementation. 355 * 356 * @throws UnsupportedOperationException {@inheritDoc} 357 * @throws ClassCastException {@inheritDoc} 358 * @throws NullPointerException {@inheritDoc} 359 * @since 1.8 360 */ 361 @Override 362 default V computeIfPresent(K key, 363 BiFunction<? super K, ? super V, ? extends V> remappingFunction) { 364 Objects.requireNonNull(remappingFunction); 365 V oldValue; 366 while((oldValue = get(key)) != null) { 367 V newValue = remappingFunction.apply(key, oldValue); 368 if (newValue != null) { 369 if (replace(key, oldValue, newValue)) 370 return newValue; 371 } else if (remove(key, oldValue)) 372 return null; 373 } 374 return oldValue; 375 } 376 377 /** 378 * {@inheritDoc} 379 * 380 * @implSpec 381 * The default implementation is equivalent to performing the following 382 * steps for this {@code map}, then returning the current value or 383 * {@code null} if absent: 384 * 385 * <pre> {@code 386 * V oldValue = map.get(key); 387 * V newValue = remappingFunction.apply(key, oldValue); 388 * if (oldValue != null ) { 389 * if (newValue != null) 390 * map.replace(key, oldValue, newValue); 391 * else 392 * map.remove(key, oldValue); 393 * } else { 394 * if (newValue != null) 395 * map.putIfAbsent(key, newValue); 396 * else 397 * return null; 398 * } 399 * }</pre> 400 * 401 * The default implementation may retry these steps when multiple 402 * threads attempt updates including potentially calling the remapping 403 * function multiple times. 404 * 405 * <p>This implementation assumes that the ConcurrentMap cannot contain null 406 * values and {@code get()} returning null unambiguously means the key is 407 * absent. Implementations which support null values <strong>must</strong> 408 * override this default implementation. 409 * 410 * @throws UnsupportedOperationException {@inheritDoc} 411 * @throws ClassCastException {@inheritDoc} 412 * @throws NullPointerException {@inheritDoc} 413 * @since 1.8 414 */ 415 @Override 416 default V compute(K key, 417 BiFunction<? super K, ? super V, ? extends V> remappingFunction) { 418 Objects.requireNonNull(remappingFunction); 419 V oldValue = get(key); 420 for(;;) { 421 V newValue = remappingFunction.apply(key, oldValue); 422 if (newValue == null) { 423 // delete mapping 424 if (oldValue != null || containsKey(key)) { 425 // something to remove 426 if (remove(key, oldValue)) { 427 // removed the old value as expected 428 return null; 429 } 430 431 // some other value replaced old value. try again. 432 oldValue = get(key); 433 } else { 434 // nothing to do. Leave things as they were. 435 return null; 436 } 437 } else { 438 // add or replace old mapping 439 if (oldValue != null) { 440 // replace 441 if (replace(key, oldValue, newValue)) { 442 // replaced as expected. 443 return newValue; 444 } 445 446 // some other value replaced old value. try again. 447 oldValue = get(key); 448 } else { 449 // add (replace if oldValue was null) 450 if ((oldValue = putIfAbsent(key, newValue)) == null) { 451 // replaced 452 return newValue; 453 } 454 455 // some other value replaced old value. try again. 456 } 457 } 458 } 459 } 460 461 462 /** 463 * {@inheritDoc} 464 * 465 * @implSpec 466 * The default implementation is equivalent to performing the 467 * following steps for this {@code map}, then returning the 468 * current value or {@code null} if absent: 469 * 470 * <pre> {@code 471 * V oldValue = map.get(key); 472 * V newValue = (oldValue == null) ? value : 473 * remappingFunction.apply(oldValue, value); 474 * if (newValue == null) 475 * map.remove(key); 476 * else if (oldValue == null) 477 * map.remove(key); 478 * else 479 * map.put(key, newValue); 480 * }</pre> 481 * 482 * <p>The default implementation may retry these steps when multiple 483 * threads attempt updates including potentially calling the remapping 484 * function multiple times. 485 * 486 * <p>This implementation assumes that the ConcurrentMap cannot contain null 487 * values and {@code get()} returning null unambiguously means the key is 488 * absent. Implementations which support null values <strong>must</strong> 489 * override this default implementation. 490 * 491 * @throws UnsupportedOperationException {@inheritDoc} 492 * @throws ClassCastException {@inheritDoc} 493 * @throws NullPointerException {@inheritDoc} 494 * @since 1.8 495 */ 496 @Override 497 default V merge(K key, V value, 498 BiFunction<? super V, ? super V, ? extends V> remappingFunction) { 499 Objects.requireNonNull(remappingFunction); 500 Objects.requireNonNull(value); 501 V oldValue = get(key); 502 for (;;) { 503 if (oldValue != null) { 504 V newValue = remappingFunction.apply(oldValue, value); 505 if (newValue != null) { 506 if (replace(key, oldValue, newValue)) 507 return newValue; 508 } else if (remove(key, oldValue)) { 509 return null; 510 } 511 oldValue = get(key); 512 } else { 513 if ((oldValue = putIfAbsent(key, value)) == null) { 514 return value; 515 } 516 } 517 } 518 } 519 }