1 /*
2  * Written by Doug Lea with assistance from members of JCP JSR-166
3  * Expert Group and released to the public domain, as explained at
4  * http://creativecommons.org/publicdomain/zero/1.0/
5  * Other contributors include Andrew Wright, Jeffrey Hayes,
6  * Pat Fisher, Mike Judd.
7  */
8 
9 package jsr166;
10 
11 import junit.framework.*;
12 import java.util.*;
13 import java.util.concurrent.ConcurrentHashMap;
14 
15 public class ConcurrentHashMapTest extends JSR166TestCase {
16 
17     /**
18      * Returns a new map from Integers 1-5 to Strings "A"-"E".
19      */
map5()20     private static ConcurrentHashMap map5() {
21         ConcurrentHashMap map = new ConcurrentHashMap(5);
22         assertTrue(map.isEmpty());
23         map.put(one, "A");
24         map.put(two, "B");
25         map.put(three, "C");
26         map.put(four, "D");
27         map.put(five, "E");
28         assertFalse(map.isEmpty());
29         assertEquals(5, map.size());
30         return map;
31     }
32 
33     // classes for testing Comparable fallbacks
34     static class BI implements Comparable<BI> {
35         private final int value;
BI(int value)36         BI(int value) { this.value = value; }
compareTo(BI other)37         public int compareTo(BI other) {
38             return Integer.compare(value, other.value);
39         }
equals(Object x)40         public boolean equals(Object x) {
41             return (x instanceof BI) && ((BI)x).value == value;
42         }
hashCode()43         public int hashCode() { return 42; }
44     }
CI(int value)45     static class CI extends BI { CI(int value) { super(value); } }
DI(int value)46     static class DI extends BI { DI(int value) { super(value); } }
47 
48     static class BS implements Comparable<BS> {
49         private final String value;
BS(String value)50         BS(String value) { this.value = value; }
compareTo(BS other)51         public int compareTo(BS other) {
52             return value.compareTo(other.value);
53         }
equals(Object x)54         public boolean equals(Object x) {
55             return (x instanceof BS) && value.equals(((BS)x).value);
56         }
hashCode()57         public int hashCode() { return 42; }
58     }
59 
60     static class LexicographicList<E extends Comparable<E>> extends ArrayList<E>
61         implements Comparable<LexicographicList<E>> {
62         static long total;
63         static long n;
LexicographicList(Collection<E> c)64         LexicographicList(Collection<E> c) { super(c); }
LexicographicList(E e)65         LexicographicList(E e) { super(Collections.singleton(e)); }
compareTo(LexicographicList<E> other)66         public int compareTo(LexicographicList<E> other) {
67             long start = System.currentTimeMillis();
68             int common = Math.min(size(), other.size());
69             int r = 0;
70             for (int i = 0; i < common; i++) {
71                 if ((r = get(i).compareTo(other.get(i))) != 0)
72                     break;
73             }
74             if (r == 0)
75                 r = Integer.compare(size(), other.size());
76             total += System.currentTimeMillis() - start;
77             n++;
78             return r;
79         }
80         private static final long serialVersionUID = 0;
81     }
82 
83     /**
84      * Inserted elements that are subclasses of the same Comparable
85      * class are found.
86      */
testComparableFamily()87     public void testComparableFamily() {
88         ConcurrentHashMap<BI, Boolean> m =
89             new ConcurrentHashMap<BI, Boolean>();
90         for (int i = 0; i < 1000; i++) {
91             assertTrue(m.put(new CI(i), true) == null);
92         }
93         for (int i = 0; i < 1000; i++) {
94             assertTrue(m.containsKey(new CI(i)));
95             assertTrue(m.containsKey(new DI(i)));
96         }
97     }
98 
99     /**
100      * Elements of classes with erased generic type parameters based
101      * on Comparable can be inserted and found.
102      */
testGenericComparable()103      public void testGenericComparable() {
104          ConcurrentHashMap<Object, Boolean> m =
105              new ConcurrentHashMap<Object, Boolean>();
106          for (int i = 0; i < 1000; i++) {
107              BI bi = new BI(i);
108              BS bs = new BS(String.valueOf(i));
109              LexicographicList<BI> bis = new LexicographicList<BI>(bi);
110              LexicographicList<BS> bss = new LexicographicList<BS>(bs);
111              assertTrue(m.putIfAbsent(bis, true) == null);
112              assertTrue(m.containsKey(bis));
113              if (m.putIfAbsent(bss, true) == null)
114                  assertTrue(m.containsKey(bss));
115              assertTrue(m.containsKey(bis));
116          }
117          for (int i = 0; i < 1000; i++) {
118              assertTrue(m.containsKey(new ArrayList(Collections.singleton(new BI(i)))));
119          }
120      }
121 
122     /**
123      * Elements of non-comparable classes equal to those of classes
124      * with erased generic type parameters based on Comparable can be
125      * inserted and found.
126      */
testGenericComparable2()127     public void testGenericComparable2() {
128         ConcurrentHashMap<Object, Boolean> m =
129             new ConcurrentHashMap<Object, Boolean>();
130         for (int i = 0; i < 1000; i++) {
131             m.put(new ArrayList(Collections.singleton(new BI(i))), true);
132         }
133 
134         for (int i = 0; i < 1000; i++) {
135             LexicographicList<BI> bis = new LexicographicList<BI>(new BI(i));
136             assertTrue(m.containsKey(bis));
137         }
138     }
139 
140     /**
141      * clear removes all pairs
142      */
testClear()143     public void testClear() {
144         ConcurrentHashMap map = map5();
145         map.clear();
146         assertEquals(0, map.size());
147     }
148 
149     /**
150      * Maps with same contents are equal
151      */
testEquals()152     public void testEquals() {
153         ConcurrentHashMap map1 = map5();
154         ConcurrentHashMap map2 = map5();
155         assertEquals(map1, map2);
156         assertEquals(map2, map1);
157         map1.clear();
158         assertFalse(map1.equals(map2));
159         assertFalse(map2.equals(map1));
160     }
161 
162     /**
163      * contains returns true for contained value
164      */
testContains()165     public void testContains() {
166         ConcurrentHashMap map = map5();
167         assertTrue(map.contains("A"));
168         assertFalse(map.contains("Z"));
169     }
170 
171     /**
172      * containsKey returns true for contained key
173      */
testContainsKey()174     public void testContainsKey() {
175         ConcurrentHashMap map = map5();
176         assertTrue(map.containsKey(one));
177         assertFalse(map.containsKey(zero));
178     }
179 
180     /**
181      * containsValue returns true for held values
182      */
testContainsValue()183     public void testContainsValue() {
184         ConcurrentHashMap map = map5();
185         assertTrue(map.containsValue("A"));
186         assertFalse(map.containsValue("Z"));
187     }
188 
189     /**
190      * enumeration returns an enumeration containing the correct
191      * elements
192      */
testEnumeration()193     public void testEnumeration() {
194         ConcurrentHashMap map = map5();
195         Enumeration e = map.elements();
196         int count = 0;
197         while (e.hasMoreElements()) {
198             count++;
199             e.nextElement();
200         }
201         assertEquals(5, count);
202     }
203 
204     /**
205      * get returns the correct element at the given key,
206      * or null if not present
207      */
testGet()208     public void testGet() {
209         ConcurrentHashMap map = map5();
210         assertEquals("A", (String)map.get(one));
211         ConcurrentHashMap empty = new ConcurrentHashMap();
212         assertNull(map.get("anything"));
213     }
214 
215     /**
216      * isEmpty is true of empty map and false for non-empty
217      */
testIsEmpty()218     public void testIsEmpty() {
219         ConcurrentHashMap empty = new ConcurrentHashMap();
220         ConcurrentHashMap map = map5();
221         assertTrue(empty.isEmpty());
222         assertFalse(map.isEmpty());
223     }
224 
225     /**
226      * keys returns an enumeration containing all the keys from the map
227      */
testKeys()228     public void testKeys() {
229         ConcurrentHashMap map = map5();
230         Enumeration e = map.keys();
231         int count = 0;
232         while (e.hasMoreElements()) {
233             count++;
234             e.nextElement();
235         }
236         assertEquals(5, count);
237     }
238 
239     /**
240      * keySet returns a Set containing all the keys
241      */
testKeySet()242     public void testKeySet() {
243         ConcurrentHashMap map = map5();
244         Set s = map.keySet();
245         assertEquals(5, s.size());
246         assertTrue(s.contains(one));
247         assertTrue(s.contains(two));
248         assertTrue(s.contains(three));
249         assertTrue(s.contains(four));
250         assertTrue(s.contains(five));
251     }
252 
253     /**
254      * keySet.toArray returns contains all keys
255      */
testKeySetToArray()256     public void testKeySetToArray() {
257         ConcurrentHashMap map = map5();
258         Set s = map.keySet();
259         Object[] ar = s.toArray();
260         assertTrue(s.containsAll(Arrays.asList(ar)));
261         assertEquals(5, ar.length);
262         ar[0] = m10;
263         assertFalse(s.containsAll(Arrays.asList(ar)));
264     }
265 
266     /**
267      * Values.toArray contains all values
268      */
testValuesToArray()269     public void testValuesToArray() {
270         ConcurrentHashMap map = map5();
271         Collection v = map.values();
272         Object[] ar = v.toArray();
273         ArrayList s = new ArrayList(Arrays.asList(ar));
274         assertEquals(5, ar.length);
275         assertTrue(s.contains("A"));
276         assertTrue(s.contains("B"));
277         assertTrue(s.contains("C"));
278         assertTrue(s.contains("D"));
279         assertTrue(s.contains("E"));
280     }
281 
282     /**
283      * entrySet.toArray contains all entries
284      */
testEntrySetToArray()285     public void testEntrySetToArray() {
286         ConcurrentHashMap map = map5();
287         Set s = map.entrySet();
288         Object[] ar = s.toArray();
289         assertEquals(5, ar.length);
290         for (int i = 0; i < 5; ++i) {
291             assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
292             assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
293         }
294     }
295 
296     /**
297      * values collection contains all values
298      */
testValues()299     public void testValues() {
300         ConcurrentHashMap map = map5();
301         Collection s = map.values();
302         assertEquals(5, s.size());
303         assertTrue(s.contains("A"));
304         assertTrue(s.contains("B"));
305         assertTrue(s.contains("C"));
306         assertTrue(s.contains("D"));
307         assertTrue(s.contains("E"));
308     }
309 
310     /**
311      * entrySet contains all pairs
312      */
testEntrySet()313     public void testEntrySet() {
314         ConcurrentHashMap map = map5();
315         Set s = map.entrySet();
316         assertEquals(5, s.size());
317         Iterator it = s.iterator();
318         while (it.hasNext()) {
319             Map.Entry e = (Map.Entry) it.next();
320             assertTrue(
321                        (e.getKey().equals(one) && e.getValue().equals("A")) ||
322                        (e.getKey().equals(two) && e.getValue().equals("B")) ||
323                        (e.getKey().equals(three) && e.getValue().equals("C")) ||
324                        (e.getKey().equals(four) && e.getValue().equals("D")) ||
325                        (e.getKey().equals(five) && e.getValue().equals("E")));
326         }
327     }
328 
329     /**
330      * putAll adds all key-value pairs from the given map
331      */
testPutAll()332     public void testPutAll() {
333         ConcurrentHashMap empty = new ConcurrentHashMap();
334         ConcurrentHashMap map = map5();
335         empty.putAll(map);
336         assertEquals(5, empty.size());
337         assertTrue(empty.containsKey(one));
338         assertTrue(empty.containsKey(two));
339         assertTrue(empty.containsKey(three));
340         assertTrue(empty.containsKey(four));
341         assertTrue(empty.containsKey(five));
342     }
343 
344     /**
345      * putIfAbsent works when the given key is not present
346      */
testPutIfAbsent()347     public void testPutIfAbsent() {
348         ConcurrentHashMap map = map5();
349         map.putIfAbsent(six, "Z");
350         assertTrue(map.containsKey(six));
351     }
352 
353     /**
354      * putIfAbsent does not add the pair if the key is already present
355      */
testPutIfAbsent2()356     public void testPutIfAbsent2() {
357         ConcurrentHashMap map = map5();
358         assertEquals("A", map.putIfAbsent(one, "Z"));
359     }
360 
361     /**
362      * replace fails when the given key is not present
363      */
testReplace()364     public void testReplace() {
365         ConcurrentHashMap map = map5();
366         assertNull(map.replace(six, "Z"));
367         assertFalse(map.containsKey(six));
368     }
369 
370     /**
371      * replace succeeds if the key is already present
372      */
testReplace2()373     public void testReplace2() {
374         ConcurrentHashMap map = map5();
375         assertNotNull(map.replace(one, "Z"));
376         assertEquals("Z", map.get(one));
377     }
378 
379     /**
380      * replace value fails when the given key not mapped to expected value
381      */
testReplaceValue()382     public void testReplaceValue() {
383         ConcurrentHashMap map = map5();
384         assertEquals("A", map.get(one));
385         assertFalse(map.replace(one, "Z", "Z"));
386         assertEquals("A", map.get(one));
387     }
388 
389     /**
390      * replace value succeeds when the given key mapped to expected value
391      */
testReplaceValue2()392     public void testReplaceValue2() {
393         ConcurrentHashMap map = map5();
394         assertEquals("A", map.get(one));
395         assertTrue(map.replace(one, "A", "Z"));
396         assertEquals("Z", map.get(one));
397     }
398 
399     /**
400      * remove removes the correct key-value pair from the map
401      */
testRemove()402     public void testRemove() {
403         ConcurrentHashMap map = map5();
404         map.remove(five);
405         assertEquals(4, map.size());
406         assertFalse(map.containsKey(five));
407     }
408 
409     /**
410      * remove(key,value) removes only if pair present
411      */
testRemove2()412     public void testRemove2() {
413         ConcurrentHashMap map = map5();
414         map.remove(five, "E");
415         assertEquals(4, map.size());
416         assertFalse(map.containsKey(five));
417         map.remove(four, "A");
418         assertEquals(4, map.size());
419         assertTrue(map.containsKey(four));
420     }
421 
422     /**
423      * size returns the correct values
424      */
testSize()425     public void testSize() {
426         ConcurrentHashMap map = map5();
427         ConcurrentHashMap empty = new ConcurrentHashMap();
428         assertEquals(0, empty.size());
429         assertEquals(5, map.size());
430     }
431 
432     /**
433      * toString contains toString of elements
434      */
testToString()435     public void testToString() {
436         ConcurrentHashMap map = map5();
437         String s = map.toString();
438         for (int i = 1; i <= 5; ++i) {
439             assertTrue(s.contains(String.valueOf(i)));
440         }
441     }
442 
443     // Exception tests
444 
445     /**
446      * Cannot create with negative capacity
447      */
testConstructor1()448     public void testConstructor1() {
449         try {
450             new ConcurrentHashMap(-1,0,1);
451             shouldThrow();
452         } catch (IllegalArgumentException success) {}
453     }
454 
455     /**
456      * Cannot create with negative concurrency level
457      */
testConstructor2()458     public void testConstructor2() {
459         try {
460             new ConcurrentHashMap(1,0,-1);
461             shouldThrow();
462         } catch (IllegalArgumentException success) {}
463     }
464 
465     /**
466      * Cannot create with only negative capacity
467      */
testConstructor3()468     public void testConstructor3() {
469         try {
470             new ConcurrentHashMap(-1);
471             shouldThrow();
472         } catch (IllegalArgumentException success) {}
473     }
474 
475     /**
476      * get(null) throws NPE
477      */
testGet_NullPointerException()478     public void testGet_NullPointerException() {
479         try {
480             ConcurrentHashMap c = new ConcurrentHashMap(5);
481             c.get(null);
482             shouldThrow();
483         } catch (NullPointerException success) {}
484     }
485 
486     /**
487      * containsKey(null) throws NPE
488      */
testContainsKey_NullPointerException()489     public void testContainsKey_NullPointerException() {
490         try {
491             ConcurrentHashMap c = new ConcurrentHashMap(5);
492             c.containsKey(null);
493             shouldThrow();
494         } catch (NullPointerException success) {}
495     }
496 
497     /**
498      * containsValue(null) throws NPE
499      */
testContainsValue_NullPointerException()500     public void testContainsValue_NullPointerException() {
501         try {
502             ConcurrentHashMap c = new ConcurrentHashMap(5);
503             c.containsValue(null);
504             shouldThrow();
505         } catch (NullPointerException success) {}
506     }
507 
508     /**
509      * contains(null) throws NPE
510      */
testContains_NullPointerException()511     public void testContains_NullPointerException() {
512         try {
513             ConcurrentHashMap c = new ConcurrentHashMap(5);
514             c.contains(null);
515             shouldThrow();
516         } catch (NullPointerException success) {}
517     }
518 
519     /**
520      * put(null,x) throws NPE
521      */
testPut1_NullPointerException()522     public void testPut1_NullPointerException() {
523         try {
524             ConcurrentHashMap c = new ConcurrentHashMap(5);
525             c.put(null, "whatever");
526             shouldThrow();
527         } catch (NullPointerException success) {}
528     }
529 
530     /**
531      * put(x, null) throws NPE
532      */
testPut2_NullPointerException()533     public void testPut2_NullPointerException() {
534         try {
535             ConcurrentHashMap c = new ConcurrentHashMap(5);
536             c.put("whatever", null);
537             shouldThrow();
538         } catch (NullPointerException success) {}
539     }
540 
541     /**
542      * putIfAbsent(null, x) throws NPE
543      */
testPutIfAbsent1_NullPointerException()544     public void testPutIfAbsent1_NullPointerException() {
545         try {
546             ConcurrentHashMap c = new ConcurrentHashMap(5);
547             c.putIfAbsent(null, "whatever");
548             shouldThrow();
549         } catch (NullPointerException success) {}
550     }
551 
552     /**
553      * replace(null, x) throws NPE
554      */
testReplace_NullPointerException()555     public void testReplace_NullPointerException() {
556         try {
557             ConcurrentHashMap c = new ConcurrentHashMap(5);
558             c.replace(null, "whatever");
559             shouldThrow();
560         } catch (NullPointerException success) {}
561     }
562 
563     /**
564      * replace(null, x, y) throws NPE
565      */
testReplaceValue_NullPointerException()566     public void testReplaceValue_NullPointerException() {
567         try {
568             ConcurrentHashMap c = new ConcurrentHashMap(5);
569             c.replace(null, one, "whatever");
570             shouldThrow();
571         } catch (NullPointerException success) {}
572     }
573 
574     /**
575      * putIfAbsent(x, null) throws NPE
576      */
testPutIfAbsent2_NullPointerException()577     public void testPutIfAbsent2_NullPointerException() {
578         try {
579             ConcurrentHashMap c = new ConcurrentHashMap(5);
580             c.putIfAbsent("whatever", null);
581             shouldThrow();
582         } catch (NullPointerException success) {}
583     }
584 
585     /**
586      * replace(x, null) throws NPE
587      */
testReplace2_NullPointerException()588     public void testReplace2_NullPointerException() {
589         try {
590             ConcurrentHashMap c = new ConcurrentHashMap(5);
591             c.replace("whatever", null);
592             shouldThrow();
593         } catch (NullPointerException success) {}
594     }
595 
596     /**
597      * replace(x, null, y) throws NPE
598      */
testReplaceValue2_NullPointerException()599     public void testReplaceValue2_NullPointerException() {
600         try {
601             ConcurrentHashMap c = new ConcurrentHashMap(5);
602             c.replace("whatever", null, "A");
603             shouldThrow();
604         } catch (NullPointerException success) {}
605     }
606 
607     /**
608      * replace(x, y, null) throws NPE
609      */
testReplaceValue3_NullPointerException()610     public void testReplaceValue3_NullPointerException() {
611         try {
612             ConcurrentHashMap c = new ConcurrentHashMap(5);
613             c.replace("whatever", one, null);
614             shouldThrow();
615         } catch (NullPointerException success) {}
616     }
617 
618     /**
619      * remove(null) throws NPE
620      */
testRemove1_NullPointerException()621     public void testRemove1_NullPointerException() {
622         try {
623             ConcurrentHashMap c = new ConcurrentHashMap(5);
624             c.put("sadsdf", "asdads");
625             c.remove(null);
626             shouldThrow();
627         } catch (NullPointerException success) {}
628     }
629 
630     /**
631      * remove(null, x) throws NPE
632      */
testRemove2_NullPointerException()633     public void testRemove2_NullPointerException() {
634         try {
635             ConcurrentHashMap c = new ConcurrentHashMap(5);
636             c.put("sadsdf", "asdads");
637             c.remove(null, "whatever");
638             shouldThrow();
639         } catch (NullPointerException success) {}
640     }
641 
642     /**
643      * remove(x, null) returns false
644      */
testRemove3()645     public void testRemove3() {
646         ConcurrentHashMap c = new ConcurrentHashMap(5);
647         c.put("sadsdf", "asdads");
648         assertFalse(c.remove("sadsdf", null));
649     }
650 
651     /**
652      * A deserialized map equals original
653      */
testSerialization()654     public void testSerialization() throws Exception {
655         Map x = map5();
656         Map y = serialClone(x);
657 
658         assertNotSame(x, y);
659         assertEquals(x.size(), y.size());
660         assertEquals(x, y);
661         assertEquals(y, x);
662     }
663 
664     /**
665      * SetValue of an EntrySet entry sets value in the map.
666      */
testSetValueWriteThrough()667     public void testSetValueWriteThrough() {
668         // Adapted from a bug report by Eric Zoerner
669         ConcurrentHashMap map = new ConcurrentHashMap(2, 5.0f, 1);
670         assertTrue(map.isEmpty());
671         for (int i = 0; i < 20; i++)
672             map.put(new Integer(i), new Integer(i));
673         assertFalse(map.isEmpty());
674         Map.Entry entry1 = (Map.Entry)map.entrySet().iterator().next();
675         // Unless it happens to be first (in which case remainder of
676         // test is skipped), remove a possibly-colliding key from map
677         // which, under some implementations, may cause entry1 to be
678         // cloned in map
679         if (!entry1.getKey().equals(new Integer(16))) {
680             map.remove(new Integer(16));
681             entry1.setValue("XYZ");
682             assertTrue(map.containsValue("XYZ")); // fails if write-through broken
683         }
684     }
685 
686 }
687