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