1 10 11 package org.objectweb.jac.util; 12 13 import java.lang.ref.ReferenceQueue ; 14 import java.lang.ref.WeakReference ; 15 import java.util.AbstractCollection ; 16 import java.util.AbstractSet ; 17 import java.util.ArrayList ; 18 import java.util.Collection ; 19 import java.util.ConcurrentModificationException ; 20 import java.util.Iterator ; 21 import java.util.Map.Entry; 22 import java.util.Map ; 23 import java.util.NoSuchElementException ; 24 import java.util.Set ; 25 import org.apache.log4j.Logger; 26 27 53 54 public class WeakHashMap extends AbstractMap implements Map { 55 static Logger logger = Logger.getLogger("weak.collections"); 56 57 static int hash(Object x) { 58 int h = x.hashCode(); 59 60 h += ~(h << 9); 61 h ^= (h >>> 14); 62 h += (h << 4); 63 h ^= (h >>> 10); 64 return h; 65 } 66 67 70 private static final int DEFAULT_INITIAL_CAPACITY = 16; 71 72 77 private static final int MAXIMUM_CAPACITY = 1 << 30; 78 79 82 private static final float DEFAULT_LOAD_FACTOR = 0.75f; 83 84 87 private Entry[] table; 88 89 92 private int size; 93 94 97 private int threshold; 98 99 102 private final float loadFactor; 103 104 107 private final ReferenceQueue queue = new ReferenceQueue (); 108 109 116 private volatile int modCount; 117 118 127 public WeakHashMap(int initialCapacity, float loadFactor) { 128 if (initialCapacity < 0) 129 throw new IllegalArgumentException ("Illegal Initial Capacity: "+ 130 initialCapacity); 131 if (initialCapacity > MAXIMUM_CAPACITY) 132 initialCapacity = MAXIMUM_CAPACITY; 133 134 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 135 throw new IllegalArgumentException ("Illegal Load factor: "+ 136 loadFactor); 137 int capacity = 1; 138 while (capacity < initialCapacity) 139 capacity <<= 1; 140 table = new Entry[capacity]; 141 this.loadFactor = loadFactor; 142 threshold = (int)(capacity * loadFactor); 143 } 144 145 152 public WeakHashMap(int initialCapacity) { 153 this(initialCapacity, DEFAULT_LOAD_FACTOR); 154 } 155 156 160 public WeakHashMap() { 161 this.loadFactor = DEFAULT_LOAD_FACTOR; 162 threshold = (int)(DEFAULT_INITIAL_CAPACITY); 163 table = new Entry[DEFAULT_INITIAL_CAPACITY]; 164 } 165 166 176 public WeakHashMap(Map t) { 177 this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, 16), 178 DEFAULT_LOAD_FACTOR); 179 putAll(t); 180 } 181 182 184 187 private static final Object NULL_KEY = new Object (); 188 189 192 private static Object maskNull(Object key) { 193 return (key == null ? NULL_KEY : key); 194 } 195 196 199 private static Object unmaskNull(Object key) { 200 return (key == NULL_KEY ? null : key); 201 } 202 203 207 static boolean eq(Object x, Object y) { 208 return x == y || x.equals(y); 209 } 210 211 214 static int indexFor(int h, int length) { 215 return h & (length-1); 216 } 217 218 221 private void expungeStaleEntries() { 222 Object r; 223 while ( (r = queue.poll()) != null) { 224 Entry e = (Entry)r; 225 logger.debug("removing from hashmap "+r); 226 227 int h = e.hash; 228 int i = indexFor(h, table.length); 229 230 Entry prev = table[i]; 231 Entry p = prev; 232 while (p != null) { 233 Entry next = p.next; 234 if (p == e) { 235 if (prev == e) 236 table[i] = next; 237 else 238 prev.next = next; 239 e.next = null; e.key = null; size--; 242 break; 243 } 244 prev = p; 245 p = next; 246 } 247 } 248 } 249 250 253 private Entry[] getTable() { 254 expungeStaleEntries(); 255 return table; 256 } 257 258 264 public int size() { 265 if (size == 0) 266 return 0; 267 expungeStaleEntries(); 268 return size; 269 } 270 271 277 public boolean isEmpty() { 278 return size() == 0; 279 } 280 281 295 public Object get(Object key) { 296 Object k = maskNull(key); 297 int h = hash(k); 298 Entry[] tab = getTable(); 299 int index = indexFor(h, tab.length); 300 Entry e = tab[index]; 301 while (e != null) { 302 if (e.hash == h && eq(k, e.getKey())) 303 return e.getValue(); 304 e = e.next; 305 } 306 return null; 307 } 308 309 317 public boolean containsKey(Object key) { 318 return getEntry(key) != null; 319 } 320 321 325 Entry getEntry(Object key) { 326 Object k = maskNull(key); 327 int h = hash(k); 328 Entry[] tab = getTable(); 329 int index = indexFor(h, tab.length); 330 Entry e = tab[index]; 331 while (e != null && !(e.hash == h && eq(k, e.get()))) 332 e = e.next; 333 return e; 334 } 335 336 348 public Object put(Object key, Object value) { 349 Object k = maskNull(key); 350 int h = hash(k); 351 Entry[] tab = getTable(); 352 Object old = null; 353 int i = indexFor(h, tab.length); 354 355 Entry prev = tab[i]; 356 Entry e = prev; 357 358 while (e != null) { 359 Entry next = e.next; 360 361 if (h == e.hash && eq(k, e.key)) { 362 modCount++; 363 size--; 364 if (prev == e) 365 tab[i] = next; 366 else 367 prev.next = next; 368 old = e.getValue(); 369 break; 370 } 371 372 prev = e; 373 e = next; 374 } 375 376 modCount++; 377 tab[i] = new Entry(k, value, queue, h, tab[i]); 378 if (++size >= threshold) 379 resize(tab.length * 2); 380 return old; 381 } 382 383 393 void resize(int newCapacity) { 394 396 Entry[] oldTable = getTable(); 397 int oldCapacity = oldTable.length; 398 399 if (size < threshold || oldCapacity > newCapacity) 401 return; 402 403 Entry[] newTable = new Entry[newCapacity]; 404 405 transfer(oldTable, newTable); 406 table = newTable; 407 408 413 if (size >= threshold / 2) { 414 threshold = (int)(newCapacity * loadFactor); 415 } else { 416 expungeStaleEntries(); 417 transfer(newTable, oldTable); 418 table = oldTable; 419 } 420 } 421 422 423 private void transfer(Entry[] src, Entry[] dest) { 424 for (int j = 0; j < src.length; ++j) { 425 Entry e = src[j]; 426 src[j] = null; 427 while (e != null) { 428 Entry next = e.next; 429 Object key = e.get(); 430 if (key == null) { 431 e.next = null; e.key = null; size--; 434 } else { 435 int i = indexFor(e.hash, dest.length); 436 e.next = dest[i]; 437 dest[i] = e; 438 } 439 e = next; 440 } 441 } 442 } 443 444 452 public void putAll(Map t) { 453 int n = t.size(); 455 if (n == 0) 456 return; 457 if (n >= threshold) { 458 n = (int)(n / loadFactor + 1); 459 if (n > MAXIMUM_CAPACITY) 460 n = MAXIMUM_CAPACITY; 461 int capacity = table.length; 462 while (capacity < n) 463 capacity <<= 1; 464 resize(capacity); 465 } 466 467 for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) { 468 Map.Entry e = (Map.Entry ) i.next(); 469 put(e.getKey(), e.getValue()); 470 } 471 } 472 473 482 public Object remove(Object key) { 483 Object k = maskNull(key); 484 int h = hash(k); 485 Entry[] tab = getTable(); 486 int i = indexFor(h, tab.length); 487 Entry prev = tab[i]; 488 Entry e = prev; 489 490 while (e != null) { 491 Entry next = e.next; 492 if (h == e.hash && eq(k, e.key)) { 493 modCount++; 494 size--; 495 if (prev == e) 496 tab[i] = next; 497 else 498 prev.next = next; 499 return e.getValue(); 500 } 501 prev = e; 502 e = next; 503 } 504 505 return null; 506 } 507 508 509 510 511 Entry removeMapping(Object o) { 512 if (!(o instanceof Map.Entry )) 513 return null; 514 Entry[] tab = getTable(); 515 Map.Entry entry = (Map.Entry )o; 516 Object k = maskNull(entry.getKey()); 517 int h = hash(k); 518 int i = indexFor(h, tab.length); 519 Entry prev = tab[i]; 520 Entry e = prev; 521 522 while (e != null) { 523 Entry next = e.next; 524 if (h == e.hash && e.equals(entry)) { 525 modCount++; 526 size--; 527 if (prev == e) 528 tab[i] = next; 529 else 530 prev.next = next; 531 return e; 532 } 533 prev = e; 534 e = next; 535 } 536 537 return null; 538 } 539 540 543 public void clear() { 544 while (queue.poll() != null) 547 ; 548 549 modCount++; 550 Entry tab[] = table; 551 for (int i = 0; i < tab.length; ++i) 552 tab[i] = null; 553 size = 0; 554 555 while (queue.poll() != null) 559 ; 560 } 561 562 570 public boolean containsValue(Object value) { 571 if (value==null) 572 return containsNullValue(); 573 574 Entry tab[] = getTable(); 575 for (int i = tab.length ; i-- > 0 ;) 576 for (Entry e = tab[i] ; e != null ; e = e.next) 577 if (value.equals(e.getValue())) 578 return true; 579 return false; 580 } 581 582 585 private boolean containsNullValue() { 586 Entry tab[] = getTable(); 587 for (int i = tab.length ; i-- > 0 ;) 588 for (Entry e = tab[i] ; e != null ; e = e.next) 589 if (e.getValue()==null) 590 return true; 591 return false; 592 } 593 594 598 private static class Entry extends WeakReference implements Map.Entry { 599 private Object key; 600 private final int hash; 601 private Entry next; 602 603 606 Entry(Object key, Object value, ReferenceQueue queue, 607 int hash, Entry next) { 608 super(value, queue); 609 this.key = key; 610 this.hash = hash; 611 this.next = next; 612 } 613 614 public Object getKey() { 615 return unmaskNull(key); 616 } 617 618 public Object getValue() { 619 return this.get(); 620 } 621 622 public Object setValue(Object newValue) { 623 throw new RuntimeException ( 624 "Entry.setValue cannot be implemented in weak-value entries"); 625 } 626 627 public boolean equals(Object o) { 628 if (!(o instanceof Map.Entry )) 629 return false; 630 Map.Entry e = (Map.Entry )o; 631 Object k1 = getKey(); 632 Object k2 = e.getKey(); 633 if (k1 == k2 || (k1 != null && k1.equals(k2))) { 634 Object v1 = getValue(); 635 Object v2 = e.getValue(); 636 if (v1 == v2 || (v1 != null && v1.equals(v2))) 637 return true; 638 } 639 return false; 640 } 641 642 public int hashCode() { 643 Object k = getKey(); 644 Object v = getValue(); 645 return ((k==null ? 0 : k.hashCode()) ^ 646 (v==null ? 0 : v.hashCode())); 647 } 648 649 public String toString() { 650 return getKey() + "=" + getValue(); 651 } 652 } 653 654 private abstract class HashIterator implements Iterator { 655 int index; 656 Entry entry = null; 657 Entry lastReturned = null; 658 int expectedModCount = modCount; 659 660 664 Object nextKey = null; 665 666 670 Object currentKey = null; 671 672 HashIterator() { 673 index = (size() != 0 ? table.length : 0); 674 } 675 676 public boolean hasNext() { 677 Entry[] t = table; 678 679 while (nextKey == null) { 680 Entry e = entry; 681 int i = index; 682 while (e == null && i > 0) 683 e = t[--i]; 684 entry = e; 685 index = i; 686 if (e == null) { 687 currentKey = null; 688 return false; 689 } 690 nextKey = e.get(); if (nextKey == null) 692 entry = entry.next; 693 } 694 return true; 695 } 696 697 698 protected Entry nextEntry() { 699 if (modCount != expectedModCount) 700 throw new ConcurrentModificationException (); 701 if (nextKey == null && !hasNext()) 702 throw new NoSuchElementException (); 703 704 lastReturned = entry; 705 entry = entry.next; 706 currentKey = nextKey; 707 nextKey = null; 708 return lastReturned; 709 } 710 711 public void remove() { 712 if (lastReturned == null) 713 throw new IllegalStateException (); 714 if (modCount != expectedModCount) 715 throw new ConcurrentModificationException (); 716 717 WeakHashMap.this.remove(currentKey); 718 expectedModCount = modCount; 719 lastReturned = null; 720 currentKey = null; 721 } 722 723 } 724 725 private class ValueIterator extends HashIterator { 726 public Object next() { 727 return nextEntry().getValue(); 728 } 729 } 730 731 private class KeyIterator extends HashIterator { 732 public Object next() { 733 return nextEntry().getKey(); 734 } 735 } 736 737 private class EntryIterator extends HashIterator { 738 public Object next() { 739 return nextEntry(); 740 } 741 } 742 743 745 private transient Set entrySet = null; 746 747 758 763 private class KeySet extends AbstractSet { 764 public Iterator iterator() { 765 return new KeyIterator(); 766 } 767 768 public int size() { 769 return WeakHashMap.this.size(); 770 } 771 772 public boolean contains(Object o) { 773 return containsKey(o); 774 } 775 776 public boolean remove(Object o) { 777 if (containsKey(o)) { 778 WeakHashMap.this.remove(o); 779 return true; 780 } 781 else 782 return false; 783 } 784 785 public void clear() { 786 WeakHashMap.this.clear(); 787 } 788 789 public Object [] toArray() { 790 Collection c = new ArrayList (size()); 791 for (Iterator i = iterator(); i.hasNext(); ) 792 c.add(i.next()); 793 return c.toArray(); 794 } 795 796 public Object [] toArray(Object a[]) { 797 Collection c = new ArrayList (size()); 798 for (Iterator i = iterator(); i.hasNext(); ) 799 c.add(i.next()); 800 return c.toArray(a); 801 } 802 } 803 804 815 public Collection values() { 816 Collection vs = values; 817 return (vs != null ? vs : (values = new Values())); 818 } 819 820 private class Values extends AbstractCollection { 821 public Iterator iterator() { 822 return new ValueIterator(); 823 } 824 825 public int size() { 826 return WeakHashMap.this.size(); 827 } 828 829 public boolean contains(Object o) { 830 return containsValue(o); 831 } 832 833 public void clear() { 834 WeakHashMap.this.clear(); 835 } 836 837 public Object [] toArray() { 838 Collection c = new ArrayList (size()); 839 for (Iterator i = iterator(); i.hasNext(); ) 840 c.add(i.next()); 841 return c.toArray(); 842 } 843 844 public Object [] toArray(Object a[]) { 845 Collection c = new ArrayList (size()); 846 for (Iterator i = iterator(); i.hasNext(); ) 847 c.add(i.next()); 848 return c.toArray(a); 849 } 850 } 851 852 865 public Set entrySet() { 866 Set es = entrySet; 867 return (es != null ? es : (entrySet = new EntrySet())); 868 } 869 870 private class EntrySet extends AbstractSet { 871 public Iterator iterator() { 872 return new EntryIterator(); 873 } 874 875 public boolean contains(Object o) { 876 if (!(o instanceof Map.Entry )) 877 return false; 878 Map.Entry e = (Map.Entry )o; 879 Object k = e.getKey(); 880 Entry candidate = getEntry(e.getKey()); 881 return candidate != null && candidate.equals(e); 882 } 883 884 public boolean remove(Object o) { 885 return removeMapping(o) != null; 886 } 887 888 public int size() { 889 return WeakHashMap.this.size(); 890 } 891 892 public void clear() { 893 WeakHashMap.this.clear(); 894 } 895 896 public Object [] toArray() { 897 Collection c = new ArrayList (size()); 898 for (Iterator i = iterator(); i.hasNext(); ) 899 c.add(new AbstractMap.SimpleEntry((Map.Entry ) i.next())); 900 return c.toArray(); 901 } 902 903 public Object [] toArray(Object a[]) { 904 Collection c = new ArrayList (size()); 905 for (Iterator i = iterator(); i.hasNext(); ) 906 c.add(new AbstractMap.SimpleEntry((Map.Entry ) i.next())); 907 return c.toArray(a); 908 } 909 } 910 } 911 | Popular Tags |