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 */
017package org.apache.commons.collections4.map;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.util.AbstractList;
024import java.util.Collection;
025import java.util.Iterator;
026import java.util.List;
027import java.util.ListIterator;
028import java.util.Map;
029
030import org.apache.commons.collections4.iterators.UnmodifiableIterator;
031import org.apache.commons.collections4.iterators.UnmodifiableListIterator;
032import org.apache.commons.collections4.list.UnmodifiableList;
033
034/**
035 * A <code>Map</code> implementation that maintains the order of the entries.
036 * In this implementation order is maintained by original insertion.
037 * <p>
038 * This implementation improves on the JDK1.4 LinkedHashMap by adding the
039 * {@link org.apache.commons.collections4.MapIterator MapIterator}
040 * functionality, additional convenience methods and allowing
041 * bidirectional iteration. It also implements <code>OrderedMap</code>.
042 * In addition, non-interface methods are provided to access the map by index.
043 * <p>
044 * The <code>orderedMapIterator()</code> method provides direct access to a
045 * bidirectional iterator. The iterators from the other views can also be cast
046 * to <code>OrderedIterator</code> if required.
047 * <p>
048 * All the available iterators can be reset back to the start by casting to
049 * <code>ResettableIterator</code> and calling <code>reset()</code>.
050 * <p>
051 * The implementation is also designed to be subclassed, with lots of useful
052 * methods exposed.
053 * <p>
054 * <strong>Note that LinkedMap is not synchronized and is not thread-safe.</strong>
055 * If you wish to use this map from multiple threads concurrently, you must use
056 * appropriate synchronization. The simplest approach is to wrap this map
057 * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
058 * exceptions when accessed by concurrent threads without synchronization.
059 *
060 * @param <K> the type of the keys in this map
061 * @param <V> the type of the values in this map
062 * @since 3.0
063 */
064public class LinkedMap<K, V> extends AbstractLinkedMap<K, V> implements Serializable, Cloneable {
065
066    /** Serialisation version */
067    private static final long serialVersionUID = 9077234323521161066L;
068
069    /**
070     * Constructs a new empty map with default size and load factor.
071     */
072    public LinkedMap() {
073        super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
074    }
075
076    /**
077     * Constructs a new, empty map with the specified initial capacity.
078     *
079     * @param initialCapacity  the initial capacity
080     * @throws IllegalArgumentException if the initial capacity is negative
081     */
082    public LinkedMap(final int initialCapacity) {
083        super(initialCapacity);
084    }
085
086    /**
087     * Constructs a new, empty map with the specified initial capacity and
088     * load factor.
089     *
090     * @param initialCapacity  the initial capacity
091     * @param loadFactor  the load factor
092     * @throws IllegalArgumentException if the initial capacity is negative
093     * @throws IllegalArgumentException if the load factor is less than zero
094     */
095    public LinkedMap(final int initialCapacity, final float loadFactor) {
096        super(initialCapacity, loadFactor);
097    }
098
099    /**
100     * Constructor copying elements from another map.
101     *
102     * @param map  the map to copy
103     * @throws NullPointerException if the map is null
104     */
105    public LinkedMap(final Map<? extends K, ? extends V> map) {
106        super(map);
107    }
108
109    //-----------------------------------------------------------------------
110    /**
111     * Clones the map without cloning the keys or values.
112     *
113     * @return a shallow clone
114     */
115    @Override
116    public LinkedMap<K, V> clone() {
117        return (LinkedMap<K, V>) super.clone();
118    }
119
120    /**
121     * Write the map out using a custom routine.
122     *
123     * @param out  the output stream
124     * @throws IOException if an error occurs while writing to the stream
125     */
126    private void writeObject(final ObjectOutputStream out) throws IOException {
127        out.defaultWriteObject();
128        doWriteObject(out);
129    }
130
131    /**
132     * Read the map in using a custom routine.
133     *
134     * @param in the input stream
135     * @throws IOException if an error occurs while reading from the stream
136     * @throws ClassNotFoundException if an object read from the stream can not be loaded
137     */
138    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
139        in.defaultReadObject();
140        doReadObject(in);
141    }
142
143    //-----------------------------------------------------------------------
144    /**
145     * Gets the key at the specified index.
146     *
147     * @param index  the index to retrieve
148     * @return the key at the specified index
149     * @throws IndexOutOfBoundsException if the index is invalid
150     */
151    public K get(final int index) {
152        return getEntry(index).getKey();
153    }
154
155    /**
156     * Gets the value at the specified index.
157     *
158     * @param index  the index to retrieve
159     * @return the value at the specified index
160     * @throws IndexOutOfBoundsException if the index is invalid
161     */
162    public V getValue(final int index) {
163        return getEntry(index).getValue();
164    }
165
166    /**
167     * Gets the index of the specified key.
168     *
169     * @param key  the key to find the index of
170     * @return the index, or -1 if not found
171     */
172    public int indexOf(Object key) {
173        key = convertKey(key);
174        int i = 0;
175        for (LinkEntry<K, V> entry = header.after; entry != header; entry = entry.after, i++) {
176            if (isEqualKey(key, entry.key)) {
177                return i;
178            }
179        }
180        return -1;
181    }
182
183    /**
184     * Removes the element at the specified index.
185     *
186     * @param index  the index of the object to remove
187     * @return the previous value corresponding the <code>key</code>,
188     *  or <code>null</code> if none existed
189     * @throws IndexOutOfBoundsException if the index is invalid
190     */
191    public V remove(final int index) {
192        return remove(get(index));
193    }
194
195    /**
196     * Gets an unmodifiable List view of the keys.
197     * <p>
198     * The returned list is unmodifiable because changes to the values of
199     * the list (using {@link java.util.ListIterator#set(Object)}) will
200     * effectively remove the value from the list and reinsert that value at
201     * the end of the list, which is an unexpected side effect of changing the
202     * value of a list.  This occurs because changing the key, changes when the
203     * mapping is added to the map and thus where it appears in the list.
204     * <p>
205     * An alternative to this method is to use {@link #keySet()}.
206     *
207     * @see #keySet()
208     * @return The ordered list of keys.
209     */
210    public List<K> asList() {
211        return new LinkedMapList<>(this);
212    }
213
214    /**
215     * List view of map.
216     */
217    static class LinkedMapList<K> extends AbstractList<K> {
218
219        private final LinkedMap<K, ?> parent;
220
221        LinkedMapList(final LinkedMap<K, ?> parent) {
222            this.parent = parent;
223        }
224
225        @Override
226        public int size() {
227            return parent.size();
228        }
229
230        @Override
231        public K get(final int index) {
232            return parent.get(index);
233        }
234
235        @Override
236        public boolean contains(final Object obj) {
237            return parent.containsKey(obj);
238        }
239
240        @Override
241        public int indexOf(final Object obj) {
242            return parent.indexOf(obj);
243        }
244
245        @Override
246        public int lastIndexOf(final Object obj) {
247            return parent.indexOf(obj);
248        }
249
250        @Override
251        public boolean containsAll(final Collection<?> coll) {
252            return parent.keySet().containsAll(coll);
253        }
254
255        @Override
256        public K remove(final int index) {
257            throw new UnsupportedOperationException();
258        }
259
260        @Override
261        public boolean remove(final Object obj) {
262            throw new UnsupportedOperationException();
263        }
264
265        @Override
266        public boolean removeAll(final Collection<?> coll) {
267            throw new UnsupportedOperationException();
268        }
269
270        @Override
271        public boolean retainAll(final Collection<?> coll) {
272            throw new UnsupportedOperationException();
273        }
274
275        @Override
276        public void clear() {
277            throw new UnsupportedOperationException();
278        }
279
280        @Override
281        public Object[] toArray() {
282            return parent.keySet().toArray();
283        }
284
285        @Override
286        public <T> T[] toArray(final T[] array) {
287            return parent.keySet().toArray(array);
288        }
289
290        @Override
291        public Iterator<K> iterator() {
292            return UnmodifiableIterator.unmodifiableIterator(parent.keySet().iterator());
293        }
294
295        @Override
296        public ListIterator<K> listIterator() {
297            return UnmodifiableListIterator.umodifiableListIterator(super.listIterator());
298        }
299
300        @Override
301        public ListIterator<K> listIterator(final int fromIndex) {
302            return UnmodifiableListIterator.umodifiableListIterator(super.listIterator(fromIndex));
303        }
304
305        @Override
306        public List<K> subList(final int fromIndexInclusive, final int toIndexExclusive) {
307            return UnmodifiableList.unmodifiableList(super.subList(fromIndexInclusive, toIndexExclusive));
308        }
309    }
310
311}