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 package org.apache.commons.collections.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
25 /**
26 * A <code>Map</code> implementation that allows mappings to be
27 * removed by the garbage collector and matches keys and values based
28 * on <code>==</code> not <code>equals()</code>.
29 * <p>
30 * <p>
31 * When you construct a <code>ReferenceIdentityMap</code>, 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 * Different types of references can be specified for keys and values.
39 * The default constructor uses hard keys and soft values, providing a
40 * memory-sensitive cache.
41 * <p>
42 * This map is similar to
43 * {@link org.apache.commons.collections.map.ReferenceMap ReferenceMap}.
44 * It differs in that keys and values in this class are compared using <code>==</code>.
45 * <p>
46 * This map will violate the detail of various Map and map view contracts.
47 * As a general rule, don't compare this map to other maps.
48 * <p>
49 * This {@link java.util.Map Map} implementation does <i>not</i> allow null elements.
50 * Attempting to add a null key or value to the map will raise a <code>NullPointerException</code>.
51 * <p>
52 * This implementation is not synchronized.
53 * You can use {@link java.util.Collections#synchronizedMap} to
54 * provide synchronized access to a <code>ReferenceIdentityMap</code>.
55 * Remember that synchronization will not stop the garbage collecter removing entries.
56 * <p>
57 * All the available iterators can be reset back to the start by casting to
58 * <code>ResettableIterator</code> and calling <code>reset()</code>.
59 * <p>
60 * <strong>Note that ReferenceIdentityMap is not synchronized and is not thread-safe.</strong>
61 * If you wish to use this map from multiple threads concurrently, you must use
62 * appropriate synchronization. The simplest approach is to wrap this map
63 * using {@link java.util.Collections#synchronizedMap}. This class may throw
64 * exceptions when accessed by concurrent threads without synchronization.
65 *
66 * @see java.lang.ref.Reference
67 *
68 * @since 3.0 (previously in main package v2.1)
69 * @version $Id: ReferenceIdentityMap.java 1429905 2013-01-07 17:15:14Z ggregory $
70 */
71 public class ReferenceIdentityMap<K, V> extends AbstractReferenceMap<K, V> implements Serializable {
72
73 /** Serialization version */
74 private static final long serialVersionUID = -1266190134568365852L;
75
76 /**
77 * Constructs a new <code>ReferenceIdentityMap</code> that will
78 * use hard references to keys and soft references to values.
79 */
80 public ReferenceIdentityMap() {
81 super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY,
82 DEFAULT_LOAD_FACTOR, false);
83 }
84
85 /**
86 * Constructs a new <code>ReferenceIdentityMap</code> that will
87 * use the specified types of references.
88 *
89 * @param keyType the type of reference to use for keys;
90 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
91 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
92 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
93 * @param valueType the type of reference to use for values;
94 * must be {@link AbstractReferenceMap.ReferenceStrength#HARD HARD},
95 * {@link AbstractReferenceMap.ReferenceStrength#SOFT SOFT},
96 * {@link AbstractReferenceMap.ReferenceStrength#WEAK WEAK}
97 */
98 public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType) {
99 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 = keyType == 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 }