ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Map/Defaults.java
Revision: 1.3
Committed: Mon Dec 14 21:15:46 2015 UTC (8 years, 5 months ago) by jsr166
Branch: MAIN
Changes since 1.2: +211 -11 lines
Log Message:
improve Map default method implementations, docs and tests

File Contents

# Content
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 }