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 * @since 3.0 061 * @version $Id: LinkedMap.html 972421 2015-11-14 20:00:04Z tn $ 062 */ 063public class LinkedMap<K, V> extends AbstractLinkedMap<K, V> implements Serializable, Cloneable { 064 065 /** Serialisation version */ 066 private static final long serialVersionUID = 9077234323521161066L; 067 068 /** 069 * Constructs a new empty map with default size and load factor. 070 */ 071 public LinkedMap() { 072 super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD); 073 } 074 075 /** 076 * Constructs a new, empty map with the specified initial capacity. 077 * 078 * @param initialCapacity the initial capacity 079 * @throws IllegalArgumentException if the initial capacity is negative 080 */ 081 public LinkedMap(final int initialCapacity) { 082 super(initialCapacity); 083 } 084 085 /** 086 * Constructs a new, empty map with the specified initial capacity and 087 * load factor. 088 * 089 * @param initialCapacity the initial capacity 090 * @param loadFactor the load factor 091 * @throws IllegalArgumentException if the initial capacity is negative 092 * @throws IllegalArgumentException if the load factor is less than zero 093 */ 094 public LinkedMap(final int initialCapacity, final float loadFactor) { 095 super(initialCapacity, loadFactor); 096 } 097 098 /** 099 * Constructor copying elements from another map. 100 * 101 * @param map the map to copy 102 * @throws NullPointerException if the map is null 103 */ 104 public LinkedMap(final Map<? extends K, ? extends V> map) { 105 super(map); 106 } 107 108 //----------------------------------------------------------------------- 109 /** 110 * Clones the map without cloning the keys or values. 111 * 112 * @return a shallow clone 113 */ 114 @Override 115 public LinkedMap<K, V> clone() { 116 return (LinkedMap<K, V>) super.clone(); 117 } 118 119 /** 120 * Write the map out using a custom routine. 121 */ 122 private void writeObject(final ObjectOutputStream out) throws IOException { 123 out.defaultWriteObject(); 124 doWriteObject(out); 125 } 126 127 /** 128 * Read the map in using a custom routine. 129 */ 130 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 131 in.defaultReadObject(); 132 doReadObject(in); 133 } 134 135 //----------------------------------------------------------------------- 136 /** 137 * Gets the key at the specified index. 138 * 139 * @param index the index to retrieve 140 * @return the key at the specified index 141 * @throws IndexOutOfBoundsException if the index is invalid 142 */ 143 public K get(final int index) { 144 return getEntry(index).getKey(); 145 } 146 147 /** 148 * Gets the value at the specified index. 149 * 150 * @param index the index to retrieve 151 * @return the value at the specified index 152 * @throws IndexOutOfBoundsException if the index is invalid 153 */ 154 public V getValue(final int index) { 155 return getEntry(index).getValue(); 156 } 157 158 /** 159 * Gets the index of the specified key. 160 * 161 * @param key the key to find the index of 162 * @return the index, or -1 if not found 163 */ 164 public int indexOf(Object key) { 165 key = convertKey(key); 166 int i = 0; 167 for (LinkEntry<K, V> entry = header.after; entry != header; entry = entry.after, i++) { 168 if (isEqualKey(key, entry.key)) { 169 return i; 170 } 171 } 172 return -1; 173 } 174 175 /** 176 * Removes the element at the specified index. 177 * 178 * @param index the index of the object to remove 179 * @return the previous value corresponding the <code>key</code>, 180 * or <code>null</code> if none existed 181 * @throws IndexOutOfBoundsException if the index is invalid 182 */ 183 public V remove(final int index) { 184 return remove(get(index)); 185 } 186 187 /** 188 * Gets an unmodifiable List view of the keys. 189 * <p> 190 * The returned list is unmodifiable because changes to the values of 191 * the list (using {@link java.util.ListIterator#set(Object)}) will 192 * effectively remove the value from the list and reinsert that value at 193 * the end of the list, which is an unexpected side effect of changing the 194 * value of a list. This occurs because changing the key, changes when the 195 * mapping is added to the map and thus where it appears in the list. 196 * <p> 197 * An alternative to this method is to use {@link #keySet()}. 198 * 199 * @see #keySet() 200 * @return The ordered list of keys. 201 */ 202 public List<K> asList() { 203 return new LinkedMapList<K>(this); 204 } 205 206 /** 207 * List view of map. 208 */ 209 static class LinkedMapList<K> extends AbstractList<K> { 210 211 private final LinkedMap<K, ?> parent; 212 213 LinkedMapList(final LinkedMap<K, ?> parent) { 214 this.parent = parent; 215 } 216 217 @Override 218 public int size() { 219 return parent.size(); 220 } 221 222 @Override 223 public K get(final int index) { 224 return parent.get(index); 225 } 226 227 @Override 228 public boolean contains(final Object obj) { 229 return parent.containsKey(obj); 230 } 231 232 @Override 233 public int indexOf(final Object obj) { 234 return parent.indexOf(obj); 235 } 236 237 @Override 238 public int lastIndexOf(final Object obj) { 239 return parent.indexOf(obj); 240 } 241 242 @Override 243 public boolean containsAll(final Collection<?> coll) { 244 return parent.keySet().containsAll(coll); 245 } 246 247 @Override 248 public K remove(final int index) { 249 throw new UnsupportedOperationException(); 250 } 251 252 @Override 253 public boolean remove(final Object obj) { 254 throw new UnsupportedOperationException(); 255 } 256 257 @Override 258 public boolean removeAll(final Collection<?> coll) { 259 throw new UnsupportedOperationException(); 260 } 261 262 @Override 263 public boolean retainAll(final Collection<?> coll) { 264 throw new UnsupportedOperationException(); 265 } 266 267 @Override 268 public void clear() { 269 throw new UnsupportedOperationException(); 270 } 271 272 @Override 273 public Object[] toArray() { 274 return parent.keySet().toArray(); 275 } 276 277 @Override 278 public <T> T[] toArray(final T[] array) { 279 return parent.keySet().toArray(array); 280 } 281 282 @Override 283 public Iterator<K> iterator() { 284 return UnmodifiableIterator.unmodifiableIterator(parent.keySet().iterator()); 285 } 286 287 @Override 288 public ListIterator<K> listIterator() { 289 return UnmodifiableListIterator.umodifiableListIterator(super.listIterator()); 290 } 291 292 @Override 293 public ListIterator<K> listIterator(final int fromIndex) { 294 return UnmodifiableListIterator.umodifiableListIterator(super.listIterator(fromIndex)); 295 } 296 297 @Override 298 public List<K> subList(final int fromIndexInclusive, final int toIndexExclusive) { 299 return UnmodifiableList.unmodifiableList(super.subList(fromIndexInclusive, toIndexExclusive)); 300 } 301 } 302 303}