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 */ 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; 36 BI(int value) { this.value = value; } 37 public int compareTo(BI other) { 38 return Integer.compare(value, other.value); 39 } 40 public boolean equals(Object x) { 41 return (x instanceof BI) && ((BI)x).value == value; 42 } 43 public int hashCode() { return 42; } 44 } 45 static class CI extends BI { CI(int value) { super(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; 50 BS(String value) { this.value = value; } 51 public int compareTo(BS other) { 52 return value.compareTo(other.value); 53 } 54 public boolean equals(Object x) { 55 return (x instanceof BS) && value.equals(((BS)x).value); 56 } 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; 64 LexicographicList(Collection<E> c) { super(c); } 65 LexicographicList(E e) { super(Collections.singleton(e)); } 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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