1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.util;
19
20import java.lang.ref.ReferenceQueue;
21import java.lang.ref.WeakReference;
22
23/**
24 * WeakHashMap is an implementation of Map with keys which are WeakReferences. A
25 * key/value mapping is removed when the key is no longer referenced. All
26 * optional operations (adding and removing) are supported. Keys and values can
27 * be any objects. Note that the garbage collector acts similar to a second
28 * thread on this collection, possibly removing keys.
29 *
30 * @since 1.2
31 * @see HashMap
32 * @see WeakReference
33 */
34public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
35
36    private static final int DEFAULT_SIZE = 16;
37
38    private final ReferenceQueue<K> referenceQueue;
39
40    int elementCount;
41
42    Entry<K, V>[] elementData;
43
44    private final int loadFactor;
45
46    private int threshold;
47
48    volatile int modCount;
49
50    // Simple utility method to isolate unchecked cast for array creation
51    @SuppressWarnings("unchecked")
52    private static <K, V> Entry<K, V>[] newEntryArray(int size) {
53        return new Entry[size];
54    }
55
56    private static final class Entry<K, V> extends WeakReference<K> implements
57            Map.Entry<K, V> {
58        int hash;
59
60        boolean isNull;
61
62        V value;
63
64        Entry<K, V> next;
65
66        interface Type<R, K, V> {
67            R get(Map.Entry<K, V> entry);
68        }
69
70        Entry(K key, V object, ReferenceQueue<K> queue) {
71            super(key, queue);
72            isNull = key == null;
73            hash = isNull ? 0 : key.hashCode();
74            value = object;
75        }
76
77        public K getKey() {
78            return super.get();
79        }
80
81        public V getValue() {
82            return value;
83        }
84
85        public V setValue(V object) {
86            V result = value;
87            value = object;
88            return result;
89        }
90
91        @Override
92        public boolean equals(Object other) {
93            if (!(other instanceof Map.Entry)) {
94                return false;
95            }
96            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) other;
97            Object key = super.get();
98            return (key == null ? key == entry.getKey() : key.equals(entry
99                    .getKey()))
100                    && (value == null ? value == entry.getValue() : value
101                            .equals(entry.getValue()));
102        }
103
104        @Override
105        public int hashCode() {
106            return hash + (value == null ? 0 : value.hashCode());
107        }
108
109        @Override
110        public String toString() {
111            return super.get() + "=" + value;
112        }
113    }
114
115    class HashIterator<R> implements Iterator<R> {
116        private int position = 0, expectedModCount;
117
118        private Entry<K, V> currentEntry, nextEntry;
119
120        private K nextKey;
121
122        final Entry.Type<R, K, V> type;
123
124        HashIterator(Entry.Type<R, K, V> type) {
125            this.type = type;
126            expectedModCount = modCount;
127        }
128
129        public boolean hasNext() {
130            if (nextEntry != null && (nextKey != null || nextEntry.isNull)) {
131                return true;
132            }
133            while (true) {
134                if (nextEntry == null) {
135                    while (position < elementData.length) {
136                        if ((nextEntry = elementData[position++]) != null) {
137                            break;
138                        }
139                    }
140                    if (nextEntry == null) {
141                        return false;
142                    }
143                }
144                // ensure key of next entry is not gc'ed
145                nextKey = nextEntry.get();
146                if (nextKey != null || nextEntry.isNull) {
147                    return true;
148                }
149                nextEntry = nextEntry.next;
150            }
151        }
152
153        public R next() {
154            if (expectedModCount == modCount) {
155                if (hasNext()) {
156                    currentEntry = nextEntry;
157                    nextEntry = currentEntry.next;
158                    R result = type.get(currentEntry);
159                    // free the key
160                    nextKey = null;
161                    return result;
162                }
163                throw new NoSuchElementException();
164            }
165            throw new ConcurrentModificationException();
166        }
167
168        public void remove() {
169            if (expectedModCount == modCount) {
170                if (currentEntry != null) {
171                    removeEntry(currentEntry);
172                    currentEntry = null;
173                    expectedModCount++;
174                    // cannot poll() as that would change the expectedModCount
175                } else {
176                    throw new IllegalStateException();
177                }
178            } else {
179                throw new ConcurrentModificationException();
180            }
181        }
182    }
183
184    /**
185     * Constructs a new empty {@code WeakHashMap} instance.
186     */
187    public WeakHashMap() {
188        this(DEFAULT_SIZE);
189    }
190
191    /**
192     * Constructs a new {@code WeakHashMap} instance with the specified
193     * capacity.
194     *
195     * @param capacity
196     *            the initial capacity of this map.
197     * @throws IllegalArgumentException
198     *                if the capacity is less than zero.
199     */
200    public WeakHashMap(int capacity) {
201        if (capacity >= 0) {
202            elementCount = 0;
203            elementData = newEntryArray(capacity == 0 ? 1 : capacity);
204            loadFactor = 7500; // Default load factor of 0.75
205            computeMaxSize();
206            referenceQueue = new ReferenceQueue<K>();
207        } else {
208            throw new IllegalArgumentException();
209        }
210    }
211
212    /**
213     * Constructs a new {@code WeakHashMap} instance with the specified capacity
214     * and load factor.
215     *
216     * @param capacity
217     *            the initial capacity of this map.
218     * @param loadFactor
219     *            the initial load factor.
220     * @throws IllegalArgumentException
221     *             if the capacity is less than zero or the load factor is less
222     *             or equal to zero.
223     */
224    public WeakHashMap(int capacity, float loadFactor) {
225        if (capacity >= 0 && loadFactor > 0) {
226            elementCount = 0;
227            elementData = newEntryArray(capacity == 0 ? 1 : capacity);
228            this.loadFactor = (int) (loadFactor * 10000);
229            computeMaxSize();
230            referenceQueue = new ReferenceQueue<K>();
231        } else {
232            throw new IllegalArgumentException();
233        }
234    }
235
236    /**
237     * Constructs a new {@code WeakHashMap} instance containing the mappings
238     * from the specified map.
239     *
240     * @param map
241     *            the mappings to add.
242     */
243    public WeakHashMap(Map<? extends K, ? extends V> map) {
244        this(map.size() < 6 ? 11 : map.size() * 2);
245        putAllImpl(map);
246    }
247
248    /**
249     * Removes all mappings from this map, leaving it empty.
250     *
251     * @see #isEmpty()
252     * @see #size()
253     */
254    @Override
255    public void clear() {
256        if (elementCount > 0) {
257            elementCount = 0;
258            Arrays.fill(elementData, null);
259            modCount++;
260            while (referenceQueue.poll() != null) {
261                // do nothing
262            }
263        }
264    }
265
266    private void computeMaxSize() {
267        threshold = (int) ((long) elementData.length * loadFactor / 10000);
268    }
269
270    /**
271     * Returns whether this map contains the specified key.
272     *
273     * @param key
274     *            the key to search for.
275     * @return {@code true} if this map contains the specified key,
276     *         {@code false} otherwise.
277     */
278    @Override
279    public boolean containsKey(Object key) {
280        return getEntry(key) != null;
281    }
282
283    /**
284     * Returns a set containing all of the mappings in this map. Each mapping is
285     * an instance of {@link Map.Entry}. As the set is backed by this map,
286     * changes in one will be reflected in the other. It does not support adding
287     * operations.
288     *
289     * @return a set of the mappings.
290     */
291    @Override
292    public Set<Map.Entry<K, V>> entrySet() {
293        poll();
294        return new AbstractSet<Map.Entry<K, V>>() {
295            @Override
296            public int size() {
297                return WeakHashMap.this.size();
298            }
299
300            @Override
301            public void clear() {
302                WeakHashMap.this.clear();
303            }
304
305            @Override
306            public boolean remove(Object object) {
307                if (contains(object)) {
308                    WeakHashMap.this
309                            .remove(((Map.Entry<?, ?>) object).getKey());
310                    return true;
311                }
312                return false;
313            }
314
315            @Override
316            public boolean contains(Object object) {
317                if (object instanceof Map.Entry) {
318                    Entry<?, ?> entry = getEntry(((Map.Entry<?, ?>) object)
319                            .getKey());
320                    if (entry != null) {
321                        Object key = entry.get();
322                        if (key != null || entry.isNull) {
323                            return object.equals(entry);
324                        }
325                    }
326                }
327                return false;
328            }
329
330            @Override
331            public Iterator<Map.Entry<K, V>> iterator() {
332                return new HashIterator<Map.Entry<K, V>>(
333                        new Entry.Type<Map.Entry<K, V>, K, V>() {
334                            public Map.Entry<K, V> get(Map.Entry<K, V> entry) {
335                                return entry;
336                            }
337                        });
338            }
339        };
340    }
341
342    /**
343     * Returns a set of the keys contained in this map. The set is backed by
344     * this map so changes to one are reflected by the other. The set does not
345     * support adding.
346     *
347     * @return a set of the keys.
348     */
349    @Override
350    public Set<K> keySet() {
351        poll();
352        if (keySet == null) {
353            keySet = new AbstractSet<K>() {
354                @Override
355                public boolean contains(Object object) {
356                    return containsKey(object);
357                }
358
359                @Override
360                public int size() {
361                    return WeakHashMap.this.size();
362                }
363
364                @Override
365                public void clear() {
366                    WeakHashMap.this.clear();
367                }
368
369                @Override
370                public boolean remove(Object key) {
371                    if (containsKey(key)) {
372                        WeakHashMap.this.remove(key);
373                        return true;
374                    }
375                    return false;
376                }
377
378                @Override
379                public Iterator<K> iterator() {
380                    return new HashIterator<K>(new Entry.Type<K, K, V>() {
381                        public K get(Map.Entry<K, V> entry) {
382                            return entry.getKey();
383                        }
384                    });
385                }
386
387                @Override
388                public Object[] toArray() {
389                    Collection<K> coll = new ArrayList<K>(size());
390
391                    for (Iterator<K> iter = iterator(); iter.hasNext();) {
392                        coll.add(iter.next());
393                    }
394                    return coll.toArray();
395                }
396
397                @Override
398                public <T> T[] toArray(T[] contents) {
399                    Collection<K> coll = new ArrayList<K>(size());
400
401                    for (Iterator<K> iter = iterator(); iter.hasNext();) {
402                        coll.add(iter.next());
403                    }
404                    return coll.toArray(contents);
405                }
406            };
407        }
408        return keySet;
409    }
410
411    /**
412     * Returns a collection of the values contained in this map. The collection
413     * is backed by this map so changes to one are reflected by the other. The
414     * collection supports remove, removeAll, retainAll and clear operations,
415     * and it does not support add or addAll operations.
416     * <p>
417     * This method returns a collection which is the subclass of
418     * AbstractCollection. The iterator method of this subclass returns a
419     * "wrapper object" over the iterator of map's entrySet(). The size method
420     * wraps the map's size method and the contains method wraps the map's
421     * containsValue method.
422     * <p>
423     * The collection is created when this method is called at first time and
424     * returned in response to all subsequent calls. This method may return
425     * different Collection when multiple calls to this method, since it has no
426     * synchronization performed.
427     *
428     * @return a collection of the values contained in this map.
429     */
430    @Override
431    public Collection<V> values() {
432        poll();
433        if (valuesCollection == null) {
434            valuesCollection = new AbstractCollection<V>() {
435                @Override
436                public int size() {
437                    return WeakHashMap.this.size();
438                }
439
440                @Override
441                public void clear() {
442                    WeakHashMap.this.clear();
443                }
444
445                @Override
446                public boolean contains(Object object) {
447                    return containsValue(object);
448                }
449
450                @Override
451                public Iterator<V> iterator() {
452                    return new HashIterator<V>(new Entry.Type<V, K, V>() {
453                        public V get(Map.Entry<K, V> entry) {
454                            return entry.getValue();
455                        }
456                    });
457                }
458            };
459        }
460        return valuesCollection;
461    }
462
463    /**
464     * Returns the value of the mapping with the specified key.
465     *
466     * @param key
467     *            the key.
468     * @return the value of the mapping with the specified key, or {@code null}
469     *         if no mapping for the specified key is found.
470     */
471    @Override
472    public V get(Object key) {
473        poll();
474        if (key != null) {
475            int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
476            Entry<K, V> entry = elementData[index];
477            while (entry != null) {
478                if (key.equals(entry.get())) {
479                    return entry.value;
480                }
481                entry = entry.next;
482            }
483            return null;
484        }
485        Entry<K, V> entry = elementData[0];
486        while (entry != null) {
487            if (entry.isNull) {
488                return entry.value;
489            }
490            entry = entry.next;
491        }
492        return null;
493    }
494
495    Entry<K, V> getEntry(Object key) {
496        poll();
497        if (key != null) {
498            int index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
499            Entry<K, V> entry = elementData[index];
500            while (entry != null) {
501                if (key.equals(entry.get())) {
502                    return entry;
503                }
504                entry = entry.next;
505            }
506            return null;
507        }
508        Entry<K, V> entry = elementData[0];
509        while (entry != null) {
510            if (entry.isNull) {
511                return entry;
512            }
513            entry = entry.next;
514        }
515        return null;
516    }
517
518    /**
519     * Returns whether this map contains the specified value.
520     *
521     * @param value
522     *            the value to search for.
523     * @return {@code true} if this map contains the specified value,
524     *         {@code false} otherwise.
525     */
526    @Override
527    public boolean containsValue(Object value) {
528        poll();
529        if (value != null) {
530            for (int i = elementData.length; --i >= 0;) {
531                Entry<K, V> entry = elementData[i];
532                while (entry != null) {
533                    K key = entry.get();
534                    if ((key != null || entry.isNull)
535                            && value.equals(entry.value)) {
536                        return true;
537                    }
538                    entry = entry.next;
539                }
540            }
541        } else {
542            for (int i = elementData.length; --i >= 0;) {
543                Entry<K, V> entry = elementData[i];
544                while (entry != null) {
545                    K key = entry.get();
546                    if ((key != null || entry.isNull) && entry.value == null) {
547                        return true;
548                    }
549                    entry = entry.next;
550                }
551            }
552        }
553        return false;
554    }
555
556    /**
557     * Returns the number of elements in this map.
558     *
559     * @return the number of elements in this map.
560     */
561    @Override
562    public boolean isEmpty() {
563        return size() == 0;
564    }
565
566    @SuppressWarnings("unchecked")
567    void poll() {
568        Entry<K, V> toRemove;
569        while ((toRemove = (Entry<K, V>) referenceQueue.poll()) != null) {
570            removeEntry(toRemove);
571        }
572    }
573
574    void removeEntry(Entry<K, V> toRemove) {
575        Entry<K, V> entry, last = null;
576        int index = (toRemove.hash & 0x7FFFFFFF) % elementData.length;
577        entry = elementData[index];
578        // Ignore queued entries which cannot be found, the user could
579        // have removed them before they were queued, i.e. using clear()
580        while (entry != null) {
581            if (toRemove == entry) {
582                modCount++;
583                if (last == null) {
584                    elementData[index] = entry.next;
585                } else {
586                    last.next = entry.next;
587                }
588                elementCount--;
589                break;
590            }
591            last = entry;
592            entry = entry.next;
593        }
594    }
595
596    /**
597     * Maps the specified key to the specified value.
598     *
599     * @param key
600     *            the key.
601     * @param value
602     *            the value.
603     * @return the value of any previous mapping with the specified key or
604     *         {@code null} if there was no mapping.
605     */
606    @Override
607    public V put(K key, V value) {
608        poll();
609        int index = 0;
610        Entry<K, V> entry;
611        if (key != null) {
612            index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
613            entry = elementData[index];
614            while (entry != null && !key.equals(entry.get())) {
615                entry = entry.next;
616            }
617        } else {
618            entry = elementData[0];
619            while (entry != null && !entry.isNull) {
620                entry = entry.next;
621            }
622        }
623        if (entry == null) {
624            modCount++;
625            if (++elementCount > threshold) {
626                rehash();
627                index = key == null ? 0 : (key.hashCode() & 0x7FFFFFFF)
628                        % elementData.length;
629            }
630            entry = new Entry<K, V>(key, value, referenceQueue);
631            entry.next = elementData[index];
632            elementData[index] = entry;
633            return null;
634        }
635        V result = entry.value;
636        entry.value = value;
637        return result;
638    }
639
640    private void rehash() {
641        int length = elementData.length * 2;
642        if (length == 0) {
643            length = 1;
644        }
645        Entry<K, V>[] newData = newEntryArray(length);
646        for (int i = 0; i < elementData.length; i++) {
647            Entry<K, V> entry = elementData[i];
648            while (entry != null) {
649                int index = entry.isNull ? 0 : (entry.hash & 0x7FFFFFFF)
650                        % length;
651                Entry<K, V> next = entry.next;
652                entry.next = newData[index];
653                newData[index] = entry;
654                entry = next;
655            }
656        }
657        elementData = newData;
658        computeMaxSize();
659    }
660
661    /**
662     * Copies all the mappings in the given map to this map. These mappings will
663     * replace all mappings that this map had for any of the keys currently in
664     * the given map.
665     *
666     * @param map
667     *            the map to copy mappings from.
668     * @throws NullPointerException
669     *             if {@code map} is {@code null}.
670     */
671    @Override
672    public void putAll(Map<? extends K, ? extends V> map) {
673        putAllImpl(map);
674    }
675
676    /**
677     * Removes the mapping with the specified key from this map.
678     *
679     * @param key
680     *            the key of the mapping to remove.
681     * @return the value of the removed mapping or {@code null} if no mapping
682     *         for the specified key was found.
683     */
684    @Override
685    public V remove(Object key) {
686        poll();
687        int index = 0;
688        Entry<K, V> entry, last = null;
689        if (key != null) {
690            index = (key.hashCode() & 0x7FFFFFFF) % elementData.length;
691            entry = elementData[index];
692            while (entry != null && !key.equals(entry.get())) {
693                last = entry;
694                entry = entry.next;
695            }
696        } else {
697            entry = elementData[0];
698            while (entry != null && !entry.isNull) {
699                last = entry;
700                entry = entry.next;
701            }
702        }
703        if (entry != null) {
704            modCount++;
705            if (last == null) {
706                elementData[index] = entry.next;
707            } else {
708                last.next = entry.next;
709            }
710            elementCount--;
711            return entry.value;
712        }
713        return null;
714    }
715
716    /**
717     * Returns the number of elements in this map.
718     *
719     * @return the number of elements in this map.
720     */
721    @Override
722    public int size() {
723        poll();
724        return elementCount;
725    }
726
727    private void putAllImpl(Map<? extends K, ? extends V> map) {
728        if (map.entrySet() != null) {
729            super.putAll(map);
730        }
731    }
732}
733