/[classpath]/classpath/java/util/WeakHashMap.java
ViewVC logotype

Diff of /classpath/java/util/WeakHashMap.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.8

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26