Source for java.util.WeakHashMap

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