Source for gnu.java.util.WeakIdentityHashMap

   1: /* WeakIdentityHashMap -- an identity hashtable that keeps only weak references
   2:    to its keys, allowing the virtual machine to reclaim them
   3:    Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package gnu.java.util;
  41: 
  42: import java.lang.ref.ReferenceQueue;
  43: import java.lang.ref.WeakReference;
  44: import java.util.AbstractMap;
  45: import java.util.AbstractSet;
  46: import java.util.Collection;
  47: import java.util.ConcurrentModificationException;
  48: import java.util.Iterator;
  49: import java.util.Map;
  50: import java.util.NoSuchElementException;
  51: import java.util.Set;
  52: 
  53: /**
  54:  * A weak hash map has only weak references to the key. This means that it
  55:  * allows the key to be garbage collected if it is not used otherwise. If
  56:  * this happens, the entry will eventually disappear from the map,
  57:  * asynchronously.
  58:  *
  59:  * <p>Other strange behaviors to be aware of: The size of this map may
  60:  * spontaneously shrink (even if you use a synchronized map and synchronize
  61:  * it); it behaves as if another thread removes entries from this table
  62:  * without synchronization.  The entry set returned by <code>entrySet</code>
  63:  * has similar phenomenons: The size may spontaneously shrink, or an
  64:  * entry, that was in the set before, suddenly disappears.
  65:  *
  66:  * <p>A weak hash map is not meant for caches; use a normal map, with
  67:  * soft references as values instead, or try {@link LinkedHashMap}.
  68:  *
  69:  * <p>The weak hash map supports null values and null keys.  The null key
  70:  * is never deleted from the map (except explictly of course). The
  71:  * performance of the methods are similar to that of a hash map.
  72:  *
  73:  * <p>The value objects are strongly referenced by this table.  So if a
  74:  * value object maintains a strong reference to the key (either direct
  75:  * or indirect) the key will never be removed from this map.  According
  76:  * to Sun, this problem may be fixed in a future release.  It is not
  77:  * possible to do it with the jdk 1.2 reference model, though.
  78:  *
  79:  * @author Jochen Hoenicke
  80:  * @author Eric Blake (ebb9@email.byu.edu)
  81:  * @author Jeroen Frijters
  82:  *
  83:  * @see HashMap
  84:  * @see WeakReference
  85:  * @see WeakHashMap
  86:  * @see IdentityHashMap
  87:  * @see LinkedHashMap
  88:  */
  89: public class WeakIdentityHashMap extends AbstractMap implements Map
  90: {
  91:   /**
  92:    * The default capacity for an instance of HashMap.
  93:    * Sun's documentation mildly suggests that this (11) is the correct
  94:    * value.
  95:    */
  96:   private static final int DEFAULT_CAPACITY = 11;
  97: 
  98:   /**
  99:    * The default load factor of a HashMap.
 100:    */
 101:   private static final float DEFAULT_LOAD_FACTOR = 0.75F;
 102: 
 103:   /**
 104:    * This is used instead of the key value <i>null</i>.  It is needed
 105:    * to distinguish between an null key and a removed key.
 106:    */
 107:   // Package visible for use by nested classes.
 108:   static final Object NULL_KEY = new Object();
 109: 
 110:   /**
 111:    * The reference queue where our buckets (which are WeakReferences) are
 112:    * registered to.
 113:    */
 114:   private final ReferenceQueue queue;
 115: 
 116:   /**
 117:    * The number of entries in this hash map.
 118:    */
 119:   // Package visible for use by nested classes.
 120:   int size;
 121: 
 122:   /**
 123:    * The load factor of this WeakIdentityHashMap.  This is the maximum ratio of
 124:    * size versus number of buckets.  If size grows the number of buckets
 125:    * must grow, too.
 126:    */
 127:   private float loadFactor;
 128: 
 129:   /**
 130:    * The rounded product of the capacity (i.e. number of buckets) and
 131:    * the load factor. When the number of elements exceeds the
 132:    * threshold, the HashMap calls <code>rehash()</code>.
 133:    */
 134:   private int threshold;
 135: 
 136:   /**
 137:    * The number of structural modifications.  This is used by
 138:    * iterators, to see if they should fail.  This doesn't count
 139:    * the silent key removals, when a weak reference is cleared
 140:    * by the garbage collection.  Instead the iterators must make
 141:    * sure to have strong references to the entries they rely on.
 142:    */
 143:   // Package visible for use by nested classes.
 144:   int modCount;
 145: 
 146:   /**
 147:    * The entry set.  There is only one instance per hashmap, namely
 148:    * theEntrySet.  Note that the entry set may silently shrink, just
 149:    * like the WeakIdentityHashMap.
 150:    */
 151:   private final class WeakEntrySet extends AbstractSet
 152:   {
 153:     /**
 154:      * Non-private constructor to reduce bytecode emitted.
 155:      */
 156:     WeakEntrySet()
 157:     {
 158:     }
 159: 
 160:     /**
 161:      * Returns the size of this set.
 162:      *
 163:      * @return the set size
 164:      */
 165:     public int size()
 166:     {
 167:       return size;
 168:     }
 169: 
 170:     /**
 171:      * Returns an iterator for all entries.
 172:      *
 173:      * @return an Entry iterator
 174:      */
 175:     public Iterator iterator()
 176:     {
 177:       return new Iterator()
 178:       {
 179:         /**
 180:          * The entry that was returned by the last
 181:          * <code>next()</code> call.  This is also the entry whose
 182:          * bucket should be removed by the <code>remove</code> call. <br>
 183:          *
 184:          * It is null, if the <code>next</code> method wasn't
 185:          * called yet, or if the entry was already removed.  <br>
 186:          *
 187:          * Remembering this entry here will also prevent it from
 188:          * being removed under us, since the entry strongly refers
 189:          * to the key.
 190:          */
 191:         WeakBucket.WeakEntry lastEntry;
 192: 
 193:         /**
 194:          * The entry that will be returned by the next
 195:          * <code>next()</code> call.  It is <code>null</code> if there
 196:          * is no further entry. <br>
 197:          *
 198:          * Remembering this entry here will also prevent it from
 199:          * being removed under us, since the entry strongly refers
 200:          * to the key.
 201:          */
 202:         WeakBucket.WeakEntry nextEntry = findNext(null);
 203: 
 204:         /**
 205:          * The known number of modification to the list, if it differs
 206:          * from the real number, we throw an exception.
 207:          */
 208:         int knownMod = modCount;
 209: 
 210:         /**
 211:          * Check the known number of modification to the number of
 212:          * modifications of the table.  If it differs from the real
 213:          * number, we throw an exception.
 214:          * @throws ConcurrentModificationException if the number
 215:          *         of modifications doesn't match.
 216:          */
 217:         private void checkMod()
 218:         {
 219:           // This method will get inlined.
 220:           cleanQueue();
 221:           if (knownMod != modCount)
 222:             throw new ConcurrentModificationException(knownMod + " != "
 223:                                                       + modCount);
 224:         }
 225: 
 226:         /**
 227:          * Get a strong reference to the next entry after
 228:          * lastBucket.
 229:          * @param lastEntry the previous bucket, or null if we should
 230:          * get the first entry.
 231:          * @return the next entry.
 232:          */
 233:         private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry)
 234:         {
 235:           int slot;
 236:           WeakBucket nextBucket;
 237:           if (lastEntry != null)
 238:             {
 239:               nextBucket = lastEntry.getBucket().next;
 240:               slot = lastEntry.getBucket().slot;
 241:             }
 242:           else
 243:             {
 244:               nextBucket = buckets[0];
 245:               slot = 0;
 246:             }
 247: 
 248:           while (true)
 249:             {
 250:               while (nextBucket != null)
 251:                 {
 252:                   WeakBucket.WeakEntry entry = nextBucket.getEntry();
 253:                   if (entry != null)
 254:                     // This is the next entry.
 255:                     return entry;
 256: 
 257:                   // Entry was cleared, try next.
 258:                   nextBucket = nextBucket.next;
 259:                 }
 260: 
 261:               slot++;
 262:               if (slot == buckets.length)
 263:                 // No more buckets, we are through.
 264:                 return null;
 265: 
 266:               nextBucket = buckets[slot];
 267:             }
 268:         }
 269: 
 270:         /**
 271:          * Checks if there are more entries.
 272:          * @return true, iff there are more elements.
 273:          * @throws ConcurrentModificationException if the hash map was
 274:          *         modified.
 275:          */
 276:         public boolean hasNext()
 277:         {
 278:           checkMod();
 279:           return nextEntry != null;
 280:         }
 281: 
 282:         /**
 283:          * Returns the next entry.
 284:          * @return the next entry.
 285:          * @throws ConcurrentModificationException if the hash map was
 286:          *         modified.
 287:          * @throws NoSuchElementException if there is no entry.
 288:          */
 289:         public Object next()
 290:         {
 291:           checkMod();
 292:           if (nextEntry == null)
 293:             throw new NoSuchElementException();
 294:           lastEntry = nextEntry;
 295:           nextEntry = findNext(lastEntry);
 296:           return lastEntry;
 297:         }
 298: 
 299:         /**
 300:          * Removes the last returned entry from this set.  This will
 301:          * also remove the bucket of the underlying weak hash map.
 302:          * @throws ConcurrentModificationException if the hash map was
 303:          *         modified.
 304:          * @throws IllegalStateException if <code>next()</code> was
 305:          *         never called or the element was already removed.
 306:          */
 307:         public void remove()
 308:         {
 309:           checkMod();
 310:           if (lastEntry == null)
 311:             throw new IllegalStateException();
 312:           modCount++;
 313:           internalRemove(lastEntry.getBucket());
 314:           lastEntry = null;
 315:           knownMod++;
 316:         }
 317:       };
 318:     }
 319:   }
 320: 
 321:   /**
 322:    * A bucket is a weak reference to the key, that contains a strong
 323:    * reference to the value, a pointer to the next bucket and its slot
 324:    * number. <br>
 325:    *
 326:    * It would be cleaner to have a WeakReference as field, instead of
 327:    * extending it, but if a weak reference gets cleared, we only get
 328:    * the weak reference (by queue.poll) and wouldn't know where to
 329:    * look for this reference in the hashtable, to remove that entry.
 330:    *
 331:    * @author Jochen Hoenicke
 332:    */
 333:   private static class WeakBucket extends WeakReference
 334:   {
 335:     /**
 336:      * The value of this entry.  The key is stored in the weak
 337:      * reference that we extend.
 338:      */
 339:     Object value;
 340: 
 341:     /**
 342:      * The next bucket describing another entry that uses the same
 343:      * slot.
 344:      */
 345:     WeakBucket next;
 346: 
 347:     /**
 348:      * The slot of this entry. This should be
 349:      * <code>Math.abs(key.hashCode() % buckets.length)</code>.
 350:      *
 351:      * But since the key may be silently removed we have to remember
 352:      * the slot number.
 353:      *
 354:      * If this bucket was removed the slot is -1.  This marker will
 355:      * prevent the bucket from being removed twice.
 356:      */
 357:     int slot;
 358: 
 359:     /**
 360:      * Creates a new bucket for the given key/value pair and the specified
 361:      * slot.
 362:      * @param key the key
 363:      * @param queue the queue the weak reference belongs to
 364:      * @param value the value
 365:      * @param slot the slot.  This must match the slot where this bucket
 366:      *        will be enqueued.
 367:      */
 368:     public WeakBucket(Object key, ReferenceQueue queue, Object value,
 369:                       int slot)
 370:     {
 371:       super(key, queue);
 372:       this.value = value;
 373:       this.slot = slot;
 374:     }
 375: 
 376:     /**
 377:      * This class gives the <code>Entry</code> representation of the
 378:      * current bucket.  It also keeps a strong reference to the
 379:      * key; bad things may happen otherwise.
 380:      */
 381:     class WeakEntry implements Map.Entry
 382:     {
 383:       /**
 384:        * The strong ref to the key.
 385:        */
 386:       Object key;
 387: 
 388:       /**
 389:        * Creates a new entry for the key.
 390:        * @param key the key
 391:        */
 392:       public WeakEntry(Object key)
 393:       {
 394:         this.key = key;
 395:       }
 396: 
 397:       /**
 398:        * Returns the underlying bucket.
 399:        * @return the owning bucket
 400:        */
 401:       public WeakBucket getBucket()
 402:       {
 403:         return WeakBucket.this;
 404:       }
 405: 
 406:       /**
 407:        * Returns the key.
 408:        * @return the key
 409:        */
 410:       public Object getKey()
 411:       {
 412:         return key == NULL_KEY ? null : key;
 413:       }
 414: 
 415:       /**
 416:        * Returns the value.
 417:        * @return the value
 418:        */
 419:       public Object getValue()
 420:       {
 421:         return value;
 422:       }
 423: 
 424:       /**
 425:        * This changes the value.  This change takes place in
 426:        * the underlying hash map.
 427:        * @param newVal the new value
 428:        * @return the old value
 429:        */
 430:       public Object setValue(Object newVal)
 431:       {
 432:         Object oldVal = value;
 433:         value = newVal;
 434:         return oldVal;
 435:       }
 436: 
 437:       /**
 438:        * The hashCode as specified in the Entry interface.
 439:        * @return the hash code
 440:        */
 441:       public int hashCode()
 442:       {
 443:         return System.identityHashCode(key)
 444:             ^ (value == null ? 0 : value.hashCode());
 445:       }
 446: 
 447:       /**
 448:        * The equals method as specified in the Entry interface.
 449:        * @param o the object to compare to
 450:        * @return true iff o represents the same key/value pair
 451:        */
 452:       public boolean equals(Object o)
 453:       {
 454:         if (o instanceof Map.Entry)
 455:           {
 456:             Map.Entry e = (Map.Entry) o;
 457:             return getKey() == e.getKey()
 458:               && (value == null ? e.getValue() == null
 459:                                 : value.equals(e.getValue()));
 460:           }
 461:         return false;
 462:       }
 463: 
 464:       public String toString()
 465:       {
 466:         return getKey() + "=" + value;
 467:       }
 468:     }
 469: 
 470:     /**
 471:      * This returns the entry stored in this bucket, or null, if the
 472:      * bucket got cleared in the mean time.
 473:      * @return the Entry for this bucket, if it exists
 474:      */
 475:     WeakEntry getEntry()
 476:     {
 477:       final Object key = this.get();
 478:       if (key == null)
 479:         return null;
 480:       return new WeakEntry(key);
 481:     }
 482:   }
 483: 
 484:   /**
 485:    * The entry set returned by <code>entrySet()</code>.
 486:    */
 487:   private final WeakEntrySet theEntrySet;
 488: 
 489:   /**
 490:    * The hash buckets.  These are linked lists. Package visible for use in
 491:    * nested classes.
 492:    */
 493:   WeakBucket[] buckets;
 494: 
 495:   /**
 496:    * Creates a new weak hash map with default load factor and default
 497:    * capacity.
 498:    */
 499:   public WeakIdentityHashMap()
 500:   {
 501:     this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
 502:   }
 503: 
 504:   /**
 505:    * Creates a new weak hash map with default load factor and the given
 506:    * capacity.
 507:    * @param initialCapacity the initial capacity
 508:    * @throws IllegalArgumentException if initialCapacity is negative
 509:    */
 510:   public WeakIdentityHashMap(int initialCapacity)
 511:   {
 512:     this(initialCapacity, DEFAULT_LOAD_FACTOR);
 513:   }
 514: 
 515:   /**
 516:    * Creates a new weak hash map with the given initial capacity and
 517:    * load factor.
 518:    * @param initialCapacity the initial capacity.
 519:    * @param loadFactor the load factor (see class description of HashMap).
 520:    * @throws IllegalArgumentException if initialCapacity is negative, or
 521:    *         loadFactor is non-positive
 522:    */
 523:   public WeakIdentityHashMap(int initialCapacity, float loadFactor)
 524:   {
 525:     // Check loadFactor for NaN as well.
 526:     if (initialCapacity < 0 || ! (loadFactor > 0))
 527:       throw new IllegalArgumentException();
 528:     if (initialCapacity == 0)
 529:       initialCapacity = 1;
 530:     this.loadFactor = loadFactor;
 531:     threshold = (int) (initialCapacity * loadFactor);
 532:     theEntrySet = new WeakEntrySet();
 533:     queue = new ReferenceQueue();
 534:     buckets = new WeakBucket[initialCapacity];
 535:   }
 536: 
 537:   /**
 538:    * Construct a new WeakIdentityHashMap with the same mappings as the given map.
 539:    * The WeakIdentityHashMap has a default load factor of 0.75.
 540:    *
 541:    * @param m the map to copy
 542:    * @throws NullPointerException if m is null
 543:    * @since 1.3
 544:    */
 545:   public WeakIdentityHashMap(Map m)
 546:   {
 547:     this(m.size(), DEFAULT_LOAD_FACTOR);
 548:     putAll(m);
 549:   }
 550: 
 551:   /**
 552:    * Simply hashes a non-null Object to its array index.
 553:    * @param key the key to hash
 554:    * @return its slot number
 555:    */
 556:   private int hash(Object key)
 557:   {
 558:     return Math.abs(System.identityHashCode(key) % buckets.length);
 559:   }
 560: 
 561:   /**
 562:    * Cleans the reference queue.  This will poll all references (which
 563:    * are WeakBuckets) from the queue and remove them from this map.
 564:    * This will not change modCount, even if it modifies the map.  The
 565:    * iterators have to make sure that nothing bad happens.  <br>
 566:    *
 567:    * Currently the iterator maintains a strong reference to the key, so
 568:    * that is no problem.
 569:    */
 570:   // Package visible for use by nested classes.
 571:   void cleanQueue()
 572:   {
 573:     Object bucket = queue.poll();
 574:     while (bucket != null)
 575:       {
 576:         internalRemove((WeakBucket) bucket);
 577:         bucket = queue.poll();
 578:       }
 579:   }
 580: 
 581:   /**
 582:    * Rehashes this hashtable.  This will be called by the
 583:    * <code>add()</code> method if the size grows beyond the threshold.
 584:    * It will grow the bucket size at least by factor two and allocates
 585:    * new buckets.
 586:    */
 587:   private void rehash()
 588:   {
 589:     WeakBucket[] oldBuckets = buckets;
 590:     int newsize = buckets.length * 2 + 1; // XXX should be prime.
 591:     threshold = (int) (newsize * loadFactor);
 592:     buckets = new WeakBucket[newsize];
 593: 
 594:     // Now we have to insert the buckets again.
 595:     for (int i = 0; i < oldBuckets.length; i++)
 596:       {
 597:         WeakBucket bucket = oldBuckets[i];
 598:         WeakBucket nextBucket;
 599:         while (bucket != null)
 600:           {
 601:             nextBucket = bucket.next;
 602: 
 603:             Object key = bucket.get();
 604:             if (key == null)
 605:               {
 606:                 // This bucket should be removed; it is probably
 607:                 // already on the reference queue.  We don't insert it
 608:                 // at all, and mark it as cleared.
 609:                 bucket.slot = -1;
 610:                 size--;
 611:               }
 612:             else
 613:               {
 614:                 // Add this bucket to its new slot.
 615:                 int slot = hash(key);
 616:                 bucket.slot = slot;
 617:                 bucket.next = buckets[slot];
 618:                 buckets[slot] = bucket;
 619:               }
 620:             bucket = nextBucket;
 621:           }
 622:       }
 623:   }
 624: 
 625:   /**
 626:    * Finds the entry corresponding to key.  Since it returns an Entry
 627:    * it will also prevent the key from being removed under us.
 628:    * @param key the key, may be null
 629:    * @return The WeakBucket.WeakEntry or null, if the key wasn't found.
 630:    */
 631:   private WeakBucket.WeakEntry internalGet(Object key)
 632:   {
 633:     if (key == null)
 634:       key = NULL_KEY;
 635:     int slot = hash(key);
 636:     WeakBucket bucket = buckets[slot];
 637:     while (bucket != null)
 638:       {
 639:         WeakBucket.WeakEntry entry = bucket.getEntry();
 640:         if (entry != null && key == entry.key)
 641:           return entry;
 642: 
 643:         bucket = bucket.next;
 644:       }
 645:     return null;
 646:   }
 647: 
 648:   /**
 649:    * Adds a new key/value pair to the hash map.
 650:    * @param key the key. This mustn't exists in the map. It may be null.
 651:    * @param value the value.
 652:    */
 653:   private void internalAdd(Object key, Object value)
 654:   {
 655:     if (key == null)
 656:       key = NULL_KEY;
 657:     int slot = hash(key);
 658:     WeakBucket bucket = new WeakBucket(key, queue, value, slot);
 659:     bucket.next = buckets[slot];
 660:     buckets[slot] = bucket;
 661:     size++;
 662:   }
 663: 
 664:   /**
 665:    * Removes a bucket from this hash map, if it wasn't removed before
 666:    * (e.g. one time through rehashing and one time through reference queue).
 667:    * Package visible for use in nested classes.
 668:    *
 669:    * @param bucket the bucket to remove.
 670:    */
 671:   void internalRemove(WeakBucket bucket)
 672:   {
 673:     int slot = bucket.slot;
 674:     if (slot == -1)
 675:       // This bucket was already removed.
 676:       return;
 677: 
 678:     // Mark the bucket as removed.  This is necessary, since the
 679:     // bucket may be enqueued later by the garbage collection, and
 680:     // internalRemove will be called a second time.
 681:     bucket.slot = -1;
 682: 
 683:     WeakBucket prev = null;
 684:     WeakBucket next = buckets[slot];
 685:     while (next != bucket)
 686:       {
 687:          if (next == null)
 688:             throw new InternalError("WeakIdentityHashMap in inconsistent state");
 689:          prev = next;
 690:          next = prev.next;
 691:       }
 692:     if (prev == null)
 693:       buckets[slot] = bucket.next;
 694:     else
 695:       prev.next = bucket.next;
 696: 
 697:     size--;
 698:   }
 699: 
 700:   /**
 701:    * Returns the size of this hash map.  Note that the size() may shrink
 702:    * spontaneously, if the some of the keys were only weakly reachable.
 703:    * @return the number of entries in this hash map.
 704:    */
 705:   public int size()
 706:   {
 707:     cleanQueue();
 708:     return size;
 709:   }
 710: 
 711:   /**
 712:    * Tells if the map is empty.  Note that the result may change
 713:    * spontanously, if all of the keys were only weakly reachable.
 714:    * @return true, iff the map is empty.
 715:    */
 716:   public boolean isEmpty()
 717:   {
 718:     cleanQueue();
 719:     return size == 0;
 720:   }
 721: 
 722:   /**
 723:    * Tells if the map contains the given key.  Note that the result
 724:    * may change spontanously, if the key was only weakly
 725:    * reachable.
 726:    * @param key the key to look for
 727:    * @return true, iff the map contains an entry for the given key.
 728:    */
 729:   public boolean containsKey(Object key)
 730:   {
 731:     cleanQueue();
 732:     return internalGet(key) != null;
 733:   }
 734: 
 735:   /**
 736:    * Gets the value the key is mapped to.
 737:    * @return the value the key was mapped to.  It returns null if
 738:    *         the key wasn't in this map, or if the mapped value was
 739:    *         explicitly set to null.
 740:    */
 741:   public Object get(Object key)
 742:   {
 743:     cleanQueue();
 744:     WeakBucket.WeakEntry entry = internalGet(key);
 745:     return entry == null ? null : entry.getValue();
 746:   }
 747: 
 748:   /**
 749:    * Adds a new key/value mapping to this map.
 750:    * @param key the key, may be null
 751:    * @param value the value, may be null
 752:    * @return the value the key was mapped to previously.  It returns
 753:    *         null if the key wasn't in this map, or if the mapped value
 754:    *         was explicitly set to null.
 755:    */
 756:   public Object put(Object key, Object value)
 757:   {
 758:     cleanQueue();
 759:     WeakBucket.WeakEntry entry = internalGet(key);
 760:     if (entry != null)
 761:       return entry.setValue(value);
 762: 
 763:     modCount++;
 764:     if (size >= threshold)
 765:       rehash();
 766: 
 767:     internalAdd(key, value);
 768:     return null;
 769:   }
 770: 
 771:   /**
 772:    * Removes the key and the corresponding value from this map.
 773:    * @param key the key. This may be null.
 774:    * @return the value the key was mapped to previously.  It returns
 775:    *         null if the key wasn't in this map, or if the mapped value was
 776:    *         explicitly set to null.
 777:    */
 778:   public Object remove(Object key)
 779:   {
 780:     cleanQueue();
 781:     WeakBucket.WeakEntry entry = internalGet(key);
 782:     if (entry == null)
 783:       return null;
 784: 
 785:     modCount++;
 786:     internalRemove(entry.getBucket());
 787:     return entry.getValue();
 788:   }
 789: 
 790:   /**
 791:    * Returns a set representation of the entries in this map.  This
 792:    * set will not have strong references to the keys, so they can be
 793:    * silently removed.  The returned set has therefore the same
 794:    * strange behaviour (shrinking size(), disappearing entries) as
 795:    * this weak hash map.
 796:    * @return a set representation of the entries.
 797:    */
 798:   public Set entrySet()
 799:   {
 800:     cleanQueue();
 801:     return theEntrySet;
 802:   }
 803: 
 804:   /**
 805:    * Clears all entries from this map.
 806:    */
 807:   public void clear()
 808:   {
 809:     super.clear();
 810:   }
 811: 
 812:   /**
 813:    * Returns true if the map contains at least one key which points to
 814:    * the specified object as a value.  Note that the result
 815:    * may change spontanously, if its key was only weakly reachable.
 816:    * @param value the value to search for
 817:    * @return true if it is found in the set.
 818:    */
 819:   public boolean containsValue(Object value)
 820:   {
 821:     cleanQueue();
 822:     return super.containsValue(value);
 823:   }
 824: 
 825:   /**
 826:    * Returns a set representation of the keys in this map.  This
 827:    * set will not have strong references to the keys, so they can be
 828:    * silently removed.  The returned set has therefore the same
 829:    * strange behaviour (shrinking size(), disappearing entries) as
 830:    * this weak hash map.
 831:    * @return a set representation of the keys.
 832:    */
 833:   public Set keySet()
 834:   {
 835:     cleanQueue();
 836:     return super.keySet();
 837:   }
 838: 
 839:   /**
 840:    * Puts all of the mappings from the given map into this one. If the
 841:    * key already exists in this map, its value is replaced.
 842:    * @param m the map to copy in
 843:    */
 844:   public void putAll(Map m)
 845:   {
 846:     super.putAll(m);
 847:   }
 848: 
 849:   /**
 850:    * Returns a collection representation of the values in this map.  This
 851:    * collection will not have strong references to the keys, so mappings
 852:    * can be silently removed.  The returned collection has therefore the same
 853:    * strange behaviour (shrinking size(), disappearing entries) as
 854:    * this weak hash map.
 855:    * @return a collection representation of the values.
 856:    */
 857:   public Collection values()
 858:   {
 859:     cleanQueue();
 860:     return super.values();
 861:   }
 862: } // class WeakIdentityHashMap