001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.map;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.lang.ref.Reference;
024
025/**
026 * A <code>Map</code> implementation that allows mappings to be
027 * removed by the garbage collector and matches keys and values based
028 * on <code>==</code> not <code>equals()</code>.
029 * <p>
030 * <p>
031 * When you construct a <code>ReferenceIdentityMap</code>, you can specify what kind
032 * of references are used to store the map's keys and values.
033 * If non-hard references are used, then the garbage collector can remove
034 * mappings if a key or value becomes unreachable, or if the JVM's memory is
035 * running low. For information on how the different reference types behave,
036 * see {@link Reference}.
037 * <p>
038 * Different types of references can be specified for keys and values.
039 * The default constructor uses hard keys and soft values, providing a
040 * memory-sensitive cache.
041 * <p>
042 * This map is similar to
043 * {@link org.apache.commons.collections4.map.ReferenceMap ReferenceMap}.
044 * It differs in that keys and values in this class are compared using <code>==</code>.
045 * <p>
046 * This map will violate the detail of various Map and map view contracts.
047 * As a general rule, don't compare this map to other maps.
048 * <p>
049 * This {@link java.util.Map Map} implementation does <i>not</i> allow null elements.
050 * Attempting to add a null key or value to the map will raise a <code>NullPointerException</code>.
051 * <p>
052 * This implementation is not synchronized.
053 * You can use {@link java.util.Collections#synchronizedMap} to
054 * provide synchronized access to a <code>ReferenceIdentityMap</code>.
055 * Remember that synchronization will not stop the garbage collector removing entries.
056 * <p>
057 * All the available iterators can be reset back to the start by casting to
058 * <code>ResettableIterator</code> and calling <code>reset()</code>.
059 * <p>
060 * <strong>Note that ReferenceIdentityMap is not synchronized and is not thread-safe.</strong>
061 * If you wish to use this map from multiple threads concurrently, you must use
062 * appropriate synchronization. The simplest approach is to wrap this map
063 * using {@link java.util.Collections#synchronizedMap}. This class may throw
064 * exceptions when accessed by concurrent threads without synchronization.
065 *
066 * @see java.lang.ref.Reference
067 *
068 * @since 3.0 (previously in main package v2.1)
069 * @version $Id: ReferenceIdentityMap.html 972421 2015-11-14 20:00:04Z tn $
070 */
071public class ReferenceIdentityMap<K, V> extends AbstractReferenceMap<K, V> implements Serializable {
072
073    /** Serialization version */
074    private static final long serialVersionUID = -1266190134568365852L;
075
076    /**
077     * Constructs a new <code>ReferenceIdentityMap</code> that will
078     * use hard references to keys and soft references to values.
079     */
080    public ReferenceIdentityMap() {
081        super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY,
082                DEFAULT_LOAD_FACTOR, false);
083    }
084
085    /**
086     * Constructs a new <code>ReferenceIdentityMap</code> that will
087     * use the specified types of references.
088     *
089     * @param keyType  the type of reference to use for keys;
090     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
091     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
092     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
093     * @param valueType  the type of reference to use for values;
094     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
095     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
096     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
097     */
098    public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType) {
099        super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false);
100    }
101
102    /**
103     * Constructs a new <code>ReferenceIdentityMap</code> that will
104     * use the specified types of references.
105     *
106     * @param keyType  the type of reference to use for keys;
107     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
108     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
109     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
110     * @param valueType  the type of reference to use for values;
111     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
112     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
113     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
114     * @param purgeValues should the value be automatically purged when the
115     *   key is garbage collected
116     */
117    public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
118            final boolean purgeValues) {
119        super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues);
120    }
121
122    /**
123     * Constructs a new <code>ReferenceIdentityMap</code> with the
124     * specified reference types, load factor and initial capacity.
125     *
126     * @param keyType  the type of reference to use for keys;
127     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
128     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
129     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
130     * @param valueType  the type of reference to use for values;
131     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
132     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
133     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
134     * @param capacity  the initial capacity for the map
135     * @param loadFactor  the load factor for the map
136     */
137    public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
138            final int capacity, final float loadFactor) {
139        super(keyType, valueType, capacity, loadFactor, false);
140    }
141
142    /**
143     * Constructs a new <code>ReferenceIdentityMap</code> with the
144     * specified reference types, load factor and initial capacity.
145     *
146     * @param keyType  the type of reference to use for keys;
147     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
148     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
149     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
150     * @param valueType  the type of reference to use for values;
151     *   must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
152     *   {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
153     *   {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
154     * @param capacity  the initial capacity for the map
155     * @param loadFactor  the load factor for the map
156     * @param purgeValues  should the value be automatically purged when the
157     *   key is garbage collected
158     */
159    public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
160            final int capacity, final float loadFactor, final boolean purgeValues) {
161        super(keyType, valueType, capacity, loadFactor, purgeValues);
162    }
163
164    //-----------------------------------------------------------------------
165    /**
166     * Gets the hash code for the key specified.
167     * <p>
168     * This implementation uses the identity hash code.
169     *
170     * @param key  the key to get a hash code for
171     * @return the hash code
172     */
173    @Override
174    protected int hash(final Object key) {
175        return System.identityHashCode(key);
176    }
177
178    /**
179     * Gets the hash code for a MapEntry.
180     * <p>
181     * This implementation uses the identity hash code.
182     *
183     * @param key  the key to get a hash code for, may be null
184     * @param value  the value to get a hash code for, may be null
185     * @return the hash code, as per the MapEntry specification
186     */
187    @Override
188    protected int hashEntry(final Object key, final Object value) {
189        return System.identityHashCode(key) ^
190               System.identityHashCode(value);
191    }
192
193    /**
194     * Compares two keys for equals.
195     * <p>
196     * This implementation converts the key from the entry to a real reference
197     * before comparison and uses <code>==</code>.
198     *
199     * @param key1  the first key to compare passed in from outside
200     * @param key2  the second key extracted from the entry via <code>entry.key</code>
201     * @return true if equal by identity
202     */
203    @Override
204    protected boolean isEqualKey(final Object key1, Object key2) {
205        key2 = isKeyType(ReferenceStrength.HARD) ? key2 : ((Reference<?>) key2).get();
206        return key1 == key2;
207    }
208
209    /**
210     * Compares two values for equals.
211     * <p>
212     * This implementation uses <code>==</code>.
213     *
214     * @param value1  the first value to compare passed in from outside
215     * @param value2  the second value extracted from the entry via <code>getValue()</code>
216     * @return true if equal by identity
217     */
218    @Override
219    protected boolean isEqualValue(final Object value1, final Object value2) {
220        return value1 == value2;
221    }
222
223    //-----------------------------------------------------------------------
224    /**
225     * Write the map out using a custom routine.
226     */
227    private void writeObject(final ObjectOutputStream out) throws IOException {
228        out.defaultWriteObject();
229        doWriteObject(out);
230    }
231
232    /**
233     * Read the map in using a custom routine.
234     */
235    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
236        in.defaultReadObject();
237        doReadObject(in);
238    }
239
240}