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.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.util.Map;
24
25 /**
26 * A {@code Map} implementation that matches keys and values based
27 * on {@code ==} not {@code equals()}.
28 * <p>
29 * <strong>This map will violate the detail of various Map and map view contracts.</strong>
30 * As a general rule, don't compare this map to other maps. In particular, you can't
31 * use decorators like {@link ListOrderedMap} on it, which silently assume that these
32 * contracts are fulfilled.
33 * <p>
34 * <strong>Note that IdentityMap is not synchronized and is not thread-safe.</strong>
35 * If you wish to use this map from multiple threads concurrently, you must use
36 * appropriate synchronization. The simplest approach is to wrap this map
37 * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
38 * exceptions when accessed by concurrent threads without synchronization.
39 * <p>
40 * From 4.0, this class is replaced by java.util.IdentityHashMap but kept as a
41 * test-class because it is still used by the ReferenceIdentityMapTest.
42 *
43 * @param <K> the type of the keys in this map
44 * @param <V> the type of the values in this map
45 */
46 public class IdentityMap<K, V>
47 extends AbstractHashedMap<K, V> implements Serializable, Cloneable {
48
49 /**
50 * HashEntry.
51 *
52 * @param <K> the key type.
53 * @param <V> the value type.
54 */
55 protected static class IdentityEntry<K, V> extends HashEntry<K, V> {
56
57 protected IdentityEntry(final HashEntry<K, V> next, final int hashCode, final K key, final V value) {
58 super(next, hashCode, key, value);
59 }
60
61 @Override
62 public boolean equals(final Object obj) {
63 if (obj == this) {
64 return true;
65 }
66 if (!(obj instanceof Entry)) {
67 return false;
68 }
69 final Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
70 return
71 getKey() == other.getKey() &&
72 getValue() == other.getValue();
73 }
74
75 @Override
76 public int hashCode() {
77 return System.identityHashCode(getKey()) ^
78 System.identityHashCode(getValue());
79 }
80 }
81
82 /** Serialization version */
83 private static final long serialVersionUID = 2028493495224302329L;
84
85 /**
86 * Constructs a new empty map with default size and load factor.
87 */
88 public IdentityMap() {
89 super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
90 }
91
92 /**
93 * Constructs a new, empty map with the specified initial capacity.
94 *
95 * @param initialCapacity the initial capacity
96 * @throws IllegalArgumentException if the initial capacity is negative
97 */
98 public IdentityMap(final int initialCapacity) {
99 super(initialCapacity);
100 }
101
102 /**
103 * Constructs a new, empty map with the specified initial capacity and
104 * load factor.
105 *
106 * @param initialCapacity the initial capacity
107 * @param loadFactor the load factor
108 * @throws IllegalArgumentException if the initial capacity is negative
109 * @throws IllegalArgumentException if the load factor is less than zero
110 */
111 public IdentityMap(final int initialCapacity, final float loadFactor) {
112 super(initialCapacity, loadFactor);
113 }
114
115 /**
116 * Constructor copying elements from another map.
117 *
118 * @param map the map to copy
119 * @throws NullPointerException if the map is null
120 */
121 public IdentityMap(final Map<K, V> map) {
122 super(map);
123 }
124
125 /**
126 * Clones the map without cloning the keys or values.
127 *
128 * @return a shallow clone
129 */
130 @Override
131 public IdentityMap<K, V> clone() {
132 return (IdentityMap<K, V>) super.clone();
133 }
134
135 /**
136 * Creates an entry to store the data.
137 * This implementation creates an IdentityEntry instance.
138 *
139 * @param next the next entry in sequence
140 * @param hashCode the hash code to use
141 * @param key the key to store
142 * @param value the value to store
143 * @return the newly created entry
144 */
145 @Override
146 protected IdentityEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode,
147 final K key, final V value) {
148 return new IdentityEntry<>(next, hashCode, key, value);
149 }
150
151 /**
152 * Gets the hash code for the key specified.
153 * This implementation uses the identity hash code.
154 *
155 * @param key the key to get a hash code for
156 * @return the hash code
157 */
158 @Override
159 protected int hash(final Object key) {
160 return System.identityHashCode(key);
161 }
162
163 /**
164 * Compares two keys for equals.
165 * This implementation uses {@code ==}.
166 *
167 * @param key1 the first key to compare
168 * @param key2 the second key to compare
169 * @return true if equal by identity
170 */
171 @Override
172 protected boolean isEqualKey(final Object key1, final Object key2) {
173 return key1 == key2;
174 }
175
176 /**
177 * Compares two values for equals.
178 * This implementation uses {@code ==}.
179 *
180 * @param value1 the first value to compare
181 * @param value2 the second value to compare
182 * @return true if equal by identity
183 */
184 @Override
185 protected boolean isEqualValue(final Object value1, final Object value2) {
186 return value1 == value2;
187 }
188
189 /**
190 * Deserializes an instance from an ObjectInputStream.
191 *
192 * @param in The source ObjectInputStream.
193 * @throws IOException Any of the usual Input/Output related exceptions.
194 * @throws ClassNotFoundException A class of a serialized object cannot be found.
195 */
196 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
197 in.defaultReadObject();
198 doReadObject(in);
199 }
200
201 /**
202 * Serializes this object to an ObjectOutputStream.
203 *
204 * @param out the target ObjectOutputStream.
205 * @throws IOException thrown when an I/O errors occur writing to the target stream.
206 */
207 private void writeObject(final ObjectOutputStream out) throws IOException {
208 out.defaultWriteObject();
209 doWriteObject(out);
210 }
211
212 }