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