• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, 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 package test.java.util.IdentityHashMap;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.IdentityHashMap;
32 import java.util.Set;
33 import java.util.function.BiPredicate;
34 import java.util.stream.IntStream;
35 import org.testng.annotations.BeforeMethod;
36 import org.testng.annotations.Test;
37 
38 import static java.util.Map.entry;
39 import static org.testng.Assert.*;
40 
41 /*
42  * @test
43  * @bug 8285295 8178355
44  * @summary Basic tests for IdentityHashMap
45  * @run testng Basic
46  */
47 
48 // NOTE: avoid using TestNG's assertEquals/assertNotEquals directly on two IDHM instances,
49 // as its logic for testing collections equality is suspect. Use checkEntries() to assert
50 // that a map's entrySet contains exactly the expected mappings. There are no guarantees about
51 // the identities of Map.Entry instances obtained from the entrySet; however, the keys and
52 // values they contain are guaranteed to have the right identity.
53 
54 // TODO add tests using null keys and values
55 // TODO deeper testing of view collections including iterators, equals, contains, etc.
56 // TODO Map.Entry::setValue
57 
58 public class Basic {
59     /*
60      * Helpers
61      */
62 
Box(int i)63     record Box(int i) {
64         Box(Box other) {
65             this(other.i());
66         }
67     }
68 
69     // Checks that a collection contains exactly the given elements and no others, using the
70     // provided predicate for equivalence. Checking is performed both using contains() on the
71     // collection and by simple array searching. The latter is O(N^2) so is suitable only for
72     // small arrays. No two of the given elements can be equivalent according to the predicate.
73 
74     // TODO: read out the elements using iterator and stream and check them too
75 
76     @SafeVarargs
checkContents(Collection<E> c, BiPredicate<E,E> p, E... given)77     private <E> void checkContents(Collection<E> c, BiPredicate<E,E> p, E... given) {
78         @SuppressWarnings("unchecked")
79         E[] contents = (E[]) c.toArray();
80 
81         assertEquals(c.size(), given.length);
82         assertEquals(contents.length, given.length);
83         final int LEN = given.length;
84 
85         for (E e : given) {
86             assertTrue(c.contains(e));
87         }
88 
89         // Fill indexes array with position of a given element in the contents array,
90         // or -1 if the given element cannot be found.
91 
92         int[] indexes = new int[LEN];
93 
94         outer:
95         for (int i = 0; i < LEN; i++) {
96             for (int j = 0; j < LEN; j++) {
97                 if (p.test(given[i], contents[j])) {
98                     indexes[i] = j;
99                     continue outer;
100                 }
101             }
102             indexes[i] = -1;
103         }
104 
105         // If every given element matches a distinct element in the contents array,
106         // the sorted indexes array will be the sequence [0..LEN-1].
107 
108         Arrays.sort(indexes);
109         assertEquals(indexes, IntStream.range(0, LEN).toArray());
110     }
111 
112     // Checks that the collection contains the given boxes, by identity.
checkElements(Collection<Box> c, Box... given)113     private void checkElements(Collection<Box> c, Box... given) {
114         checkContents(c, (b1, b2) -> b1 == b2, given);
115     }
116 
117     // Checks that the collection contains entries that have identical keys and values.
118     // The entries themselves are not checked for identity.
119     @SafeVarargs
checkEntries(Collection<Map.Entry<Box, Box>> c, Map.Entry<Box, Box>... given)120     private void checkEntries(Collection<Map.Entry<Box, Box>> c, Map.Entry<Box, Box>... given) {
121         checkContents(c, (e1, e2) -> e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue(), given);
122     }
123 
124     /*
125      * Setup
126      */
127 
128     final Box k1a = new Box(17);
129     final Box k1b = new Box(17); // equals but != k1a
130     final Box k2  = new Box(42);
131 
132     final Box v1a = new Box(30);
133     final Box v1b = new Box(30); // equals but != v1a
134     final Box v2  = new Box(99);
135 
136     IdentityHashMap<Box, Box> map;
137     IdentityHashMap<Box, Box> map2;
138 
139     @BeforeMethod
setup()140     public void setup() {
141         map = new IdentityHashMap<>();
142         map.put(k1a, v1a);
143         map.put(k1b, v1b);
144         map.put(k2,  v2);
145 
146         map2 = new IdentityHashMap<>();
147         map2.put(k1a, v1a);
148         map2.put(k1b, v1b);
149         map2.put(k2,  v2);
150     }
151 
152     /*
153      * Tests
154      */
155 
156     // containsKey
157     // containsValue
158     // size
159     @Test
testSizeContainsKeyValue()160     public void testSizeContainsKeyValue() {
161         assertEquals(map.size(), 3);
162 
163         assertTrue(map.containsKey(k1a));
164         assertTrue(map.containsKey(k1b));
165         assertTrue(map.containsKey(k2));
166         assertFalse(map.containsKey(new Box(k1a)));
167 
168         assertTrue(map.containsValue(v1a));
169         assertTrue(map.containsValue(v1b));
170         assertFalse(map.containsValue(new Box(v1a)));
171         assertTrue(map.containsValue(v2));
172     }
173 
174     // get
175     @Test
testGet()176     public void testGet() {
177         assertSame(map.get(k1a), v1a);
178         assertSame(map.get(k1b), v1b);
179         assertSame(map.get(k2), v2);
180         assertNull(map.get(new Box(k1a)));
181     }
182 
183     // getOrDefault
184     @Test
testGetOrDefault()185     public void testGetOrDefault() {
186         Box other = new Box(22);
187 
188         assertSame(map.getOrDefault(k1a, other), v1a);
189         assertSame(map.getOrDefault(k1b, other), v1b);
190         assertSame(map.getOrDefault(new Box(k1a), other), other);
191         assertSame(map.getOrDefault(k2, other), v2);
192     }
193 
194     // clear
195     // isEmpty
196     @Test
testClearEmpty()197     public void testClearEmpty() {
198         assertFalse(map.isEmpty());
199         map.clear();
200         assertTrue(map.isEmpty());
201     }
202 
203     // hashCode
204     @Test
testHashCode()205     public void testHashCode() {
206         int expected = (System.identityHashCode(k1a) ^ System.identityHashCode(v1a)) +
207                        (System.identityHashCode(k1b) ^ System.identityHashCode(v1b)) +
208                        (System.identityHashCode(k2)  ^ System.identityHashCode(v2));
209         assertEquals(map.hashCode(), expected);
210         assertEquals(map.entrySet().hashCode(), expected);
211     }
212 
213     // equals
214     @Test
testEquals()215     public void testEquals() {
216         assertTrue(map.equals(map));
217         assertTrue(map.equals(map2));
218         assertTrue(map2.equals(map));
219 
220         assertTrue(map.keySet().equals(map.keySet()));
221         assertTrue(map.keySet().equals(map2.keySet()));
222         assertTrue(map2.keySet().equals(map.keySet()));
223 
224         assertTrue(map.entrySet().equals(map.entrySet()));
225         assertTrue(map.entrySet().equals(map2.entrySet()));
226         assertTrue(map2.entrySet().equals(map.entrySet()));
227     }
228 
229     // equals
230     @Test
testEqualsDifferentKey()231     public void testEqualsDifferentKey() {
232         map2.remove(k1a);
233         map2.put(new Box(k1a), v1a);
234 
235         assertFalse(map.equals(map2));
236         assertFalse(map2.equals(map));
237 
238         assertFalse(map.keySet().equals(map2.keySet()));
239         assertFalse(map2.keySet().equals(map.keySet()));
240 
241         assertFalse(map.entrySet().equals(map2.entrySet()));
242         assertFalse(map2.entrySet().equals(map.entrySet()));
243     }
244 
245     // equals
246     @Test
testEqualsDifferentValue()247     public void testEqualsDifferentValue() {
248         map2.put(k1a, new Box(v1a));
249 
250         assertFalse(map.equals(map2));
251         assertFalse(map2.equals(map));
252 
253         assertTrue(map.keySet().equals(map2.keySet()));
254         assertTrue(map2.keySet().equals(map.keySet()));
255 
256         assertFalse(map.entrySet().equals(map2.entrySet()));
257         assertFalse(map2.entrySet().equals(map.entrySet()));
258     }
259 
260     // equals
261     @Test
testEqualsNewMapping()262     public void testEqualsNewMapping() {
263         map.put(new Box(k1a), new Box(v1a));
264 
265         assertFalse(map.equals(map2));
266         assertFalse(map2.equals(map));
267 
268         assertFalse(map.keySet().equals(map2.keySet()));
269         assertFalse(map2.keySet().equals(map.keySet()));
270 
271         assertFalse(map.entrySet().equals(map2.entrySet()));
272         assertFalse(map2.entrySet().equals(map.entrySet()));
273     }
274 
275     // equals
276     @Test
testEqualsMissingMapping()277     public void testEqualsMissingMapping() {
278         var tmp = new IdentityHashMap<Box, Box>();
279         tmp.put(k1a, v1a);
280         tmp.put(k1b, v1b);
281 
282         assertFalse(map.equals(tmp));
283         assertFalse(tmp.equals(map));
284 
285         assertFalse(map.keySet().equals(tmp.keySet()));
286         assertFalse(tmp.keySet().equals(map.keySet()));
287 
288         assertFalse(map.entrySet().equals(tmp.entrySet()));
289         assertFalse(tmp.entrySet().equals(map.entrySet()));
290     }
291 
292     // keySet equals, contains
293     @Test
testKeySet()294     public void testKeySet() {
295         Set<Box> keySet = map.keySet();
296 
297         checkElements(keySet, k1a, k1b, k2);
298         assertFalse(keySet.contains(new Box(k1a)));
299         assertTrue(map.keySet().equals(map2.keySet()));
300         assertTrue(map2.keySet().equals(map.keySet()));
301     }
302 
303     // keySet remove
304     @Test
testKeySetNoRemove()305     public void testKeySetNoRemove() {
306         Set<Box> keySet = map.keySet();
307         boolean r = keySet.remove(new Box(k1a));
308 
309         assertFalse(r);
310         checkElements(keySet, k1a, k1b, k2);
311         checkEntries(map.entrySet(), entry(k1a, v1a),
312                                      entry(k1b, v1b),
313                                      entry(k2, v2));
314         assertTrue(map.keySet().equals(map2.keySet()));
315         assertTrue(map2.keySet().equals(map.keySet()));
316     }
317 
318     // keySet remove
319     @Test
testKeySetRemove()320     public void testKeySetRemove() {
321         Set<Box> keySet = map.keySet();
322         boolean r = keySet.remove(k1a);
323 
324         assertTrue(r);
325         checkElements(keySet, k1b, k2);
326         checkEntries(map.entrySet(), entry(k1b, v1b),
327                                      entry(k2, v2));
328         assertFalse(map.keySet().equals(map2.keySet()));
329         assertFalse(map2.keySet().equals(map.keySet()));
330     }
331 
332     // values
333     @Test
testValues()334     public void testValues() {
335         Collection<Box> values = map.values();
336         checkElements(values, v1a, v1b, v2);
337         assertFalse(values.contains(new Box(v1a)));
338     }
339 
340     // values remove
341     @Test
testValuesNoRemove()342     public void testValuesNoRemove() {
343         Collection<Box> values = map.values();
344         boolean r = values.remove(new Box(v1a));
345 
346         assertFalse(r);
347         checkElements(values, v1a, v1b, v2);
348         checkEntries(map.entrySet(), entry(k1a, v1a),
349                                      entry(k1b, v1b),
350                                      entry(k2, v2));
351     }
352 
353     // values remove
354     @Test
testValuesRemove()355     public void testValuesRemove() {
356         Collection<Box> values = map.values();
357         boolean r = values.remove(v1a);
358 
359         assertTrue(r);
360         checkElements(values, v1b, v2);
361         checkEntries(map.entrySet(), entry(k1b, v1b),
362                                      entry(k2, v2));
363     }
364 
365     // entrySet equals, contains
366     @Test
testEntrySet()367     public void testEntrySet() {
368         Set<Map.Entry<Box,Box>> entrySet = map.entrySet();
369 
370         assertFalse(entrySet.contains(entry(new Box(k1a), v1a)));
371         assertFalse(entrySet.contains(entry(k1b, new Box(v1b))));
372         assertFalse(entrySet.contains(entry(new Box(k2), new Box(v2))));
373         assertTrue(map.entrySet().equals(map2.entrySet()));
374         checkEntries(entrySet, entry(k1a, v1a),
375                                entry(k1b, v1b),
376                                entry(k2, v2));
377     }
378 
379     // entrySet remove
380     @Test
testEntrySetNoRemove()381     public void testEntrySetNoRemove() {
382         Set<Map.Entry<Box, Box>> entrySet = map.entrySet();
383         boolean r1 = entrySet.remove(entry(new Box(k1a), v1a));
384         boolean r2 = entrySet.remove(entry(k1a, new Box(v1a)));
385 
386         assertFalse(r1);
387         assertFalse(r2);
388         assertTrue(entrySet.equals(map2.entrySet()));
389         checkEntries(entrySet, entry(k1a, v1a),
390                                entry(k1b, v1b),
391                                entry(k2, v2));
392     }
393 
394     // entrySet remove
395     @Test
testEntrySetRemove()396     public void testEntrySetRemove() {
397         Set<Map.Entry<Box, Box>> entrySet = map.entrySet();
398         boolean r = entrySet.remove(Map.entry(k1a, v1a));
399 
400         assertTrue(r);
401         assertFalse(entrySet.equals(map2.entrySet()));
402         assertFalse(map.entrySet().equals(map2.entrySet()));
403         checkEntries(entrySet, entry(k1b, v1b),
404                                entry(k2, v2));
405         checkEntries(map.entrySet(), entry(k1b, v1b),
406                                      entry(k2, v2));
407     }
408 
409     // put
410     @Test
testPutNew()411     public void testPutNew() {
412         Box newKey = new Box(k1a);
413         Box newVal = new Box(v1a);
414         Box r = map.put(newKey, newVal);
415 
416         assertNull(r);
417         checkEntries(map.entrySet(), entry(k1a, v1a),
418                                      entry(k1b, v1b),
419                                      entry(k2, v2),
420                                      entry(newKey, newVal));
421     }
422 
423     // put
424     @Test
testPutOverwrite()425     public void testPutOverwrite() {
426         Box newVal = new Box(v1a);
427         Box r = map.put(k1a, newVal);
428 
429         assertSame(r, v1a);
430         checkEntries(map.entrySet(), entry(k1a, newVal),
431                                      entry(k1b, v1b),
432                                      entry(k2, v2));
433     }
434 
435     // putAll
436     @Test
testPutAll()437     public void testPutAll() {
438         Box newKey  = new Box(k1a);
439         Box newVal  = new Box(v1a);
440         Box newValB = new Box(v1b);
441         var argMap = new IdentityHashMap<Box, Box>();
442         argMap.put(newKey, newVal); // new entry
443         argMap.put(k1b, newValB);   // will overwrite value
444         map.putAll(argMap);
445 
446         checkEntries(map.entrySet(), entry(k1a, v1a),
447                                      entry(k1b, newValB),
448                                      entry(k2, v2),
449                                      entry(newKey, newVal));
450     }
451 
452     // putIfAbsent
453     @Test
testPutIfAbsentNoop()454     public void testPutIfAbsentNoop() {
455         Box r = map.putIfAbsent(k1a, new Box(v1a)); // no-op
456 
457         assertSame(r, v1a);
458         checkEntries(map.entrySet(), entry(k1a, v1a),
459                                      entry(k1b, v1b),
460                                      entry(k2, v2));
461     }
462 
463     // putIfAbsent
464     @Test
testPutIfAbsentAddsNew()465     public void testPutIfAbsentAddsNew() {
466         Box newKey = new Box(k1a);
467         Box newVal = new Box(v1a);
468         Box r = map.putIfAbsent(newKey, newVal); // adds new entry
469 
470         assertNull(r);
471         checkEntries(map.entrySet(), entry(k1a, v1a),
472                                      entry(k1b, v1b),
473                                      entry(k2, v2),
474                                      entry(newKey, newVal));
475     }
476 
477     // remove(Object)
478     @Test
testRemoveKey()479     public void testRemoveKey() {
480         Box r = map.remove(k1b);
481 
482         assertSame(r, v1b);
483         checkEntries(map.entrySet(), entry(k1a, v1a),
484                                      entry(k2, v2));
485     }
486 
487     // remove(Object, Object) absent key, absent value
488     @Test
testRemoveAA()489     public void testRemoveAA() {
490         Box k1c = new Box(k1a);
491         Box v1c = new Box(v1a);
492         assertFalse(map.remove(k1c, v1c));
493         checkEntries(map.entrySet(),
494                 entry(k1a, v1a),
495                 entry(k1b, v1b),
496                 entry(k2, v2));
497     }
498 
499     // remove(Object, Object) absent key, present value
500     @Test
testRemoveAV()501     public void testRemoveAV() {
502         Box k1c = new Box(k1a);
503         assertFalse(map.remove(k1c, v1a));
504         checkEntries(map.entrySet(),
505                      entry(k1a, v1a),
506                      entry(k1b, v1b),
507                      entry(k2, v2));
508     }
509 
510     // remove(Object, Object) present key, absent value
511     @Test
testRemoveKA()512     public void testRemoveKA() {
513         Box v1c = new Box(v1a);
514         assertFalse(map.remove(k1a, v1c));
515         checkEntries(map.entrySet(),
516                 entry(k1a, v1a),
517                 entry(k1b, v1b),
518                 entry(k2, v2));
519     }
520 
521     // remove(Object, Object) present key, present value
522     @Test
testRemoveKV()523     public void testRemoveKV() {
524         assertTrue(map.remove(k1a, v1a));
525         checkEntries(map.entrySet(),
526                 entry(k1b, v1b),
527                 entry(k2, v2));
528     }
529 
530     // replace(K, V, V) absent key, absent oldValue
531     @Test
testReplaceAA()532     public void testReplaceAA() {
533         Box k1c = new Box(k1a);
534         Box v1c = new Box(v1a);
535         Box newVal = new Box(v2);
536         assertFalse(map.replace(k1c, v1c, newVal));
537         checkEntries(map.entrySet(),
538                 entry(k1a, v1a),
539                 entry(k1b, v1b),
540                 entry(k2, v2));
541     }
542 
543     // replace(K, V, V) absent key, present oldValue
544     @Test
testReplaceAV()545     public void testReplaceAV() {
546         Box k1c = new Box(k1a);
547         Box newVal = new Box(v2);
548         assertFalse(map.replace(k1c, v1a, newVal));
549         checkEntries(map.entrySet(),
550                 entry(k1a, v1a),
551                 entry(k1b, v1b),
552                 entry(k2, v2));
553     }
554 
555     // replace(K, V, V) present key, absent oldValue
556     @Test
testReplaceKA()557     public void testReplaceKA() {
558         Box v1c = new Box(v1a);
559         Box newVal = new Box(v2);
560         assertFalse(map.replace(k1a, v1c, newVal));
561         checkEntries(map.entrySet(),
562                 entry(k1a, v1a),
563                 entry(k1b, v1b),
564                 entry(k2, v2));
565     }
566 
567     // replace(K, V, V) present key, present oldValue
568     @Test
testReplaceKV()569     public void testReplaceKV() {
570         Box newVal = new Box(v2);
571         assertTrue(map.replace(k1a, v1a, newVal));
572         checkEntries(map.entrySet(),
573                 entry(k1a, newVal),
574                 entry(k1b, v1b),
575                 entry(k2, v2));
576     }
577 
578     // AN: key absent, remappingFunction returns null
579     @Test
testComputeAN()580     public void testComputeAN() {
581         Box newKey = new Box(k1a);
582         Box r = map.compute(newKey, (k, v) -> null);
583 
584         assertNull(r);
585         checkEntries(map.entrySet(), entry(k1a, v1a),
586                                      entry(k1b, v1b),
587                                      entry(k2, v2));
588     }
589 
590     // AV: key absent, remappingFunction returns non-null value
591     @Test
testComputeAV()592     public void testComputeAV() {
593         Box newKey = new Box(k1a);
594         Box newVal = new Box(v1a);
595         Box r = map.compute(newKey, (k, v) -> newVal);
596 
597         assertSame(r, newVal);
598         checkEntries(map.entrySet(), entry(k1a, v1a),
599                                      entry(k1b, v1b),
600                                      entry(k2, v2),
601                                      entry(newKey, newVal));
602     }
603 
604     // PN: key present, remappingFunction returns null
605     @Test
testComputePN()606     public void testComputePN() {
607         Box r = map.compute(k1a, (k, v) -> null);
608 
609         assertNull(r);
610         checkEntries(map.entrySet(), entry(k1b, v1b),
611                                      entry(k2, v2));
612     }
613 
614     // PV: key present, remappingFunction returns non-null value
615     @Test
testComputePV()616     public void testComputePV() {
617         Box newVal = new Box(v1a);
618         Box r = map.compute(k1a, (k, v) -> newVal);
619 
620         assertSame(r, newVal);
621         checkEntries(map.entrySet(), entry(k1a, newVal),
622                                      entry(k1b, v1b),
623                                      entry(k2, v2));
624     }
625 
626     // computeIfAbsent
627     @Test
testComputeIfAbsentIsCalled()628     public void testComputeIfAbsentIsCalled() {
629         boolean[] called = new boolean[1];
630         Box newKey = new Box(k1a);
631         Box newVal = new Box(v1a);
632         Box r = map.computeIfAbsent(newKey, k -> { called[0] = true; return newVal; });
633 
634         assertSame(r, newVal);
635         assertTrue(called[0]);
636         checkEntries(map.entrySet(), entry(k1a, v1a),
637                                      entry(k1b, v1b),
638                                      entry(k2, v2),
639                                      entry(newKey, newVal));
640     }
641 
642     // computeIfAbsent
643     @Test
testComputeIfAbsentNotCalled()644     public void testComputeIfAbsentNotCalled() {
645         boolean[] called = new boolean[1];
646         Box r = map.computeIfAbsent(k1a, k -> { called[0] = true; return null; });
647 
648         assertSame(r, v1a);
649         assertFalse(called[0]);
650         checkEntries(map.entrySet(), entry(k1a, v1a),
651                                      entry(k1b, v1b),
652                                      entry(k2, v2));
653     }
654 
655     // computeIfAbsent
656     @Test
testComputeIfAbsentNullReturn()657     public void testComputeIfAbsentNullReturn() {
658         boolean[] called = new boolean[1];
659         Box newKey = new Box(k1a);
660         Box r = map.computeIfAbsent(newKey, k -> { called[0] = true; return null; });
661 
662         assertNull(r);
663         assertTrue(called[0]);
664         checkEntries(map.entrySet(), entry(k1a, v1a),
665                                      entry(k1b, v1b),
666                                      entry(k2, v2));
667     }
668 
669     // computeIfPresent
670     @Test
testComputeIfPresentIsCalled()671     public void testComputeIfPresentIsCalled() {
672         boolean[] called = new boolean[1];
673         Box newVal = new Box(v1a);
674         Box r = map.computeIfPresent(k1a, (k, v) -> { called[0] = true; return newVal; });
675 
676         assertSame(r, newVal);
677         assertTrue(called[0]);
678         checkEntries(map.entrySet(), entry(k1a, newVal),
679                                      entry(k1b, v1b),
680                                      entry(k2, v2));
681     }
682 
683     // computeIfPresent
684     @Test
testComputeIfPresentNotCalled()685     public void testComputeIfPresentNotCalled() {
686         boolean[] called = new boolean[1];
687         Box r = map.computeIfPresent(new Box(k1a), (k, v) -> { called[0] = true; return null; });
688 
689         assertNull(r);
690         assertFalse(called[0]);
691         checkEntries(map.entrySet(), entry(k1a, v1a),
692                                      entry(k1b, v1b),
693                                      entry(k2, v2));
694     }
695 
696     // computeIfPresent
697     @Test
testComputeIfPresentNullReturn()698     public void testComputeIfPresentNullReturn() {
699         boolean[] called = new boolean[1];
700         Box r = map.computeIfPresent(k1a, (k, v) -> { called[0] = true; return null; });
701 
702         assertNull(r);
703         assertTrue(called[0]);
704         checkEntries(map.entrySet(), entry(k1b, v1b),
705                                      entry(k2, v2));
706     }
707 
708     // merge
709     @Test
testMergeAbsent()710     public void testMergeAbsent() {
711         boolean[] called = new boolean[1];
712         Box newKey = new Box(k1a);
713         Box newVal = new Box(v1a);
714         Box r = map.merge(newKey, newVal, (v1, v2) -> { called[0] = true; return newVal; });
715 
716         assertSame(r, newVal);
717         assertFalse(called[0]);
718         checkEntries(map.entrySet(), entry(k1a, v1a),
719                                      entry(k1b, v1b),
720                                      entry(k2, v2),
721                                      entry(newKey, newVal));
722     }
723 
724     // merge
725     @Test
testMergePresent()726     public void testMergePresent() {
727         boolean[] called = new boolean[1];
728         Box val2 = new Box(47);
729         Box[] mergedVal = new Box[1];
730         Box r = map.merge(k1a, val2, (v1, v2) -> {
731             called[0] = true;
732             mergedVal[0] = new Box(v1.i + v2.i);
733             return mergedVal[0];
734         });
735 
736         assertSame(r, mergedVal[0]);
737         assertTrue(called[0]);
738         checkEntries(map.entrySet(), entry(k1a, mergedVal[0]),
739                                      entry(k1b, v1b),
740                                      entry(k2, v2));
741     }
742 
743     // forEach
744     @Test
testForEach()745     public void testForEach() {
746         @SuppressWarnings("unchecked")
747         List<Map.Entry<Box, Box>> entries = new ArrayList<>();
748         map.forEach((k, v) -> entries.add(entry(k, v)));
749         checkEntries(entries, entry(k1a, v1a),
750                               entry(k1b, v1b),
751                               entry(k2, v2));
752     }
753 
754     // replaceAll
755     @Test
testReplaceAll()756     public void testReplaceAll() {
757         List<Map.Entry<Box, Box>> replacements = new ArrayList<>();
758 
759         map.replaceAll((k, v) -> {
760             Box v1 = new Box(v);
761             replacements.add(entry(k, v1));
762             return v1;
763         });
764 
765         @SuppressWarnings("unchecked")
766         var replacementArray = (Map.Entry<Box, Box>[]) replacements.toArray(Map.Entry[]::new);
767         checkEntries(map.entrySet(), replacementArray);
768     }
769 }
770