• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }