1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8010122 8004518 8024331 8024688 27 * @summary Test Map default methods 28 * @author Mike Duigou 29 * @run testng Defaults 30 */ 31 import java.util.AbstractMap; 32 import java.util.AbstractSet; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Collections; 37 import java.util.EnumMap; 38 import java.util.HashMap; 39 import java.util.Hashtable; 40 import java.util.HashSet; 41 import java.util.IdentityHashMap; 42 import java.util.Iterator; 43 import java.util.LinkedHashMap; 44 import java.util.Map; 45 import java.util.TreeMap; 46 import java.util.Set; 47 import java.util.WeakHashMap; 48 import java.util.concurrent.ConcurrentMap; 49 import java.util.concurrent.ConcurrentHashMap; 50 import java.util.concurrent.ConcurrentSkipListMap; 51 import java.util.concurrent.atomic.AtomicBoolean; 52 import java.util.function.BiFunction; 53 import java.util.function.Function; 54 import java.util.function.Supplier; 55 56 import org.testng.annotations.Test; 57 import org.testng.annotations.DataProvider; 58 import static java.util.Objects.requireNonNull; 59 import static org.testng.Assert.fail; 60 import static org.testng.Assert.assertEquals; 61 import static org.testng.Assert.assertTrue; 62 import static org.testng.Assert.assertFalse; 63 import static org.testng.Assert.assertNull; 64 import static org.testng.Assert.assertSame; 65 66 public class Defaults { 67 68 @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull") 69 public void testGetOrDefaultNulls(String description, Map<IntegerEnum, String> map) { 70 assertTrue(map.containsKey(null), description + ": null key absent"); 71 assertNull(map.get(null), description + ": value not null"); 72 assertSame(map.get(null), map.getOrDefault(null, EXTRA_VALUE), description + ": values should match"); 73 } 74 75 @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all") 76 public void testGetOrDefault(String description, Map<IntegerEnum, String> map) { 77 assertTrue(map.containsKey(KEYS[1]), "expected key missing"); 78 assertSame(map.get(KEYS[1]), map.getOrDefault(KEYS[1], EXTRA_VALUE), "values should match"); 79 assertFalse(map.containsKey(EXTRA_KEY), "expected absent key"); 80 assertSame(map.getOrDefault(EXTRA_KEY, EXTRA_VALUE), EXTRA_VALUE, "value not returned as default"); 81 assertNull(map.getOrDefault(EXTRA_KEY, null), "null not returned as default"); 82 } 83 84 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 85 public void testPutIfAbsentNulls(String description, Map<IntegerEnum, String> map) { 86 // null -> null 87 assertTrue(map.containsKey(null), "null key absent"); 88 assertNull(map.get(null), "value not null"); 89 assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null"); 90 // null -> EXTRA_VALUE 91 assertTrue(map.containsKey(null), "null key absent"); 92 assertSame(map.get(null), EXTRA_VALUE, "unexpected value"); 93 assertSame(map.putIfAbsent(null, null), EXTRA_VALUE, "previous not expected value"); 94 assertTrue(map.containsKey(null), "null key absent"); 95 assertSame(map.get(null), EXTRA_VALUE, "unexpected value"); 96 assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value"); 97 // null -> <absent> 98 99 assertFalse(map.containsKey(null), description + ": key present after remove"); 100 assertNull(map.putIfAbsent(null, null), "previous not null"); 101 // null -> null 102 assertTrue(map.containsKey(null), "null key absent"); 103 assertNull(map.get(null), "value not null"); 104 assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null"); 105 assertSame(map.get(null), EXTRA_VALUE, "value not expected"); 106 } 107 108 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 109 public void testPutIfAbsent(String description, Map<IntegerEnum, String> map) { 110 // 1 -> 1 111 assertTrue(map.containsKey(KEYS[1])); 112 Object expected = map.get(KEYS[1]); 113 assertTrue(null == expected || expected == VALUES[1]); 114 assertSame(map.putIfAbsent(KEYS[1], EXTRA_VALUE), expected); 115 assertSame(map.get(KEYS[1]), expected); 116 117 // EXTRA_KEY -> <absent> 118 assertFalse(map.containsKey(EXTRA_KEY)); 119 assertSame(map.putIfAbsent(EXTRA_KEY, EXTRA_VALUE), null); 120 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 121 assertSame(map.putIfAbsent(EXTRA_KEY, VALUES[2]), EXTRA_VALUE); 122 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 123 } 124 125 @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all") 126 public void testForEach(String description, Map<IntegerEnum, String> map) { 127 IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()]; 128 129 map.forEach((k, v) -> { 130 int idx = (null == k) ? 0 : k.ordinal(); // substitute for index. 131 assertNull(EACH_KEY[idx]); 132 EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison. 133 assertSame(v, map.get(k)); 134 }); 135 136 assertEquals(KEYS, EACH_KEY, description); 137 } 138 139 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 140 public static void testReplaceAll(String description, Map<IntegerEnum, String> map) { 141 IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()]; 142 Set<String> EACH_REPLACE = new HashSet<>(map.size()); 143 144 map.replaceAll((k,v) -> { 145 int idx = (null == k) ? 0 : k.ordinal(); // substitute for index. 146 assertNull(EACH_KEY[idx]); 147 EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison. 148 assertSame(v, map.get(k)); 149 String replacement = v + " replaced"; 150 EACH_REPLACE.add(replacement); 151 return replacement; 152 }); 153 154 assertEquals(KEYS, EACH_KEY, description); 155 assertEquals(map.values().size(), EACH_REPLACE.size(), description + EACH_REPLACE); 156 assertTrue(EACH_REPLACE.containsAll(map.values()), description + " : " + EACH_REPLACE + " != " + map.values()); 157 assertTrue(map.values().containsAll(EACH_REPLACE), description + " : " + EACH_REPLACE + " != " + map.values()); 158 } 159 160 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull") 161 public static void testReplaceAllNoNullReplacement(String description, Map<IntegerEnum, String> map) { 162 assertThrows( 163 () -> { map.replaceAll(null); }, 164 NullPointerException.class, 165 description); 166 assertThrows( 167 () -> { map.replaceAll((k,v) -> null); }, 168 NullPointerException.class, 169 description + " should not allow replacement with null value"); 170 } 171 172 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 173 public static void testRemoveNulls(String description, Map<IntegerEnum, String> map) { 174 assertTrue(map.containsKey(null), "null key absent"); 175 assertNull(map.get(null), "value not null"); 176 assertFalse(map.remove(null, EXTRA_VALUE), description); 177 assertTrue(map.containsKey(null)); 178 assertNull(map.get(null)); 179 assertTrue(map.remove(null, null)); 180 assertFalse(map.containsKey(null)); 181 assertNull(map.get(null)); 182 assertFalse(map.remove(null, null)); 183 } 184 185 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 186 public static void testRemove(String description, Map<IntegerEnum, String> map) { 187 assertTrue(map.containsKey(KEYS[1])); 188 Object expected = map.get(KEYS[1]); 189 assertTrue(null == expected || expected == VALUES[1]); 190 assertFalse(map.remove(KEYS[1], EXTRA_VALUE), description); 191 assertSame(map.get(KEYS[1]), expected); 192 assertTrue(map.remove(KEYS[1], expected)); 193 assertNull(map.get(KEYS[1])); 194 assertFalse(map.remove(KEYS[1], expected)); 195 196 assertFalse(map.containsKey(EXTRA_KEY)); 197 assertFalse(map.remove(EXTRA_KEY, EXTRA_VALUE)); 198 } 199 200 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 201 public void testReplaceKVNulls(String description, Map<IntegerEnum, String> map) { 202 assertTrue(map.containsKey(null), "null key absent"); 203 assertNull(map.get(null), "value not null"); 204 assertSame(map.replace(null, EXTRA_VALUE), null); 205 assertSame(map.get(null), EXTRA_VALUE); 206 } 207 208 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull") 209 public void testReplaceKVNoNulls(String description, Map<IntegerEnum, String> map) { 210 assertTrue(map.containsKey(FIRST_KEY), "expected key missing"); 211 assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value"); 212 assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE"); 213 assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value"); 214 assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value"); 215 } 216 217 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 218 public void testReplaceKV(String description, Map<IntegerEnum, String> map) { 219 assertTrue(map.containsKey(KEYS[1])); 220 Object expected = map.get(KEYS[1]); 221 assertTrue(null == expected || expected == VALUES[1]); 222 assertSame(map.replace(KEYS[1], EXTRA_VALUE), expected); 223 assertSame(map.get(KEYS[1]), EXTRA_VALUE); 224 225 assertFalse(map.containsKey(EXTRA_KEY)); 226 assertNull(map.replace(EXTRA_KEY, EXTRA_VALUE)); 227 assertFalse(map.containsKey(EXTRA_KEY)); 228 assertNull(map.get(EXTRA_KEY)); 229 assertNull(map.put(EXTRA_KEY, EXTRA_VALUE)); 230 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 231 assertSame(map.replace(EXTRA_KEY, (String)expected), EXTRA_VALUE); 232 assertSame(map.get(EXTRA_KEY), expected); 233 } 234 235 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 236 public void testReplaceKVVNulls(String description, Map<IntegerEnum, String> map) { 237 assertTrue(map.containsKey(null), "null key absent"); 238 assertNull(map.get(null), "value not null"); 239 assertFalse(map.replace(null, EXTRA_VALUE, EXTRA_VALUE)); 240 assertNull(map.get(null)); 241 assertTrue(map.replace(null, null, EXTRA_VALUE)); 242 assertSame(map.get(null), EXTRA_VALUE); 243 assertTrue(map.replace(null, EXTRA_VALUE, EXTRA_VALUE)); 244 assertSame(map.get(null), EXTRA_VALUE); 245 } 246 247 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull") 248 public void testReplaceKVVNoNulls(String description, Map<IntegerEnum, String> map) { 249 assertTrue(map.containsKey(FIRST_KEY), "expected key missing"); 250 assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value"); 251 assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE"); 252 assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class, description + ": should throw NPE"); 253 assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value"); 254 assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value"); 255 } 256 257 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 258 public void testReplaceKVV(String description, Map<IntegerEnum, String> map) { 259 assertTrue(map.containsKey(KEYS[1])); 260 Object expected = map.get(KEYS[1]); 261 assertTrue(null == expected || expected == VALUES[1]); 262 assertFalse(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE)); 263 assertSame(map.get(KEYS[1]), expected); 264 assertTrue(map.replace(KEYS[1], (String)expected, EXTRA_VALUE)); 265 assertSame(map.get(KEYS[1]), EXTRA_VALUE); 266 assertTrue(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE)); 267 assertSame(map.get(KEYS[1]), EXTRA_VALUE); 268 269 assertFalse(map.containsKey(EXTRA_KEY)); 270 assertFalse(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE)); 271 assertFalse(map.containsKey(EXTRA_KEY)); 272 assertNull(map.get(EXTRA_KEY)); 273 assertNull(map.put(EXTRA_KEY, EXTRA_VALUE)); 274 assertTrue(map.containsKey(EXTRA_KEY)); 275 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 276 assertTrue(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE)); 277 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 278 } 279 280 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 281 public void testComputeIfAbsentNulls(String description, Map<IntegerEnum, String> map) { 282 // null -> null 283 assertTrue(map.containsKey(null), "null key absent"); 284 assertNull(map.get(null), "value not null"); 285 assertSame(map.computeIfAbsent(null, (k) -> null), null, "not expected result"); 286 assertTrue(map.containsKey(null), "null key absent"); 287 assertNull(map.get(null), "value not null"); 288 assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result"); 289 // null -> EXTRA_VALUE 290 assertTrue(map.containsKey(null), "null key absent"); 291 assertSame(map.get(null), EXTRA_VALUE, "not expected value"); 292 assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value"); 293 // null -> <absent> 294 assertFalse(map.containsKey(null), "null key present"); 295 assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result"); 296 // null -> EXTRA_VALUE 297 assertTrue(map.containsKey(null), "null key absent"); 298 assertSame(map.get(null), EXTRA_VALUE, "not expected value"); 299 } 300 301 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 302 public void testComputeIfAbsent(String description, Map<IntegerEnum, String> map) { 303 // 1 -> 1 304 assertTrue(map.containsKey(KEYS[1])); 305 Object expected = map.get(KEYS[1]); 306 assertTrue(null == expected || expected == VALUES[1], description + String.valueOf(expected)); 307 expected = (null == expected) ? EXTRA_VALUE : expected; 308 assertSame(map.computeIfAbsent(KEYS[1], (k) -> EXTRA_VALUE), expected, description); 309 assertSame(map.get(KEYS[1]), expected, description); 310 311 // EXTRA_KEY -> <absent> 312 assertFalse(map.containsKey(EXTRA_KEY)); 313 assertNull(map.computeIfAbsent(EXTRA_KEY, (k) -> null)); 314 assertFalse(map.containsKey(EXTRA_KEY)); 315 assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE); 316 // EXTRA_KEY -> EXTRA_VALUE 317 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 318 } 319 320 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 321 public void testComputeIfAbsentNullFunction(String description, Map<IntegerEnum, String> map) { 322 assertThrows( () -> { map.computeIfAbsent(KEYS[1], null);}, 323 NullPointerException.class, 324 "Should throw NPE"); 325 } 326 327 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 328 public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) { 329 assertTrue(map.containsKey(null), description + ": null key absent"); 330 assertNull(map.get(null), description + ": value not null"); 331 assertSame(map.computeIfPresent(null, (k, v) -> { 332 fail(description + ": null value is not deemed present"); 333 return EXTRA_VALUE; 334 }), null, description); 335 assertTrue(map.containsKey(null)); 336 assertNull(map.get(null), description); 337 assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping"); 338 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 339 assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> { 340 fail(description + ": null value is not deemed present"); 341 return EXTRA_VALUE; 342 }), null, description); 343 assertNull(map.get(EXTRA_KEY), description + ": null mapping gone"); 344 } 345 346 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 347 public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) { 348 assertTrue(map.containsKey(KEYS[1])); 349 Object value = map.get(KEYS[1]); 350 assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); 351 Object expected = (null == value) ? null : EXTRA_VALUE; 352 assertSame(map.computeIfPresent(KEYS[1], (k, v) -> { 353 assertSame(v, value); 354 return EXTRA_VALUE; 355 }), expected, description); 356 assertSame(map.get(KEYS[1]), expected, description); 357 358 assertFalse(map.containsKey(EXTRA_KEY)); 359 assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> { 360 fail(); 361 return EXTRA_VALUE; 362 }), null); 363 assertFalse(map.containsKey(EXTRA_KEY)); 364 assertSame(map.get(EXTRA_KEY), null); 365 } 366 367 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 368 public void testComputeIfPresentNullFunction(String description, Map<IntegerEnum, String> map) { 369 assertThrows( () -> { map.computeIfPresent(KEYS[1], null);}, 370 NullPointerException.class, 371 "Should throw NPE"); 372 } 373 374 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 375 public void testComputeNulls(String description, Map<IntegerEnum, String> map) { 376 assertTrue(map.containsKey(null), "null key absent"); 377 assertNull(map.get(null), "value not null"); 378 assertSame(map.compute(null, (k, v) -> { 379 assertNull(k); 380 assertNull(v); 381 return null; 382 }), null, description); 383 assertFalse(map.containsKey(null), description + ": null key present."); 384 assertSame(map.compute(null, (k, v) -> { 385 assertSame(k, null); 386 assertNull(v); 387 return EXTRA_VALUE; 388 }), EXTRA_VALUE, description); 389 assertTrue(map.containsKey(null)); 390 assertSame(map.get(null), EXTRA_VALUE, description); 391 assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected"); 392 // no mapping before and after 393 assertFalse(map.containsKey(null), description + ": null key present"); 394 assertSame(map.compute(null, (k, v) -> { 395 assertNull(k); 396 assertNull(v); 397 return null; 398 }), null, description + ": expected null result" ); 399 assertFalse(map.containsKey(null), description + ": null key present"); 400 // compute with map not containing value 401 assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping"); 402 assertFalse(map.containsKey(EXTRA_KEY), description + ": key present"); 403 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 404 assertSame(k, EXTRA_KEY); 405 assertNull(v); 406 return null; 407 }), null, description); 408 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 409 // ensure removal. 410 assertNull(map.put(EXTRA_KEY, EXTRA_VALUE)); 411 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 412 assertSame(k, EXTRA_KEY); 413 assertSame(v, EXTRA_VALUE); 414 return null; 415 }), null, description + ": null resulted expected"); 416 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 417 // compute with map containing null value 418 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 419 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 420 assertSame(k, EXTRA_KEY); 421 assertNull(v); 422 return null; 423 }), null, description); 424 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 425 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 426 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 427 assertSame(k, EXTRA_KEY); 428 assertNull(v); 429 return EXTRA_VALUE; 430 }), EXTRA_VALUE, description); 431 assertTrue(map.containsKey(EXTRA_KEY), "null key present"); 432 } 433 434 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 435 public void testCompute(String description, Map<IntegerEnum, String> map) { 436 assertTrue(map.containsKey(KEYS[1])); 437 Object value = map.get(KEYS[1]); 438 assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); 439 assertSame(map.compute(KEYS[1], (k, v) -> { 440 assertSame(k, KEYS[1]); 441 assertSame(v, value); 442 return EXTRA_VALUE; 443 }), EXTRA_VALUE, description); 444 assertSame(map.get(KEYS[1]), EXTRA_VALUE, description); 445 assertNull(map.compute(KEYS[1], (k, v) -> { 446 assertSame(v, EXTRA_VALUE); 447 return null; 448 }), description); 449 assertFalse(map.containsKey(KEYS[1])); 450 451 assertFalse(map.containsKey(EXTRA_KEY)); 452 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 453 assertNull(v); 454 return EXTRA_VALUE; 455 }), EXTRA_VALUE); 456 assertTrue(map.containsKey(EXTRA_KEY)); 457 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 458 } 459 460 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 461 public void testComputeNullFunction(String description, Map<IntegerEnum, String> map) { 462 assertThrows( () -> { map.compute(KEYS[1], null);}, 463 NullPointerException.class, 464 "Should throw NPE"); 465 } 466 467 @Test(dataProvider = "MergeCases") 468 private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) { 469 // add and check initial conditions. 470 switch (oldValue) { 471 case ABSENT : 472 map.remove(EXTRA_KEY); 473 assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); 474 break; 475 case NULL : 476 map.put(EXTRA_KEY, null); 477 assertTrue(map.containsKey(EXTRA_KEY), "key absent"); 478 assertNull(map.get(EXTRA_KEY), "wrong value"); 479 break; 480 case OLDVALUE : 481 map.put(EXTRA_KEY, VALUES[1]); 482 assertTrue(map.containsKey(EXTRA_KEY), "key absent"); 483 assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value"); 484 break; 485 default: 486 fail("unexpected old value"); 487 } 488 489 String returned = map.merge(EXTRA_KEY, 490 newValue == Merging.Value.NULL ? (String) null : VALUES[2], 491 merger 492 ); 493 494 // check result 495 496 switch (result) { 497 case NULL : 498 assertNull(returned, "wrong value"); 499 break; 500 case NEWVALUE : 501 assertSame(returned, VALUES[2], "wrong value"); 502 break; 503 case RESULT : 504 assertSame(returned, VALUES[3], "wrong value"); 505 break; 506 default: 507 fail("unexpected new value"); 508 } 509 510 // check map 511 switch (put) { 512 case ABSENT : 513 assertFalse(map.containsKey(EXTRA_KEY), "key not absent"); 514 break; 515 case NULL : 516 assertTrue(map.containsKey(EXTRA_KEY), "key absent"); 517 assertNull(map.get(EXTRA_KEY), "wrong value"); 518 break; 519 case NEWVALUE : 520 assertTrue(map.containsKey(EXTRA_KEY), "key absent"); 521 assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value"); 522 break; 523 case RESULT : 524 assertTrue(map.containsKey(EXTRA_KEY), "key absent"); 525 assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value"); 526 break; 527 default: 528 fail("unexpected new value"); 529 } 530 } 531 532 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 533 public void testMergeNullMerger(String description, Map<IntegerEnum, String> map) { 534 assertThrows( () -> { map.merge(KEYS[1], VALUES[1], null);}, 535 NullPointerException.class, 536 "Should throw NPE"); 537 } 538 539 /** A function that flipflops between running two other functions. */ 540 static <T,U,V> BiFunction<T,U,V> twoStep(AtomicBoolean b, 541 BiFunction<T,U,V> first, 542 BiFunction<T,U,V> second) { 543 return (t, u) -> { 544 boolean bb = b.get(); 545 try { 546 return (b.get() ? first : second).apply(t, u); 547 } finally { 548 b.set(!bb); 549 }}; 550 } 551 552 /** 553 * Simulates races by modifying the map within the mapping function. 554 */ 555 @Test 556 public void testConcurrentMap_computeIfAbsent_racy() { 557 final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>(); 558 final Long two = 2L; 559 Function<Long,Long> f, g; 560 561 // race not detected if function returns null 562 f = (k) -> { map.put(two, 42L); return null; }; 563 assertNull(map.computeIfAbsent(two, f)); 564 assertEquals(42L, (long)map.get(two)); 565 566 map.clear(); 567 f = (k) -> { map.put(two, 42L); return 86L; }; 568 assertEquals(42L, (long)map.computeIfAbsent(two, f)); 569 assertEquals(42L, (long)map.get(two)); 570 571 // mapping function ignored if value already exists 572 map.put(two, 99L); 573 assertEquals(99L, (long)map.computeIfAbsent(two, f)); 574 assertEquals(99L, (long)map.get(two)); 575 } 576 577 /** 578 * Simulates races by modifying the map within the remapping function. 579 */ 580 @Test 581 public void testConcurrentMap_computeIfPresent_racy() { 582 final AtomicBoolean b = new AtomicBoolean(true); 583 final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>(); 584 final Long two = 2L; 585 BiFunction<Long,Long,Long> f, g; 586 587 for (Long val : new Long[] { null, 86L }) { 588 map.clear(); 589 590 // Function not invoked if no mapping exists 591 f = (k, v) -> { map.put(two, 42L); return val; }; 592 assertNull(map.computeIfPresent(two, f)); 593 assertNull(map.get(two)); 594 595 map.put(two, 42L); 596 f = (k, v) -> { map.put(two, 86L); return val; }; 597 g = (k, v) -> { 598 assertSame(two, k); 599 assertEquals(86L, (long)v); 600 return null; 601 }; 602 assertNull(map.computeIfPresent(two, twoStep(b, f, g))); 603 assertFalse(map.containsKey(two)); 604 assertTrue(b.get()); 605 606 map.put(two, 42L); 607 f = (k, v) -> { map.put(two, 86L); return val; }; 608 g = (k, v) -> { 609 assertSame(two, k); 610 assertEquals(86L, (long)v); 611 return 99L; 612 }; 613 assertEquals(99L, (long)map.computeIfPresent(two, twoStep(b, f, g))); 614 assertTrue(map.containsKey(two)); 615 assertTrue(b.get()); 616 } 617 } 618 619 @Test 620 public void testConcurrentMap_compute_simple() { 621 final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>(); 622 BiFunction<Long,Long,Long> fun = (k, v) -> ((v == null) ? 0L : k + v); 623 assertEquals(Long.valueOf(0L), map.compute(3L, fun)); 624 assertEquals(Long.valueOf(3L), map.compute(3L, fun)); 625 assertEquals(Long.valueOf(6L), map.compute(3L, fun)); 626 assertNull(map.compute(3L, (k, v) -> null)); 627 assertTrue(map.isEmpty()); 628 629 assertEquals(Long.valueOf(0L), map.compute(new Long(3L), fun)); 630 assertEquals(Long.valueOf(3L), map.compute(new Long(3L), fun)); 631 assertEquals(Long.valueOf(6L), map.compute(new Long(3L), fun)); 632 assertNull(map.compute(3L, (k, v) -> null)); 633 assertTrue(map.isEmpty()); 634 } 635 636 /** 637 * Simulates races by modifying the map within the remapping function. 638 */ 639 @Test 640 public void testConcurrentMap_compute_racy() { 641 final AtomicBoolean b = new AtomicBoolean(true); 642 final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>(); 643 final Long two = 2L; 644 BiFunction<Long,Long,Long> f, g; 645 646 // null -> null is a no-op; race not detected 647 f = (k, v) -> { map.put(two, 42L); return null; }; 648 assertNull(map.compute(two, f)); 649 assertEquals(42L, (long)map.get(two)); 650 651 for (Long val : new Long[] { null, 86L }) { 652 map.clear(); 653 654 f = (k, v) -> { map.put(two, 42L); return 86L; }; 655 g = (k, v) -> { 656 assertSame(two, k); 657 assertEquals(42L, (long)v); 658 return k + v; 659 }; 660 assertEquals(44L, (long)map.compute(two, twoStep(b, f, g))); 661 assertEquals(44L, (long)map.get(two)); 662 assertTrue(b.get()); 663 664 f = (k, v) -> { map.remove(two); return val; }; 665 g = (k, v) -> { 666 assertSame(two, k); 667 assertNull(v); 668 return 44L; 669 }; 670 assertEquals(44L, (long)map.compute(two, twoStep(b, f, g))); 671 assertEquals(44L, (long)map.get(two)); 672 assertTrue(map.containsKey(two)); 673 assertTrue(b.get()); 674 675 f = (k, v) -> { map.remove(two); return val; }; 676 g = (k, v) -> { 677 assertSame(two, k); 678 assertNull(v); 679 return null; 680 }; 681 assertNull(map.compute(two, twoStep(b, f, g))); 682 assertNull(map.get(two)); 683 assertFalse(map.containsKey(two)); 684 assertTrue(b.get()); 685 } 686 } 687 688 /** 689 * Simulates races by modifying the map within the remapping function. 690 */ 691 @Test 692 public void testConcurrentMap_merge_racy() { 693 final AtomicBoolean b = new AtomicBoolean(true); 694 final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>(); 695 final Long two = 2L; 696 BiFunction<Long,Long,Long> f, g; 697 698 for (Long val : new Long[] { null, 86L }) { 699 map.clear(); 700 701 f = (v, w) -> { throw new AssertionError(); }; 702 assertEquals(99L, (long)map.merge(two, 99L, f)); 703 assertEquals(99L, (long)map.get(two)); 704 705 f = (v, w) -> { map.put(two, 42L); return val; }; 706 g = (v, w) -> { 707 assertEquals(42L, (long)v); 708 assertEquals(3L, (long)w); 709 return v + w; 710 }; 711 assertEquals(45L, (long)map.merge(two, 3L, twoStep(b, f, g))); 712 assertEquals(45L, (long)map.get(two)); 713 assertTrue(b.get()); 714 715 f = (v, w) -> { map.remove(two); return val; }; 716 g = (k, v) -> { throw new AssertionError(); }; 717 assertEquals(55L, (long)map.merge(two, 55L, twoStep(b, f, g))); 718 assertEquals(55L, (long)map.get(two)); 719 assertTrue(map.containsKey(two)); 720 assertFalse(b.get()); b.set(true); 721 } 722 } 723 724 public enum IntegerEnum { 725 726 e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, 727 e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, 728 e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, 729 e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, 730 e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, 731 e50, e51, e52, e53, e54, e55, e56, e57, e58, e59, 732 e60, e61, e62, e63, e64, e65, e66, e67, e68, e69, 733 e70, e71, e72, e73, e74, e75, e76, e77, e78, e79, 734 e80, e81, e82, e83, e84, e85, e86, e87, e88, e89, 735 e90, e91, e92, e93, e94, e95, e96, e97, e98, e99, 736 EXTRA_KEY; 737 public static final int SIZE = values().length; 738 }; 739 private static final int TEST_SIZE = IntegerEnum.SIZE - 1; 740 /** 741 * Realized keys ensure that there is always a hard ref to all test objects. 742 */ 743 private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE]; 744 /** 745 * Realized values ensure that there is always a hard ref to all test 746 * objects. 747 */ 748 private static final String[] VALUES = new String[TEST_SIZE]; 749 750 static { 751 IntegerEnum[] keys = IntegerEnum.values(); 752 for (int each = 0; each < TEST_SIZE; each++) { 753 KEYS[each] = keys[each]; 754 VALUES[each] = String.valueOf(each); 755 } 756 } 757 758 private static final IntegerEnum FIRST_KEY = KEYS[0]; 759 private static final String FIRST_VALUE = VALUES[0]; 760 private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY; 761 private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE); 762 763 @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true) 764 public static Iterator<Object[]> allMapProvider() { 765 return makeAllMaps().iterator(); 766 } 767 768 @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true) 769 public static Iterator<Object[]> allMapWithNullsProvider() { 770 return makeAllMapsWithNulls().iterator(); 771 } 772 773 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true) 774 public static Iterator<Object[]> rwNonNullMapProvider() { 775 return makeRWNoNullsMaps().iterator(); 776 } 777 778 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true) 779 public static Iterator<Object[]> rwNonNullKeysMapProvider() { 780 return makeRWMapsNoNulls().iterator(); 781 } 782 783 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true) 784 public static Iterator<Object[]> rwMapProvider() { 785 return makeAllRWMaps().iterator(); 786 } 787 788 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true) 789 public static Iterator<Object[]> rwNullsMapProvider() { 790 return makeAllRWMapsWithNulls().iterator(); 791 } 792 793 private static Collection<Object[]> makeAllRWMapsWithNulls() { 794 Collection<Object[]> all = new ArrayList<>(); 795 796 all.addAll(makeRWMaps(true, true)); 797 798 return all; 799 } 800 801 private static Collection<Object[]> makeRWMapsNoNulls() { 802 Collection<Object[]> all = new ArrayList<>(); 803 804 all.addAll(makeRWNoNullKeysMaps(false)); 805 all.addAll(makeRWNoNullsMaps()); 806 807 return all; 808 } 809 810 private static Collection<Object[]> makeAllROMaps() { 811 Collection<Object[]> all = new ArrayList<>(); 812 813 all.addAll(makeROMaps(false)); 814 all.addAll(makeROMaps(true)); 815 816 return all; 817 } 818 819 private static Collection<Object[]> makeAllRWMaps() { 820 Collection<Object[]> all = new ArrayList<>(); 821 822 all.addAll(makeRWNoNullsMaps()); 823 all.addAll(makeRWMaps(false,true)); 824 all.addAll(makeRWMaps(true,true)); 825 all.addAll(makeRWNoNullKeysMaps(true)); 826 return all; 827 } 828 829 private static Collection<Object[]> makeAllMaps() { 830 Collection<Object[]> all = new ArrayList<>(); 831 832 all.addAll(makeAllROMaps()); 833 all.addAll(makeAllRWMaps()); 834 835 return all; 836 } 837 838 private static Collection<Object[]> makeAllMapsWithNulls() { 839 Collection<Object[]> all = new ArrayList<>(); 840 841 all.addAll(makeROMaps(true)); 842 all.addAll(makeRWMaps(true,true)); 843 844 return all; 845 } 846 847 /** 848 * @param nullKeys include null keys 849 * @param nullValues include null values 850 * @return 851 */ 852 private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) { 853 return Arrays.asList( 854 new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)}, 855 new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)}, 856 new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)}, 857 new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)}, 858 new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)}, 859 new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))}, 860 new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)}); 861 } 862 863 /** 864 * @param nulls include null values 865 * @return 866 */ 867 private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) { 868 return Arrays.asList( 869 // null key hostile 870 new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)}, 871 new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)}, 872 new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)}, 873 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))} 874 ); 875 } 876 877 private static Collection<Object[]> makeRWNoNullsMaps() { 878 return Arrays.asList( 879 // null key and value hostile 880 new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)}, 881 new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)}, 882 new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)}, 883 new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))}, 884 new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)}, 885 new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)}, 886 new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)} 887 ); 888 } 889 890 /** 891 * @param nulls include nulls 892 * @return 893 */ 894 private static Collection<Object[]> makeROMaps(boolean nulls) { 895 return Arrays.asList(new Object[][]{ 896 new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))} 897 }); 898 } 899 900 /** 901 * @param supplier a supplier of mutable map instances. 902 * 903 * @param nullKeys include null keys 904 * @param nullValues include null values 905 * @return 906 */ 907 private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) { 908 Map<IntegerEnum, String> result = supplier.get(); 909 910 for (int each = 0; each < TEST_SIZE; each++) { 911 IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each]; 912 String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each]; 913 914 result.put(key, value); 915 } 916 917 return result; 918 } 919 920 static class Merging { 921 public enum Value { 922 ABSENT, 923 NULL, 924 OLDVALUE, 925 NEWVALUE, 926 RESULT 927 } 928 929 public enum Merger implements BiFunction<String,String,String> { 930 UNUSED { 931 public String apply(String oldValue, String newValue) { 932 fail("should not be called"); 933 return null; 934 } 935 }, 936 NULL { 937 public String apply(String oldValue, String newValue) { 938 return null; 939 } 940 }, 941 RESULT { 942 public String apply(String oldValue, String newValue) { 943 return VALUES[3]; 944 } 945 }, 946 } 947 } 948 949 @DataProvider(name = "MergeCases", parallel = true) 950 public Iterator<Object[]> mergeCasesProvider() { 951 Collection<Object[]> cases = new ArrayList<>(); 952 953 cases.addAll(makeMergeTestCases()); 954 955 return cases.iterator(); 956 } 957 958 static Collection<Object[]> makeMergeTestCases() { 959 Collection<Object[]> cases = new ArrayList<>(); 960 961 for (Object[] mapParams : makeAllRWMaps() ) { 962 cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE }); 963 } 964 965 for (Object[] mapParams : makeAllRWMaps() ) { 966 cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL }); 967 } 968 969 for (Object[] mapParams : makeAllRWMaps() ) { 970 cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT }); 971 } 972 973 return cases; 974 } 975 976 public interface Thrower<T extends Throwable> { 977 978 public void run() throws T; 979 } 980 981 public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) { 982 assertThrows(thrower, throwable, null); 983 } 984 985 public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) { 986 Throwable thrown; 987 try { 988 thrower.run(); 989 thrown = null; 990 } catch (Throwable caught) { 991 thrown = caught; 992 } 993 994 assertInstance(thrown, throwable, 995 ((null != message) ? message : "") + 996 " Failed to throw " + throwable.getCanonicalName()); 997 } 998 999 public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) { 1000 for (Thrower<T> thrower : throwers) { 1001 assertThrows(thrower, throwable, message); 1002 } 1003 } 1004 1005 public static void assertInstance(Object actual, Class<?> expected) { 1006 assertInstance(expected.isInstance(actual), null); 1007 } 1008 1009 public static void assertInstance(Object actual, Class<?> expected, String message) { 1010 assertTrue(expected.isInstance(actual), message); 1011 } 1012 1013 /** 1014 * A simple mutable map implementation that provides only default 1015 * implementations of all methods. ie. none of the Map interface default 1016 * methods have overridden implementations. 1017 * 1018 * @param <K> Type of keys 1019 * @param <V> Type of values 1020 */ 1021 public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K,V> { 1022 1023 protected final M map; 1024 1025 public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); } 1026 1027 protected ExtendsAbstractMap(M map) { this.map = map; } 1028 1029 @Override public Set<Map.Entry<K,V>> entrySet() { 1030 return new AbstractSet<Map.Entry<K,V>>() { 1031 @Override public int size() { 1032 return map.size(); 1033 } 1034 1035 @Override public Iterator<Map.Entry<K,V>> iterator() { 1036 final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator(); 1037 return new Iterator<Map.Entry<K,V>>() { 1038 public boolean hasNext() { return source.hasNext(); } 1039 public Map.Entry<K,V> next() { return source.next(); } 1040 public void remove() { source.remove(); } 1041 }; 1042 } 1043 1044 @Override public boolean add(Map.Entry<K,V> e) { 1045 return map.entrySet().add(e); 1046 } 1047 }; 1048 } 1049 1050 @Override public V put(K key, V value) { 1051 return map.put(key, value); 1052 } 1053 } 1054 1055 /** 1056 * A simple mutable concurrent map implementation that provides only default 1057 * implementations of all methods, i.e. none of the ConcurrentMap interface 1058 * default methods have overridden implementations. 1059 * 1060 * @param <K> Type of keys 1061 * @param <V> Type of values 1062 */ 1063 public static class ImplementsConcurrentMap<K,V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> { 1064 public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); } 1065 1066 // ConcurrentMap reabstracts these methods. 1067 // 1068 // Unlike ConcurrentHashMap, we have zero tolerance for null values. 1069 1070 @Override public V replace(K k, V v) { 1071 return map.replace(requireNonNull(k), requireNonNull(v)); 1072 } 1073 1074 @Override public boolean replace(K k, V v, V vv) { 1075 return map.replace(requireNonNull(k), 1076 requireNonNull(v), 1077 requireNonNull(vv)); 1078 } 1079 1080 @Override public boolean remove(Object k, Object v) { 1081 return map.remove(requireNonNull(k), requireNonNull(v)); 1082 } 1083 1084 @Override public V putIfAbsent(K k, V v) { 1085 return map.putIfAbsent(requireNonNull(k), requireNonNull(v)); 1086 } 1087 } 1088 }