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 * https://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 package org.apache.commons.collections4.map;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.lang.ref.Reference;
24 import java.util.Map;
25
26 /**
27 * A {@code Map} implementation that allows mappings to be
28 * removed by the garbage collector and matches keys and values based
29 * on {@code ==} not {@code equals()}.
30 * <p>
31 * When you construct a {@code ReferenceIdentityMap}, you can specify what kind
32 * of references are used to store the map's keys and values.
33 * If non-hard references are used, then the garbage collector can remove
34 * mappings if a key or value becomes unreachable, or if the JVM's memory is
35 * running low. For information on how the different reference types behave,
36 * see {@link Reference}.
37 * </p>
38 * <p>
39 * Different types of references can be specified for keys and values.
40 * The default constructor uses hard keys and soft values, providing a
41 * memory-sensitive cache.
42 * </p>
43 * <p>
44 * This map is similar to
45 * {@link ReferenceMap ReferenceMap}.
46 * It differs in that keys and values in this class are compared using {@code ==}.
47 * </p>
48 * <p>
49 * This map will violate the detail of various Map and map view contracts.
50 * As a general rule, don't compare this map to other maps.
51 * </p>
52 * <p>
53 * This {@link Map Map} implementation does <em>not</em> allow null elements.
54 * Attempting to add a null key or value to the map will raise a {@code NullPointerException}.
55 * </p>
56 * <p>
57 * This implementation is not synchronized.
58 * You can use {@link java.util.Collections#synchronizedMap} to
59 * provide synchronized access to a {@code ReferenceIdentityMap}.
60 * Remember that synchronization will not stop the garbage collector removing entries.
61 * </p>
62 * <p>
63 * All the available iterators can be reset back to the start by casting to
64 * {@code ResettableIterator} and calling {@code reset()}.
65 * </p>
66 * <p>
67 * <strong>Note that ReferenceIdentityMap is not synchronized and is not thread-safe.</strong>
68 * If you wish to use this map from multiple threads concurrently, you must use
69 * appropriate synchronization. The simplest approach is to wrap this map
70 * using {@link java.util.Collections#synchronizedMap}. This class may throw
71 * exceptions when accessed by concurrent threads without synchronization.
72 * </p>
73 *
74 * @param <K> The type of the keys in this map
75 * @param <V> The type of the values in this map
76 * @see java.lang.ref.Reference
77 * @since 3.0 (previously in main package v2.1)
78 */
79 public class ReferenceIdentityMap<K, V> extends AbstractReferenceMap<K, V> implements Serializable {
80
81 /** Serialization version */
82 private static final long serialVersionUID = -1266190134568365852L;
83
84 /**
85 * Constructs a new {@code ReferenceIdentityMap} that will
86 * use hard references to keys and soft references to values.
87 */
88 public ReferenceIdentityMap() {
89 super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY,
90 DEFAULT_LOAD_FACTOR, false);
91 }
92
93 /**
94 * Constructs a new {@code ReferenceIdentityMap} that will
95 * use the specified types of references.
96 *
97 * @param keyType The type of reference to use for keys;
98 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
99 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
100 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
101 * @param valueType The type of reference to use for values;
102 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
103 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
104 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
105 */
106 public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType) {
107 super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false);
108 }
109
110 /**
111 * Constructs a new {@code ReferenceIdentityMap} that will
112 * use the specified types of references.
113 *
114 * @param keyType The type of reference to use for keys;
115 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
116 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
117 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
118 * @param valueType The type of reference to use for values;
119 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
120 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
121 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
122 * @param purgeValues should the value be automatically purged when the
123 * key is garbage collected
124 */
125 public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
126 final boolean purgeValues) {
127 super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues);
128 }
129
130 /**
131 * Constructs a new {@code ReferenceIdentityMap} with the
132 * specified reference types, load factor and initial capacity.
133 *
134 * @param keyType The type of reference to use for keys;
135 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
136 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
137 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
138 * @param valueType The type of reference to use for values;
139 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
140 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
141 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
142 * @param capacity The initial capacity for the map
143 * @param loadFactor The load factor for the map
144 */
145 public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
146 final int capacity, final float loadFactor) {
147 super(keyType, valueType, capacity, loadFactor, false);
148 }
149
150 /**
151 * Constructs a new {@code ReferenceIdentityMap} with the
152 * specified reference types, load factor and initial capacity.
153 *
154 * @param keyType The type of reference to use for keys;
155 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
156 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
157 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
158 * @param valueType The type of reference to use for values;
159 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
160 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
161 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
162 * @param capacity The initial capacity for the map
163 * @param loadFactor The load factor for the map
164 * @param purgeValues should the value be automatically purged when the
165 * key is garbage collected
166 */
167 public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType,
168 final int capacity, final float loadFactor, final boolean purgeValues) {
169 super(keyType, valueType, capacity, loadFactor, purgeValues);
170 }
171
172 /**
173 * Gets the hash code for the key specified.
174 * <p>
175 * This implementation uses the identity hash code.
176 * </p>
177 *
178 * @param key The key to get a hash code for
179 * @return The hash code
180 */
181 @Override
182 protected int hash(final Object key) {
183 return System.identityHashCode(key);
184 }
185
186 /**
187 * Gets the hash code for a MapEntry.
188 * <p>
189 * This implementation uses the identity hash code.
190 * </p>
191 *
192 * @param key The key to get a hash code for, may be null
193 * @param value The value to get a hash code for, may be null
194 * @return The hash code, as per the MapEntry specification
195 */
196 @Override
197 protected int hashEntry(final Object key, final Object value) {
198 return System.identityHashCode(key) ^
199 System.identityHashCode(value);
200 }
201
202 /**
203 * Compares two keys for equals.
204 * <p>
205 * This implementation converts the key from the entry to a real reference
206 * before comparison and uses {@code ==}.
207 * </p>
208 *
209 * @param key1 The first key to compare passed in from outside
210 * @param key2 The second key extracted from the entry via {@code entry.key}
211 * @return true if equal by identity
212 */
213 @Override
214 protected boolean isEqualKey(final Object key1, Object key2) {
215 key2 = isKeyType(ReferenceStrength.HARD) ? key2 : ((Reference<?>) key2).get();
216 return key1 == key2;
217 }
218
219 /**
220 * Compares two values for equals.
221 * <p>
222 * This implementation uses {@code ==}.
223 * </p>
224 *
225 * @param value1 The first value to compare passed in from outside
226 * @param value2 The second value extracted from the entry via {@code getValue()}
227 * @return true if equal by identity
228 */
229 @Override
230 protected boolean isEqualValue(final Object value1, final Object value2) {
231 return value1 == value2;
232 }
233
234 /**
235 * Deserializes the map in using a custom routine.
236 *
237 * @param in The input stream
238 * @throws IOException Thrown if an error occurs while reading from the stream
239 * @throws ClassNotFoundException if an object read from the stream cannot be loaded
240 */
241 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
242 in.defaultReadObject();
243 doReadObject(in);
244 }
245
246 /**
247 * Serializes this object to an ObjectOutputStream.
248 *
249 * @param out The target ObjectOutputStream.
250 * @throws IOException thrown when an I/O errors occur writing to the target stream.
251 */
252 private void writeObject(final ObjectOutputStream out) throws IOException {
253 out.defaultWriteObject();
254 doWriteObject(out);
255 }
256
257 }