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 protected static class IdentityEntry<K, V> extends HashEntry<K, V> { 53 54 protected IdentityEntry(final HashEntry<K, V> next, final int hashCode, final K key, final V value) { 55 super(next, hashCode, key, value); 56 } 57 58 @Override 59 public boolean equals(final Object obj) { 60 if (obj == this) { 61 return true; 62 } 63 if (!(obj instanceof Entry)) { 64 return false; 65 } 66 final Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj; 67 return 68 getKey() == other.getKey() && 69 getValue() == other.getValue(); 70 } 71 72 @Override 73 public int hashCode() { 74 return System.identityHashCode(getKey()) ^ 75 System.identityHashCode(getValue()); 76 } 77 } 78 79 /** Serialisation version */ 80 private static final long serialVersionUID = 2028493495224302329L; 81 82 /** 83 * Constructs a new empty map with default size and load factor. 84 */ 85 public IdentityMap() { 86 super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD); 87 } 88 89 /** 90 * Constructs a new, empty map with the specified initial capacity. 91 * 92 * @param initialCapacity the initial capacity 93 * @throws IllegalArgumentException if the initial capacity is negative 94 */ 95 public IdentityMap(final int initialCapacity) { 96 super(initialCapacity); 97 } 98 99 /** 100 * Constructs a new, empty map with the specified initial capacity and 101 * load factor. 102 * 103 * @param initialCapacity the initial capacity 104 * @param loadFactor the load factor 105 * @throws IllegalArgumentException if the initial capacity is negative 106 * @throws IllegalArgumentException if the load factor is less than zero 107 */ 108 public IdentityMap(final int initialCapacity, final float loadFactor) { 109 super(initialCapacity, loadFactor); 110 } 111 112 /** 113 * Constructor copying elements from another map. 114 * 115 * @param map the map to copy 116 * @throws NullPointerException if the map is null 117 */ 118 public IdentityMap(final Map<K, V> map) { 119 super(map); 120 } 121 122 /** 123 * Clones the map without cloning the keys or values. 124 * 125 * @return a shallow clone 126 */ 127 @Override 128 public IdentityMap<K, V> clone() { 129 return (IdentityMap<K, V>) super.clone(); 130 } 131 132 /** 133 * Creates an entry to store the data. 134 * This implementation creates an IdentityEntry instance. 135 * 136 * @param next the next entry in sequence 137 * @param hashCode the hash code to use 138 * @param key the key to store 139 * @param value the value to store 140 * @return the newly created entry 141 */ 142 @Override 143 protected IdentityEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode, 144 final K key, final V value) { 145 return new IdentityEntry<>(next, hashCode, key, value); 146 } 147 148 /** 149 * Gets the hash code for the key specified. 150 * This implementation uses the identity hash code. 151 * 152 * @param key the key to get a hash code for 153 * @return the hash code 154 */ 155 @Override 156 protected int hash(final Object key) { 157 return System.identityHashCode(key); 158 } 159 160 /** 161 * Compares two keys for equals. 162 * This implementation uses {@code ==}. 163 * 164 * @param key1 the first key to compare 165 * @param key2 the second key to compare 166 * @return true if equal by identity 167 */ 168 @Override 169 protected boolean isEqualKey(final Object key1, final Object key2) { 170 return key1 == key2; 171 } 172 173 /** 174 * Compares two values for equals. 175 * This implementation uses {@code ==}. 176 * 177 * @param value1 the first value to compare 178 * @param value2 the second value to compare 179 * @return true if equal by identity 180 */ 181 @Override 182 protected boolean isEqualValue(final Object value1, final Object value2) { 183 return value1 == value2; 184 } 185 186 /** 187 * Read the map in using a custom routine. 188 */ 189 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 190 in.defaultReadObject(); 191 doReadObject(in); 192 } 193 194 /** 195 * Write the map out using a custom routine. 196 */ 197 private void writeObject(final ObjectOutputStream out) throws IOException { 198 out.defaultWriteObject(); 199 doWriteObject(out); 200 } 201 202 }