EMMA Coverage Report (generated Tue Jan 19 17:53:40 UTC 2010)
[all classes][org.jomc.util]

COVERAGE SUMMARY FOR SOURCE FILE [WeakIdentityHashMap.java]

nameclass, %method, %block, %line, %
WeakIdentityHashMap.java0%   (0/8)0%   (0/61)0%   (0/1142)0%   (0/241)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class WeakIdentityHashMap0%   (0/1)0%   (0/30)0%   (0/730)0%   (0/162)
WeakIdentityHashMap (): void 0%   (0/1)0%   (0/5)0%   (0/2)
WeakIdentityHashMap (float): void 0%   (0/1)0%   (0/5)0%   (0/2)
WeakIdentityHashMap (int): void 0%   (0/1)0%   (0/5)0%   (0/2)
WeakIdentityHashMap (int, float): void 0%   (0/1)0%   (0/58)0%   (0/13)
access$400 (WeakIdentityHashMap): WeakIdentityHashMap$Entry [] 0%   (0/1)0%   (0/3)0%   (0/1)
access$500 (WeakIdentityHashMap): int 0%   (0/1)0%   (0/3)0%   (0/1)
calculateCapacity (): int 0%   (0/1)0%   (0/28)0%   (0/7)
clear (): void 0%   (0/1)0%   (0/26)0%   (0/7)
containsKey (Object): boolean 0%   (0/1)0%   (0/16)0%   (0/3)
containsValue (Object): boolean 0%   (0/1)0%   (0/38)0%   (0/8)
decreaseSize (): void 0%   (0/1)0%   (0/10)0%   (0/3)
entrySet (): Set 0%   (0/1)0%   (0/12)0%   (0/3)
equals (Object): boolean 0%   (0/1)0%   (0/23)0%   (0/5)
get (Object): Object 0%   (0/1)0%   (0/12)0%   (0/3)
getEntry (Object): WeakIdentityHashMap$Entry 0%   (0/1)0%   (0/31)0%   (0/5)
getHashCode (Object): int 0%   (0/1)0%   (0/3)0%   (0/1)
getHashTable (): WeakIdentityHashMap$Entry [] 0%   (0/1)0%   (0/63)0%   (0/15)
getHashTableIndex (int, int): int 0%   (0/1)0%   (0/6)0%   (0/1)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
increaseSize (): void 0%   (0/1)0%   (0/26)0%   (0/5)
internalString (): String 0%   (0/1)0%   (0/58)0%   (0/8)
isEmpty (): boolean 0%   (0/1)0%   (0/7)0%   (0/1)
keySet (): Set 0%   (0/1)0%   (0/12)0%   (0/3)
purge (): void 0%   (0/1)0%   (0/63)0%   (0/14)
put (Object, Object): Object 0%   (0/1)0%   (0/75)0%   (0/17)
putAll (Map): void 0%   (0/1)0%   (0/27)0%   (0/7)
remove (Object): Object 0%   (0/1)0%   (0/80)0%   (0/18)
size (): int 0%   (0/1)0%   (0/8)0%   (0/3)
toString (): String 0%   (0/1)0%   (0/11)0%   (0/1)
values (): Collection 0%   (0/1)0%   (0/12)0%   (0/3)
     
class WeakIdentityHashMap$10%   (0/1)0%   (0/3)0%   (0/15)0%   (0/3)
WeakIdentityHashMap$1 (WeakIdentityHashMap): void 0%   (0/1)0%   (0/6)0%   (0/1)
iterator (): Iterator 0%   (0/1)0%   (0/5)0%   (0/1)
size (): int 0%   (0/1)0%   (0/4)0%   (0/1)
     
class WeakIdentityHashMap$1$10%   (0/1)0%   (0/2)0%   (0/13)0%   (0/2)
WeakIdentityHashMap$1$1 (WeakIdentityHashMap$1): void 0%   (0/1)0%   (0/8)0%   (0/1)
next (): Object 0%   (0/1)0%   (0/5)0%   (0/1)
     
class WeakIdentityHashMap$20%   (0/1)0%   (0/3)0%   (0/15)0%   (0/3)
WeakIdentityHashMap$2 (WeakIdentityHashMap): void 0%   (0/1)0%   (0/6)0%   (0/1)
iterator (): Iterator 0%   (0/1)0%   (0/5)0%   (0/1)
size (): int 0%   (0/1)0%   (0/4)0%   (0/1)
     
class WeakIdentityHashMap$2$10%   (0/1)0%   (0/2)0%   (0/13)0%   (0/2)
WeakIdentityHashMap$2$1 (WeakIdentityHashMap$2): void 0%   (0/1)0%   (0/8)0%   (0/1)
next (): Object 0%   (0/1)0%   (0/5)0%   (0/1)
     
class WeakIdentityHashMap$30%   (0/1)0%   (0/3)0%   (0/16)0%   (0/3)
WeakIdentityHashMap$3 (WeakIdentityHashMap): void 0%   (0/1)0%   (0/6)0%   (0/1)
iterator (): Iterator 0%   (0/1)0%   (0/6)0%   (0/1)
size (): int 0%   (0/1)0%   (0/4)0%   (0/1)
     
class WeakIdentityHashMap$Entry0%   (0/1)0%   (0/14)0%   (0/168)0%   (0/30)
WeakIdentityHashMap$Entry (Object, Object, int, ReferenceQueue): void 0%   (0/1)0%   (0/11)0%   (0/4)
access$000 (WeakIdentityHashMap$Entry): Object 0%   (0/1)0%   (0/3)0%   (0/1)
access$002 (WeakIdentityHashMap$Entry, Object): Object 0%   (0/1)0%   (0/5)0%   (0/1)
access$100 (WeakIdentityHashMap$Entry): WeakIdentityHashMap$Entry 0%   (0/1)0%   (0/3)0%   (0/1)
access$102 (WeakIdentityHashMap$Entry, WeakIdentityHashMap$Entry): WeakIdenti... 0%   (0/1)0%   (0/5)0%   (0/1)
access$200 (WeakIdentityHashMap$Entry): int 0%   (0/1)0%   (0/3)0%   (0/1)
access$302 (WeakIdentityHashMap$Entry, boolean): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
equals (Object): boolean 0%   (0/1)0%   (0/32)0%   (0/5)
getKey (): Object 0%   (0/1)0%   (0/14)0%   (0/4)
getValue (): Object 0%   (0/1)0%   (0/13)0%   (0/3)
hashCode (): int 0%   (0/1)0%   (0/7)0%   (0/1)
internalString (): String 0%   (0/1)0%   (0/24)0%   (0/3)
setValue (Object): Object 0%   (0/1)0%   (0/32)0%   (0/8)
toString (): String 0%   (0/1)0%   (0/11)0%   (0/1)
     
class WeakIdentityHashMap$EntryIterator0%   (0/1)0%   (0/4)0%   (0/172)0%   (0/36)
WeakIdentityHashMap$EntryIterator (WeakIdentityHashMap): void 0%   (0/1)0%   (0/46)0%   (0/8)
hasNext (): boolean 0%   (0/1)0%   (0/17)0%   (0/3)
next (): Object 0%   (0/1)0%   (0/68)0%   (0/14)
remove (): void 0%   (0/1)0%   (0/41)0%   (0/11)

1/*
2 *   Copyright (c) 2009 The JOMC Project
3 *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
4 *   All rights reserved.
5 *
6 *   Redistribution and use in source and binary forms, with or without
7 *   modification, are permitted provided that the following conditions
8 *   are met:
9 *
10 *     o Redistributions of source code must retain the above copyright
11 *       notice, this list of conditions and the following disclaimer.
12 *
13 *     o Redistributions in binary form must reproduce the above copyright
14 *       notice, this list of conditions and the following disclaimer in
15 *       the documentation and/or other materials provided with the
16 *       distribution.
17 *
18 *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
19 *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
22 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 *   $Id: WeakIdentityHashMap.java 891 2009-11-02 03:40:00Z schulte2005 $
31 *
32 */
33package org.jomc.util;
34 
35import java.lang.ref.ReferenceQueue;
36import java.lang.ref.WeakReference;
37import java.util.AbstractCollection;
38import java.util.AbstractSet;
39import java.util.Collection;
40import java.util.ConcurrentModificationException;
41import java.util.Iterator;
42import java.util.Map;
43import java.util.NoSuchElementException;
44import java.util.Set;
45 
46/**
47 * Hash-table based {@code Map} implementation with weak keys, using object-identity in place of object-equality when
48 * comparing keys.
49 *
50 * <p>In a {@code WeakIdentityHashMap} two keys {@code k1} and {@code k2} are considered equal if and only if
51 * {@code k1==k2} evaluates to {@code true}. An entry will automatically be removed when its key is no longer in
52 * ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded
53 * by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its
54 * entry is effectively removed from the map, so this class behaves somewhat differently from other {@code Map}
55 * implementations and is not a general-purpose {@code Map} implementation! Although it implements the {@code Map}
56 * interface, it intentionally violates {@code Map}'s general contract, which mandates the use of the {@code equals}
57 * method when comparing objects.</p>
58 *
59 * <p>This class has performance characteristics similar to those of the {@code HashMap} class, and has the same
60 * efficiency parameters of {@code initialCapacity} and {@code loadFactor}. All of the optional map operations are
61 * provided. It does not support {@code null} values and the {@code null} key. All methods taking a key or value will
62 * throw a {@code NullPointerException} when given a {@code null} reference. No guarantees are made as to the order of
63 * the map; in particular, there is no guarantee that the order will remain constant over time. Like most collection
64 * classes, this class is not synchronized. A synchronized {@code WeakIdentityHashMap} may be constructed using the
65 * {@code Collections.synchronizedMap} method.</p>
66 *
67 * <p>The behavior of the {@code WeakIdentityHashMap} class depends in part upon the actions of the garbage collector,
68 * so several {@code Map} invariants do not hold for this class. Because the garbage collector may discard keys at
69 * any time, a {@code WeakIdentityHashMap} may behave as though an unknown thread is silently removing entries. In
70 * particular, even if synchronizing on a {@code WeakIdentityHashMap} instance and invoking none of its mutator
71 * methods, it is possible for the {@code size} method to return smaller values over time, for the {@code isEmpty}
72 * method to return {@code false} and then {@code true}, for the {@code containsKey} method to return {@code true} and
73 * later {@code false} for a given key, for the {@code get} method to return a value for a given key but later return
74 * {@code null}, for the {@code put} method to return {@code null} and the {@code remove} method to return {@code false}
75 * for a key that previously appeared to be in the map, and for successive examinations of the key set, the value
76 * collection, and the entry set to yield successively smaller numbers of elements. To protect against such situations
77 * all {@code Iterator}s and {@code Entry}s created by this class throw an {@code IllegalStateException} when a key
78 * becomes {@code null} or a mapping got removed.</p>
79 *
80 * <p>The iterators returned by the {@code iterator} method of the collections returned by all of this class's
81 * "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is
82 * created, in any way except through the iterator's own {@code remove} method, the iterator will throw a
83 * {@code ConcurrentModificationException}. Note that the fail-fast behavior of an iterator cannot be guaranteed as it
84 * is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent
85 * modification. Fail-fast iterators throw {@code ConcurrentModificationException}s on a best-effort basis. Therefore,
86 * it would be wrong to write a program that depends on this exception for its correctness: <i>the fail-fast behavior of
87 * iterators should be used only to detect bugs.</i></p>
88 *
89 * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
90 * @version $Id: WeakIdentityHashMap.java 891 2009-11-02 03:40:00Z schulte2005 $
91 *
92 * @see java.util.HashMap
93 * @see java.util.WeakHashMap
94 * @see java.util.IdentityHashMap
95 * @see java.util.Collections#synchronizedMap
96 */
97public final class WeakIdentityHashMap implements Map
98{
99 
100    /** Maximum capacity of the hash-table backing the implementation ({@code 2^30}). */
101    private static final int MAXIMUM_CAPACITY = 0x40000000;
102 
103    /** Default initial capacity ({@code 2^4}). */
104    private static final int DEFAULT_INITIAL_CAPACITY = 16;
105 
106    /** Default load factor ({@code 0.75}). */
107    private static final float DEFAULT_LOAD_FACTOR = 0.75F;
108 
109    /** The number of times the map got structurally modified. */
110    private volatile int modifications;
111 
112    /** The number of mappings held by the map. */
113    private int size;
114 
115    /** The size value at which the hash table needs resizing. */
116    private int resizeThreshold;
117 
118    /** The hash-table backing the map. */
119    private Entry[] hashTable;
120 
121    /** Queue, to which weak keys are appended to. */
122    private final ReferenceQueue referenceQueue = new ReferenceQueue();
123 
124    /** The key set view of the map. */
125    private transient Set keySet;
126 
127    /** The entry set view of the map. */
128    private transient Set entrySet;
129 
130    /** The value collection view of the map. */
131    private transient Collection valueCollection;
132 
133    /** The initial capacity of the hash table. */
134    private final int initialCapacity;
135 
136    /** The load factor for the hash table. */
137    private final float loadFactor;
138 
139    /** Null value returned by method {@link #getEntry(Object)}. */
140    private final Entry NULL_ENTRY = new Entry( null, null, 0, this.referenceQueue );
141 
142    /**
143     * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and load
144     * factor ({@code 0.75}).
145     */
146    public WeakIdentityHashMap()
147    {
148        this( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR );
149    }
150 
151    /**
152     * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the default load factor
153     * ({@code 0.75}).
154     *
155     * @param  initialCapacity The initial capacity of the {@code WeakIdentityHashMap}.
156     *
157     * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported
158     * capacity ({@code 2^30}).
159     */
160    public WeakIdentityHashMap( final int initialCapacity )
161    {
162        this( initialCapacity, DEFAULT_LOAD_FACTOR );
163    }
164 
165    /**
166     * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and the given
167     * load factor.
168     *
169     * @param loadFactor The load factor of the {@code WeakIdentityHashMap}.
170     *
171     * @throws IllegalArgumentException if {@code loadFactor} is nonpositive.
172     */
173    public WeakIdentityHashMap( final float loadFactor )
174    {
175        this( DEFAULT_INITIAL_CAPACITY, loadFactor );
176    }
177 
178    /**
179     * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the given load factor.
180     *
181     * @param initialCapacity The initial capacity of the {@code WeakIdentityHashMap}.
182     * @param loadFactor The load factor of the {@code WeakIdentityHashMap}.
183     *
184     * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported
185     * capacity ({@code 2^30}), or if {@code loadFactor} is nonpositive.
186     */
187    public WeakIdentityHashMap( final int initialCapacity, final float loadFactor )
188    {
189        if ( initialCapacity < 0 || initialCapacity > MAXIMUM_CAPACITY )
190        {
191            throw new IllegalArgumentException( Integer.toString( initialCapacity ) );
192        }
193        if ( loadFactor <= 0 || Float.isNaN( loadFactor ) )
194        {
195            throw new IllegalArgumentException( Float.toString( loadFactor ) );
196        }
197 
198        this.initialCapacity = initialCapacity;
199        this.loadFactor = loadFactor;
200        this.resizeThreshold = initialCapacity;
201        this.size = 0;
202        this.hashTable = new Entry[ initialCapacity ];
203    }
204 
205    /**
206     * Gets the number of key-value mappings in this map.
207     * <p>If the map contains more than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.</p>
208     *
209     * @return The number of key-value mappings in this map.
210     */
211    public int size()
212    {
213        if ( this.size > 0 )
214        {
215            this.purge();
216        }
217 
218        return this.size;
219    }
220 
221    /**
222     * Gets a flag indicating if this map is empty.
223     *
224     * @return {@code true} if this map contains no key-value mappings; {@code false} if this map contains at least one
225     * mapping.
226     */
227    public boolean isEmpty()
228    {
229        return this.size() == 0;
230    }
231 
232    /**
233     * Gets a flag indicating if this map contains a mapping for a given key.
234     * <p>More formally, returns {@code true} if and only if this map contains a mapping for a key {@code k} such that
235     * {@code key==k}. There can be at most one such mapping.</p>
236     *
237     * @param key The key whose presence in this map is to be tested.
238     *
239     * @return {@code true} if this map contains a mapping for {@code key}; {@code false} if this map does not contain a
240     * mapping for {@code key}.
241     *
242     * @throws NullPointerException if {@code key} is {@code null}.
243     */
244    public boolean containsKey( final Object key )
245    {
246        if ( key == null )
247        {
248            throw new NullPointerException( "key" );
249        }
250 
251        return this.getEntry( key ).value != null;
252    }
253 
254    /**
255     * Gets a flag indicating if this map maps one or more keys to the specified value.
256     * <p>More formally, this method returns {@code true} if and only if this map contains at least one mapping to a
257     * value {@code v} such that {@code value.equals(v)}. This operation requires time linear in the map size.</p>
258     *
259     * @param value The value whose presence in this map is to be tested.
260     *
261     * @return {@code true} if this map maps one or more keys to {@code value}; {@code false} if this map does not map
262     * any key to {@code value}.
263     *
264     * @throws NullPointerException if {@code value} is {@code null}.
265     */
266    public boolean containsValue( final Object value )
267    {
268        if ( value == null )
269        {
270            throw new NullPointerException( "value" );
271        }
272 
273        final Entry[] table = this.getHashTable();
274 
275        for ( int i = table.length - 1; i >= 0; i-- )
276        {
277            for ( Entry e = table[i]; e != null; e = e.next )
278            {
279                if ( value.equals( e.value ) )
280                {
281                    return true;
282                }
283            }
284        }
285 
286        return false;
287    }
288 
289    /**
290     * Gets the value to which a given key is mapped, or {@code null} if this map contains no mapping for that key.
291     * <p>More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that
292     * {@code key==k}, then this method returns {@code v}; otherwise it returns {@code null}. There can be at most one
293     * such mapping.</p>
294     *
295     * @param key The key whose associated value is to be returned.
296     *
297     * @return the value to which {@code key} is mapped, or {@code null} if this map contains no mapping for
298     * {@code key}.
299     *
300     * @throws NullPointerException if {@code key} is {@code null}.
301     */
302    public Object get( final Object key )
303    {
304        if ( key == null )
305        {
306            throw new NullPointerException( "key" );
307        }
308 
309        return this.getEntry( key ).value;
310    }
311 
312    /**
313     * Associates a given value with a given key in this map.
314     * <p>If the map previously contained a mapping for that key, the old value is replaced by the given value.</p>
315     *
316     * @param key The key with which {@code value} is to be associated.
317     * @param value The value to be associated with {@code key}.
318     *
319     * @return the value previously associated with {@code key}, or {@code null} if there was no mapping for
320     * {@code key}.
321     *
322     * @throws NullPointerException if {@code key} or {@code value} is {@code null}.
323     */
324    public Object put( final Object key, final Object value )
325    {
326        if ( key == null )
327        {
328            throw new NullPointerException( "key" );
329        }
330        if ( value == null )
331        {
332            throw new NullPointerException( "value" );
333        }
334 
335        final int hashCode = getHashCode( key );
336        final Entry[] table = this.getHashTable();
337        final int index = getHashTableIndex( hashCode, table.length );
338 
339        for ( Entry e = table[index]; e != null; e = e.next )
340        {
341            if ( e.hashCode == hashCode && e.get() == key )
342            {
343                final Object oldValue = e.value;
344                e.value = value;
345                return oldValue;
346            }
347        }
348 
349        final Entry entry = new Entry( key, value, hashCode, this.referenceQueue );
350        entry.next = table[index];
351        table[index] = entry;
352 
353        this.increaseSize();
354 
355        return null;
356    }
357 
358    /**
359     * Removes the mapping for a given key from this map if it is present.
360     * <p>More formally, if this map contains a mapping from key {@code k} to value {@code v} such that {@code key==k},
361     * that mapping is removed. The map can contain at most one such mapping. The map will not contain a mapping for the
362     * given key once the call returns.</p>
363     *
364     * @param key The key whose mapping is to be removed from the map.
365     *
366     * @return the value previously associated with {@code key}, or {@code null} if there was no mapping for
367     * {@code key}.
368     *
369     * @throws NullPointerException if {@code key} is {@code null}.
370     */
371    public Object remove( final Object key )
372    {
373        if ( key == null )
374        {
375            throw new NullPointerException( "key" );
376        }
377 
378        final Entry[] table = this.getHashTable();
379        final int hashCode = getHashCode( key );
380        final int index = getHashTableIndex( hashCode, table.length );
381 
382        for ( Entry e = table[index], pre = null; e != null; pre = e, e = e.next )
383        {
384            if ( e.hashCode == hashCode && e.get() == key )
385            {
386                if ( pre != null )
387                {
388                    pre.next = e.next;
389                }
390                else
391                {
392                    table[index] = e.next;
393                }
394 
395                this.decreaseSize();
396                this.modifications++;
397 
398                final Object removed = e.value;
399                e.removed = true;
400                e.value = null;
401                e.next = null;
402                return removed;
403            }
404        }
405 
406        return null;
407    }
408 
409    /**
410     * Copies all of the mappings from a given map to this map.
411     * <p>The effect of this call is equivalent to that of calling {@link #put(Object,Object) put(k, v)} on this map
412     * once for each mapping from key {@code k} to value {@code v} in the given map. The behavior of this operation is
413     * undefined if the given map is modified while the operation is in progress.</p>
414     *
415     * @param m The mappings to be stored in this map.
416     *
417     * @throws NullPointerException if {@code map} is {@code null}, or if {@code map} contains {@code null} keys or
418     * values.
419     */
420    public void putAll( final Map m )
421    {
422        if ( m == null )
423        {
424            throw new NullPointerException( "m" );
425        }
426 
427        for ( final Iterator it = m.entrySet().iterator(); it.hasNext(); )
428        {
429            final Map.Entry entry = (Map.Entry) it.next();
430            this.put( entry.getKey(), entry.getValue() );
431        }
432    }
433 
434    /** Removes all of the mappings from this map so that the map will be empty after this call returns. */
435    @SuppressWarnings( "empty-statement" )
436    public void clear()
437    {
438        this.purge();
439        this.hashTable = new Entry[ this.initialCapacity ];
440        this.size = 0;
441        this.resizeThreshold = this.initialCapacity;
442        this.modifications++;
443        while ( this.referenceQueue.poll() != null );
444    }
445 
446    /**
447     * Gets a {@code Set} view of the keys contained in this map.
448     * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is
449     * modified while an iteration over the set is in progress (except through the iterator's own {@code remove}
450     * operation), the results of the iteration are undefined, that is, the iterator may throw an
451     * {@code IllegalStateException}. The set supports element removal, which removes the corresponding mapping from the
452     * map, via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code retainAll}, and {@code clear}
453     * operations. It does not support the {@code add} or {@code addAll} operations.</p>
454     *
455     * @return a set view of the keys contained in this map.
456     */
457    public Set keySet()
458    {
459        if ( this.keySet == null )
460        {
461            this.keySet = new AbstractSet()
462            {
463 
464                public Iterator iterator()
465                {
466                    return new EntryIterator()
467                    {
468 
469                        @Override
470                        public Object next()
471                        {
472                            return ( (Entry) super.next() ).getKey();
473                        }
474 
475                    };
476                }
477 
478                public int size()
479                {
480                    return WeakIdentityHashMap.this.size();
481                }
482 
483            };
484 
485        }
486 
487        return this.keySet;
488    }
489 
490    /**
491     * Gets a {@code Collection} view of the values contained in this map.
492     * <p>The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
493     * If the map is modified while an iteration over the collection is in progress (except through the iterator's own
494     * {@code remove} operation), the results of the iteration are undefined, that is, the iterator may throw an
495     * {@code IllegalStateException}. The collection supports element removal, which removes the corresponding mapping
496     * from the map, via the {@code Iterator.remove}, {@code Collection.remove}, {@code removeAll}, {@code retainAll}
497     * and {@code clear} operations. It does not support the {@code add} or {@code addAll} operations.</p>
498     *
499     * @return a collection view of the values contained in this map.
500     */
501    public Collection values()
502    {
503        if ( this.valueCollection == null )
504        {
505            this.valueCollection = new AbstractCollection()
506            {
507 
508                public Iterator iterator()
509                {
510                    return new EntryIterator()
511                    {
512 
513                        @Override
514                        public Object next()
515                        {
516                            return ( (Entry) super.next() ).getValue();
517                        }
518 
519                    };
520                }
521 
522                public int size()
523                {
524                    return WeakIdentityHashMap.this.size();
525                }
526 
527            };
528        }
529 
530        return this.valueCollection;
531    }
532 
533    /**
534     * Gets a {@code Set} view of the mappings contained in this map.
535     * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is
536     * modified while an iteration over the set is in progress (except through the iterator's own {@code remove}
537     * operation, or through the {@code setValue} operation on a map entry returned by the iterator) the results of the
538     * iteration are undefined, that is, the iterator may throw an {@code IllegalStateException}. The set supports
539     * element removal, which removes the corresponding mapping from the map, via the {@code Iterator.remove},
540     * {@code Set.remove}, {@code removeAll}, {@code retainAll} and {@code clear} operations. It does not support the
541     * {@code add} or {@code addAll} operations.</p>
542     *
543     * @return a set view of the mappings contained in this map.
544     */
545    public Set entrySet()
546    {
547        if ( this.entrySet == null )
548        {
549            this.entrySet = new AbstractSet()
550            {
551 
552                public Iterator iterator()
553                {
554                    return new EntryIterator();
555                }
556 
557                public int size()
558                {
559                    return WeakIdentityHashMap.this.size();
560                }
561 
562            };
563        }
564 
565        return this.entrySet;
566    }
567 
568    /**
569     * Returns a string representation of the object.
570     *
571     * @return a string representation of the object.
572     */
573    @Override
574    public String toString()
575    {
576        return super.toString() + this.internalString();
577    }
578 
579    /**
580     * Compares the specified object with this map for equality.
581     * <p>Returns {@code true} if the given object is also a map and the two maps represent the same mappings. More
582     * formally, two maps {@code m1} and {@code m2} represent the same mappings if
583     * {@code m1.entrySet().equals(m2.entrySet())}.</p>
584     *
585     * @param o The object to be compared for equality with this map.
586     *
587     * @return {@code true} if {@code o} is equal to this map; {@code false} if {@code o} is not equal to this map.
588     */
589    @Override
590    public boolean equals( final Object o )
591    {
592        boolean equal = this == o;
593 
594        if ( !equal && o instanceof Map )
595        {
596            final Map that = (Map) o;
597            equal = this.entrySet().equals( that.entrySet() );
598        }
599 
600        return equal;
601    }
602 
603    /**
604     * Gets the hash code value for this map.
605     * <p>The hash code of a map is defined to be the sum of the hash codes of each entry in the map's
606     * {@code entrySet()} view.</p>
607     *
608     * @return the hash code value for this map.
609     */
610    @Override
611    public int hashCode()
612    {
613        return this.entrySet().hashCode();
614    }
615 
616    /**
617     * Creates a string representing the mappings of the instance.
618     *
619     * @return a string representing the mappings of the instance.
620     */
621    private String internalString()
622    {
623        final StringBuilder buf = new StringBuilder( 12 * this.size() ).append( '{' );
624        final Entry[] table = this.getHashTable();
625        for ( int i = table.length - 1, index = 0; i >= 0; i-- )
626        {
627            for ( Entry e = table[i]; e != null; e = e.next )
628            {
629                if ( buf.length() > 1 )
630                {
631                    buf.append( ", " );
632                }
633 
634                buf.append( '[' ).append( index++ ).append( "]=" ).append( e );
635            }
636        }
637 
638        return buf.append( '}' ).toString();
639    }
640 
641    /**
642     * Gets a hash-code value for a given key.
643     *
644     * @param key The key to get a hash-code value for.
645     *
646     * @return Hash-code value of {@code key}.
647     */
648    private static int getHashCode( final Object key )
649    {
650        return System.identityHashCode( key );
651    }
652 
653    /**
654     * Gets the index of a hash code value.
655     *
656     * @param hashCode The hash code value to return the index of.
657     * @param capacity The capacity to return an index for.
658     *
659     * @return the index of {@code hashCode} for {@code capacity}.
660     */
661    private static int getHashTableIndex( final int hashCode, final int capacity )
662    {
663        return hashCode & ( capacity - 1 );
664    }
665 
666    /**
667     * Gets the hash-table backing the instance.
668     * <p>This method creates a new hash-table and re-hashes any mappings whenever the size of the map gets greater than
669     * the capacity of the internal hash-table times the load factor value.</p>
670     *
671     * @return the hash-table backing the instance.
672     */
673    private Entry[] getHashTable()
674    {
675        if ( this.hashTable.length < this.resizeThreshold )
676        {
677            final Entry[] table = new Entry[ this.calculateCapacity() ];
678 
679            for ( int i = this.hashTable.length - 1; i >= 0; i-- )
680            {
681                Entry entry = this.hashTable[i];
682 
683                while ( entry != null )
684                {
685                    final Entry next = entry.next;
686                    final int index = getHashTableIndex( entry.hashCode, table.length );
687 
688                    entry.next = table[index];
689                    table[index] = entry;
690                    entry = next;
691                }
692            }
693 
694            this.hashTable = table;
695            this.modifications++;
696        }
697 
698        this.purge();
699        return this.hashTable;
700    }
701 
702    /** Removes any garbage collected entries. */
703    private void purge()
704    {
705        Entry purge;
706 
707        while ( ( purge = (Entry) this.referenceQueue.poll() ) != null )
708        {
709            final int index = getHashTableIndex( purge.hashCode, this.hashTable.length );
710 
711            for ( Entry e = this.hashTable[index], pre = null; e != null; pre = e, e = e.next )
712            {
713                if ( e == purge )
714                {
715                    if ( pre != null )
716                    {
717                        pre.next = purge.next;
718                    }
719                    else
720                    {
721                        this.hashTable[index] = purge.next;
722                    }
723 
724                    purge.removed = true;
725                    purge.next = null;
726                    purge.value = null;
727 
728                    this.decreaseSize();
729 
730                    break;
731                }
732            }
733        }
734    }
735 
736    private void increaseSize()
737    {
738        if ( this.size < Integer.MAX_VALUE )
739        {
740            this.size++;
741            this.resizeThreshold = (int) ( this.size * this.loadFactor );
742        }
743 
744        this.modifications++;
745    }
746 
747    private void decreaseSize()
748    {
749        if ( this.size > 0 )
750        {
751            this.size--;
752        }
753    }
754 
755    private int calculateCapacity()
756    {
757        int maxCapacity = this.initialCapacity;
758        if ( maxCapacity < this.resizeThreshold )
759        {
760            maxCapacity = this.resizeThreshold > MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : this.resizeThreshold;
761        }
762 
763        int capacity = 1;
764        while ( capacity < maxCapacity )
765        {
766            capacity <<= 1;
767        }
768 
769        return capacity;
770    }
771 
772    private Entry getEntry( final Object key )
773    {
774        final int hashCode = getHashCode( key );
775        for ( Entry e = this.hashTable[getHashTableIndex( hashCode, this.hashTable.length )]; e != null; e = e.next )
776        {
777            if ( e.hashCode == hashCode && e.get() == key )
778            {
779                return e;
780            }
781        }
782 
783        return NULL_ENTRY;
784    }
785 
786    /**
787     * A map entry (key-value pair) with weakly referenced key.
788     * <p>The {@code WeakIdentityHashMap.entrySet} method returns a collection-view of the map, whose elements are of
789     * this class. The only way to obtain a reference to a map entry is from the iterator of this collection-view. These
790     * {@code Map.Entry} objects are valid only for the duration of the iteration; more formally, the behavior of a map
791     * entry is undefined if the backing map has been modified after the entry was returned by the iterator, except
792     * through the {@code setValue} operation on the map entry.</p>
793     *
794     * @see WeakIdentityHashMap#entrySet()
795     */
796    private static class Entry extends WeakReference implements Map.Entry
797    {
798 
799        /** The value of the entry. */
800        private Object value;
801 
802        /** The next entry in the bucket. */
803        private Entry next;
804 
805        /** Flag indicating that this entry got removed from the map. */
806        private boolean removed;
807 
808        /** The hash code value of the key. */
809        private final int hashCode;
810 
811        Entry( final Object key, final Object value, final int hashCode, final ReferenceQueue queue )
812        {
813            super( key, queue );
814            this.hashCode = hashCode;
815            this.value = value;
816        }
817 
818        /**
819         * Gets the key corresponding to this entry.
820         *
821         * @return The key corresponding to this entry.
822         *
823         * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's
824         * {@code remove} operation or due to the key having been garbage collected).
825         */
826        public Object getKey()
827        {
828            final Object key = this.get();
829 
830            if ( key == null || this.removed )
831            {
832                throw new IllegalStateException();
833            }
834 
835            return key;
836        }
837 
838        /**
839         * Gets the value corresponding to this entry.
840         *
841         * @return the value corresponding to this entry.
842         *
843         * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's
844         * {@code remove} operation or due to the key having been garbage collected).
845         */
846        public Object getValue()
847        {
848            if ( this.get() == null || this.removed )
849            {
850                throw new IllegalStateException();
851            }
852 
853            return this.value;
854        }
855 
856        /**
857         * Replaces the value corresponding to this entry with the specified value.
858         *
859         * @param value The new value to be stored in this entry.
860         *
861         * @return old value corresponding to the entry.
862         *
863         * @throws NullPointerException if {@code value} is {@code null}.
864         * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's
865         * {@code remove} operation or due to the key having been garbage collected).
866         */
867        public Object setValue( final Object value )
868        {
869            if ( value == null )
870            {
871                throw new NullPointerException( "value" );
872            }
873            if ( this.get() == null || this.removed )
874            {
875                throw new IllegalStateException();
876            }
877 
878            final Object oldValue = this.getValue();
879 
880            if ( value != oldValue && !value.equals( oldValue ) )
881            {
882                this.value = value;
883            }
884 
885            return oldValue;
886        }
887 
888        /**
889         * Returns a string representation of the object.
890         *
891         * @return a string representation of the object.
892         */
893        @Override
894        public String toString()
895        {
896            return super.toString() + this.internalString();
897        }
898 
899        /**
900         * Compares a given object with this entry for equality.
901         * <p>Returns {@code true} if the given object is also a map entry and the two entries represent the same
902         * mapping. More formally, two entries {@code e1} and {@code e2} represent the same mapping if
903         * <pre><blockquote>
904         * ( e1.getKey() == e2.getKey() )  &amp;&amp;
905         * ( e1.getValue().equals( e2.getValue() ) )
906         * </blockquote></pre></p>
907         *
908         * @param o The object to be compared for equality with this map entry.
909         *
910         * @return {@code true} if {@code o} is equal to this map entry; {@code false} if {@code o} is not equal to this
911         * map entry.
912         */
913        @Override
914        public boolean equals( final Object o )
915        {
916            boolean equal = this == o;
917 
918            if ( !equal && o instanceof Map.Entry )
919            {
920                final Map.Entry that = (Map.Entry) o;
921                equal = this.getKey() == that.getKey() && this.getValue().equals( that.getValue() );
922            }
923 
924            return equal;
925        }
926 
927        /**
928         * Gets the hash code value for this map entry.
929         * <p>The hash code of a map entry {@code e} is defined to be:
930         * <pre><blockquote>
931         * ( e.getKey() == null ? 0 : e.getKey().hashCode() ) ^
932         * ( e.getValue() == null ? 0 : e.getValue().hashCode() )
933         * </blockquote></pre></p>
934         *
935         * @return the hash code value for this map entry.
936         */
937        @Override
938        public int hashCode()
939        {
940            return ( this.hashCode ) ^ ( this.getValue().hashCode() );
941        }
942 
943        /**
944         * Creates a string representing the properties of the instance.
945         *
946         * @return a string representing the properties of the instance.
947         */
948        private String internalString()
949        {
950            final StringBuilder buf = new StringBuilder( 50 ).append( '{' );
951            buf.append( "key=" ).append( this.getKey() ).append( ", value=" ).append( this.getValue() );
952            return buf.append( '}' ).toString();
953        }
954 
955    }
956 
957    /** An iterator over the hash-table backing the implementation. */
958    private class EntryIterator implements Iterator
959    {
960 
961        /** The next element in the iteration. */
962        private Entry next;
963 
964        /** The current element in the iteration. */
965        private Entry current;
966 
967        /** The current index into the hash-table. */
968        private int index;
969 
970        /** The number of modifications when this iterator got created. */
971        private int modifications;
972 
973        /** Creates a new {@code EntryIterator} instance. */
974        EntryIterator()
975        {
976            final Entry[] table = getHashTable();
977            for ( this.index = table.length - 1; this.index >= 0; this.index-- )
978            {
979                if ( table[this.index] != null )
980                {
981                    this.next = table[this.index--];
982                    break;
983                }
984            }
985 
986            this.modifications = WeakIdentityHashMap.this.modifications;
987        }
988 
989        /**
990         * Gets a flag indicating that the iteration has more elements.
991         *
992         * @return {@code true} if the iterator has more elements; {@code false} if the iterator does not have more
993         * elements.
994         */
995        public boolean hasNext()
996        {
997            if ( this.modifications != WeakIdentityHashMap.this.modifications )
998            {
999                throw new ConcurrentModificationException();
1000            }
1001 
1002            return this.next != null;
1003        }
1004 
1005        /**
1006         * Gets the next element in the iteration.
1007         *
1008         * @return the next element in the iteration.
1009         *
1010         * @throws NoSuchElementException if the iterator does not have more elements.
1011         */
1012        public Object next()
1013        {
1014            if ( this.modifications != WeakIdentityHashMap.this.modifications )
1015            {
1016                throw new ConcurrentModificationException();
1017            }
1018            if ( this.next == null )
1019            {
1020                throw new NoSuchElementException();
1021            }
1022 
1023            this.current = this.next;
1024 
1025            if ( this.next.next != null )
1026            {
1027                this.next = this.next.next;
1028            }
1029            else
1030            {
1031                this.next = null;
1032                final Entry[] table = getHashTable();
1033                for ( ; this.index >= 0; this.index-- )
1034                {
1035                    if ( table[this.index] != null )
1036                    {
1037                        this.next = table[this.index--];
1038                        break;
1039                    }
1040                }
1041            }
1042 
1043            return this.current;
1044        }
1045 
1046        /**
1047         * Removes from the underlying hash-table the last element returned by the iterator.
1048         *
1049         * @throws IllegalStateException if the {@code next} method has not yet been called, or the {@code remove}
1050         * method has already been called after the last call to the {@code next} method.
1051         */
1052        public void remove()
1053        {
1054            if ( this.modifications != WeakIdentityHashMap.this.modifications )
1055            {
1056                throw new ConcurrentModificationException();
1057            }
1058            if ( this.current == null )
1059            {
1060                throw new IllegalStateException();
1061            }
1062 
1063            final Object key = this.current.getKey();
1064 
1065            if ( key == null )
1066            {
1067                throw new IllegalStateException();
1068            }
1069 
1070            WeakIdentityHashMap.this.remove( key );
1071            this.modifications = WeakIdentityHashMap.this.modifications;
1072            this.current = null;
1073        }
1074 
1075    }
1076 
1077}

[all classes][org.jomc.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov