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