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