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     */
017    package org.apache.commons.collections.map;
018    
019    import java.io.IOException;
020    import java.io.ObjectInputStream;
021    import java.io.ObjectOutputStream;
022    import java.io.Serializable;
023    import java.util.Map;
024    
025    /**
026     * A <code>Map</code> implementation that matches keys and values based
027     * on <code>==</code> not <code>equals()</code>.
028     * <p>
029     * This map will violate the detail of various Map and map view contracts.
030     * As a general rule, don't compare this map to other maps.
031     * <p>
032     * <strong>Note that IdentityMap is not synchronized and is not thread-safe.</strong>
033     * If you wish to use this map from multiple threads concurrently, you must use
034     * appropriate synchronization. The simplest approach is to wrap this map
035     * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw 
036     * exceptions when accessed by concurrent threads without synchronization.
037     *
038     * @since Commons Collections 3.0
039     * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
040     *
041     * @author java util HashMap
042     * @author Stephen Colebourne
043     */
044    public class IdentityMap
045            extends AbstractHashedMap implements Serializable, Cloneable {
046    
047        /** Serialisation version */
048        private static final long serialVersionUID = 2028493495224302329L;
049    
050        /**
051         * Constructs a new empty map with default size and load factor.
052         */
053        public IdentityMap() {
054            super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
055        }
056    
057        /**
058         * Constructs a new, empty map with the specified initial capacity. 
059         *
060         * @param initialCapacity  the initial capacity
061         * @throws IllegalArgumentException if the initial capacity is less than one
062         */
063        public IdentityMap(int initialCapacity) {
064            super(initialCapacity);
065        }
066    
067        /**
068         * Constructs a new, empty map with the specified initial capacity and
069         * load factor. 
070         *
071         * @param initialCapacity  the initial capacity
072         * @param loadFactor  the load factor
073         * @throws IllegalArgumentException if the initial capacity is less than one
074         * @throws IllegalArgumentException if the load factor is less than zero
075         */
076        public IdentityMap(int initialCapacity, float loadFactor) {
077            super(initialCapacity, loadFactor);
078        }
079    
080        /**
081         * Constructor copying elements from another map.
082         *
083         * @param map  the map to copy
084         * @throws NullPointerException if the map is null
085         */
086        public IdentityMap(Map map) {
087            super(map);
088        }
089    
090        //-----------------------------------------------------------------------
091        /**
092         * Gets the hash code for the key specified.
093         * This implementation uses the identity hash code.
094         * 
095         * @param key  the key to get a hash code for
096         * @return the hash code
097         */
098        protected int hash(Object key) {
099            return System.identityHashCode(key);
100        }
101        
102        /**
103         * Compares two keys for equals.
104         * This implementation uses <code>==</code>.
105         * 
106         * @param key1  the first key to compare
107         * @param key2  the second key to compare
108         * @return true if equal by identity
109         */
110        protected boolean isEqualKey(Object key1, Object key2) {
111            return (key1 == key2);
112        }
113        
114        /**
115         * Compares two values for equals.
116         * This implementation uses <code>==</code>.
117         * 
118         * @param value1  the first value to compare
119         * @param value2  the second value to compare
120         * @return true if equal by identity
121         */
122        protected boolean isEqualValue(Object value1, Object value2) {
123            return (value1 == value2);
124        }
125        
126        /**
127         * Creates an entry to store the data.
128         * This implementation creates an IdentityEntry instance.
129         * 
130         * @param next  the next entry in sequence
131         * @param hashCode  the hash code to use
132         * @param key  the key to store
133         * @param value  the value to store
134         * @return the newly created entry
135         */
136        protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
137            return new IdentityEntry(next, hashCode, key, value);
138        }
139        
140        //-----------------------------------------------------------------------
141        /**
142         * HashEntry
143         */
144        protected static class IdentityEntry extends HashEntry {
145            
146            protected IdentityEntry(HashEntry next, int hashCode, Object key, Object value) {
147                super(next, hashCode, key, value);
148            }
149            
150            public boolean equals(Object obj) {
151                if (obj == this) {
152                    return true;
153                }
154                if (obj instanceof Map.Entry == false) {
155                    return false;
156                }
157                Map.Entry other = (Map.Entry) obj;
158                return
159                    (getKey() == other.getKey()) &&
160                    (getValue() == other.getValue());
161            }
162            
163            public int hashCode() {
164                return System.identityHashCode(getKey()) ^
165                       System.identityHashCode(getValue());
166            }
167        }
168        
169        //-----------------------------------------------------------------------
170        /**
171         * Clones the map without cloning the keys or values.
172         *
173         * @return a shallow clone
174         */
175        public Object clone() {
176            return super.clone();
177        }
178        
179        /**
180         * Write the map out using a custom routine.
181         */
182        private void writeObject(ObjectOutputStream out) throws IOException {
183            out.defaultWriteObject();
184            doWriteObject(out);
185        }
186    
187        /**
188         * Read the map in using a custom routine.
189         */
190        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
191            in.defaultReadObject();
192            doReadObject(in);
193        }
194        
195    }