001 /* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.collect; 018 019 import com.google.common.annotations.GwtCompatible; 020 import com.google.common.annotations.GwtIncompatible; 021 import com.google.common.annotations.VisibleForTesting; 022 import com.google.common.base.Preconditions; 023 024 import java.io.IOException; 025 import java.io.ObjectInputStream; 026 import java.io.ObjectOutputStream; 027 import java.util.Collection; 028 import java.util.Iterator; 029 import java.util.LinkedHashMap; 030 import java.util.LinkedHashSet; 031 import java.util.Map; 032 import java.util.Set; 033 034 import javax.annotation.Nullable; 035 036 /** 037 * Implementation of {@code Multimap} that does not allow duplicate key-value 038 * entries and that returns collections whose iterators follow the ordering in 039 * which the data was added to the multimap. 040 * 041 * <p>The collections returned by {@code keySet}, {@code keys}, and {@code 042 * asMap} iterate through the keys in the order they were first added to the 043 * multimap. Similarly, {@code get}, {@code removeAll}, and {@code 044 * replaceValues} return collections that iterate through the values in the 045 * order they were added. The collections generated by {@code entries} and 046 * {@code values} iterate across the key-value mappings in the order they were 047 * added to the multimap. 048 * 049 * <p>The iteration ordering of the collections generated by {@code keySet}, 050 * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of 051 * keys remains unchanged, adding or removing mappings does not affect the key 052 * iteration order. However, if you remove all values associated with a key and 053 * then add the key back to the multimap, that key will come last in the key 054 * iteration order. 055 * 056 * <p>The multimap does not store duplicate key-value pairs. Adding a new 057 * key-value pair equal to an existing key-value pair has no effect. 058 * 059 * <p>Keys and values may be null. All optional multimap methods are supported, 060 * and all returned views are modifiable. 061 * 062 * <p>This class is not threadsafe when any concurrent operations update the 063 * multimap. Concurrent read operations will work correctly. To allow concurrent 064 * update operations, wrap your multimap with a call to {@link 065 * Multimaps#synchronizedSetMultimap}. 066 * 067 * @author Jared Levy 068 * @since 2 (imported from Google Collections Library) 069 */ 070 @GwtCompatible(serializable = true, emulated = true) 071 public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { 072 private static final int DEFAULT_VALUES_PER_KEY = 8; 073 074 @VisibleForTesting 075 transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; 076 077 /** 078 * Map entries with an iteration order corresponding to the order in which the 079 * key-value pairs were added to the multimap. 080 */ 081 // package-private for GWT deserialization 082 transient Collection<Map.Entry<K, V>> linkedEntries; 083 084 /** 085 * Creates a new, empty {@code LinkedHashMultimap} with the default initial 086 * capacities. 087 */ 088 public static <K, V> LinkedHashMultimap<K, V> create() { 089 return new LinkedHashMultimap<K, V>(); 090 } 091 092 /** 093 * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold 094 * the specified numbers of keys and values without rehashing. 095 * 096 * @param expectedKeys the expected number of distinct keys 097 * @param expectedValuesPerKey the expected average number of values per key 098 * @throws IllegalArgumentException if {@code expectedKeys} or {@code 099 * expectedValuesPerKey} is negative 100 */ 101 public static <K, V> LinkedHashMultimap<K, V> create( 102 int expectedKeys, int expectedValuesPerKey) { 103 return new LinkedHashMultimap<K, V>(expectedKeys, expectedValuesPerKey); 104 } 105 106 /** 107 * Constructs a {@code LinkedHashMultimap} with the same mappings as the 108 * specified multimap. If a key-value mapping appears multiple times in the 109 * input multimap, it only appears once in the constructed multimap. The new 110 * multimap has the same {@link Multimap#entries()} iteration order as the 111 * input multimap, except for excluding duplicate mappings. 112 * 113 * @param multimap the multimap whose contents are copied to this multimap 114 */ 115 public static <K, V> LinkedHashMultimap<K, V> create( 116 Multimap<? extends K, ? extends V> multimap) { 117 return new LinkedHashMultimap<K, V>(multimap); 118 } 119 120 private LinkedHashMultimap() { 121 super(new LinkedHashMap<K, Collection<V>>()); 122 linkedEntries = Sets.newLinkedHashSet(); 123 } 124 125 private LinkedHashMultimap(int expectedKeys, int expectedValuesPerKey) { 126 super(new LinkedHashMap<K, Collection<V>>(expectedKeys)); 127 Preconditions.checkArgument(expectedValuesPerKey >= 0); 128 this.expectedValuesPerKey = expectedValuesPerKey; 129 linkedEntries = new LinkedHashSet<Map.Entry<K, V>>( 130 (int) Math.min(1 << 30, ((long) expectedKeys) * expectedValuesPerKey)); 131 } 132 133 private LinkedHashMultimap(Multimap<? extends K, ? extends V> multimap) { 134 super(new LinkedHashMap<K, Collection<V>>( 135 Maps.capacity(multimap.keySet().size()))); 136 linkedEntries 137 = new LinkedHashSet<Map.Entry<K, V>>(Maps.capacity(multimap.size())); 138 putAll(multimap); 139 } 140 141 /** 142 * {@inheritDoc} 143 * 144 * <p>Creates an empty {@code LinkedHashSet} for a collection of values for 145 * one key. 146 * 147 * @return a new {@code LinkedHashSet} containing a collection of values for 148 * one key 149 */ 150 @Override Set<V> createCollection() { 151 return new LinkedHashSet<V>(Maps.capacity(expectedValuesPerKey)); 152 } 153 154 /** 155 * {@inheritDoc} 156 * 157 * <p>Creates a decorated {@code LinkedHashSet} that also keeps track of the 158 * order in which key-value pairs are added to the multimap. 159 * 160 * @param key key to associate with values in the collection 161 * @return a new decorated {@code LinkedHashSet} containing a collection of 162 * values for one key 163 */ 164 @Override Collection<V> createCollection(@Nullable K key) { 165 return new SetDecorator(key, createCollection()); 166 } 167 168 private class SetDecorator extends ForwardingSet<V> { 169 final Set<V> delegate; 170 final K key; 171 172 SetDecorator(@Nullable K key, Set<V> delegate) { 173 this.delegate = delegate; 174 this.key = key; 175 } 176 177 @Override protected Set<V> delegate() { 178 return delegate; 179 } 180 181 <E> Map.Entry<K, E> createEntry(@Nullable E value) { 182 return Maps.immutableEntry(key, value); 183 } 184 185 <E> Collection<Map.Entry<K, E>> createEntries(Collection<E> values) { 186 // converts a collection of values into a list of key/value map entries 187 Collection<Map.Entry<K, E>> entries 188 = Lists.newArrayListWithExpectedSize(values.size()); 189 for (E value : values) { 190 entries.add(createEntry(value)); 191 } 192 return entries; 193 } 194 195 @Override public boolean add(@Nullable V value) { 196 boolean changed = delegate.add(value); 197 if (changed) { 198 linkedEntries.add(createEntry(value)); 199 } 200 return changed; 201 } 202 203 @Override public boolean addAll(Collection<? extends V> values) { 204 boolean changed = delegate.addAll(values); 205 if (changed) { 206 linkedEntries.addAll(createEntries(delegate())); 207 } 208 return changed; 209 } 210 211 @Override public void clear() { 212 for (V value : delegate) { 213 linkedEntries.remove(createEntry(value)); 214 } 215 delegate.clear(); 216 } 217 218 @Override public Iterator<V> iterator() { 219 final Iterator<V> delegateIterator = delegate.iterator(); 220 return new Iterator<V>() { 221 V value; 222 223 @Override 224 public boolean hasNext() { 225 return delegateIterator.hasNext(); 226 } 227 @Override 228 public V next() { 229 value = delegateIterator.next(); 230 return value; 231 } 232 @Override 233 public void remove() { 234 delegateIterator.remove(); 235 linkedEntries.remove(createEntry(value)); 236 } 237 }; 238 } 239 240 @Override public boolean remove(@Nullable Object value) { 241 boolean changed = delegate.remove(value); 242 if (changed) { 243 /* 244 * linkedEntries.remove() will return false when this method is called 245 * by entries().iterator().remove() 246 */ 247 linkedEntries.remove(createEntry(value)); 248 } 249 return changed; 250 } 251 252 @Override public boolean removeAll(Collection<?> values) { 253 boolean changed = delegate.removeAll(values); 254 if (changed) { 255 linkedEntries.removeAll(createEntries(values)); 256 } 257 return changed; 258 } 259 260 @Override public boolean retainAll(Collection<?> values) { 261 /* 262 * Calling linkedEntries.retainAll() would incorrectly remove values 263 * with other keys. 264 */ 265 boolean changed = false; 266 Iterator<V> iterator = delegate.iterator(); 267 while (iterator.hasNext()) { 268 V value = iterator.next(); 269 if (!values.contains(value)) { 270 iterator.remove(); 271 linkedEntries.remove(Maps.immutableEntry(key, value)); 272 changed = true; 273 } 274 } 275 return changed; 276 } 277 } 278 279 /** 280 * {@inheritDoc} 281 * 282 * <p>Generates an iterator across map entries that follows the ordering in 283 * which the key-value pairs were added to the multimap. 284 * 285 * @return a key-value iterator with the correct ordering 286 */ 287 @Override Iterator<Map.Entry<K, V>> createEntryIterator() { 288 final Iterator<Map.Entry<K, V>> delegateIterator = linkedEntries.iterator(); 289 290 return new Iterator<Map.Entry<K, V>>() { 291 Map.Entry<K, V> entry; 292 293 @Override 294 public boolean hasNext() { 295 return delegateIterator.hasNext(); 296 } 297 298 @Override 299 public Map.Entry<K, V> next() { 300 entry = delegateIterator.next(); 301 return entry; 302 } 303 304 @Override 305 public void remove() { 306 // Remove from iterator first to keep iterator valid. 307 delegateIterator.remove(); 308 LinkedHashMultimap.this.remove(entry.getKey(), entry.getValue()); 309 } 310 }; 311 } 312 313 /** 314 * {@inheritDoc} 315 * 316 * <p>If {@code values} is not empty and the multimap already contains a 317 * mapping for {@code key}, the {@code keySet()} ordering is unchanged. 318 * However, the provided values always come last in the {@link #entries()} and 319 * {@link #values()} iteration orderings. 320 */ 321 @Override public Set<V> replaceValues( 322 @Nullable K key, Iterable<? extends V> values) { 323 return super.replaceValues(key, values); 324 } 325 326 /** 327 * Returns a set of all key-value pairs. Changes to the returned set will 328 * update the underlying multimap, and vice versa. The entries set does not 329 * support the {@code add} or {@code addAll} operations. 330 * 331 * <p>The iterator generated by the returned set traverses the entries in the 332 * order they were added to the multimap. 333 * 334 * <p>Each entry is an immutable snapshot of a key-value mapping in the 335 * multimap, taken at the time the entry is returned by a method call to the 336 * collection or its iterator. 337 */ 338 @Override public Set<Map.Entry<K, V>> entries() { 339 return super.entries(); 340 } 341 342 /** 343 * Returns a collection of all values in the multimap. Changes to the returned 344 * collection will update the underlying multimap, and vice versa. 345 * 346 * <p>The iterator generated by the returned collection traverses the values 347 * in the order they were added to the multimap. 348 */ 349 @Override public Collection<V> values() { 350 return super.values(); 351 } 352 353 // Unfortunately, the entries() ordering does not determine the key ordering; 354 // see the example in the LinkedListMultimap class Javadoc. 355 356 /** 357 * @serialData the number of distinct keys, and then for each distinct key: 358 * the first key, the number of values for that key, and the key's values, 359 * followed by successive keys and values from the entries() ordering 360 */ 361 @GwtIncompatible("java.io.ObjectOutputStream") 362 private void writeObject(ObjectOutputStream stream) throws IOException { 363 stream.defaultWriteObject(); 364 stream.writeInt(expectedValuesPerKey); 365 Serialization.writeMultimap(this, stream); 366 for (Map.Entry<K, V> entry : linkedEntries) { 367 stream.writeObject(entry.getKey()); 368 stream.writeObject(entry.getValue()); 369 } 370 } 371 372 @GwtIncompatible("java.io.ObjectInputStream") 373 private void readObject(ObjectInputStream stream) 374 throws IOException, ClassNotFoundException { 375 stream.defaultReadObject(); 376 expectedValuesPerKey = stream.readInt(); 377 int distinctKeys = Serialization.readCount(stream); 378 setMap(new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys))); 379 linkedEntries = new LinkedHashSet<Map.Entry<K, V>>( 380 distinctKeys * expectedValuesPerKey); 381 Serialization.populateMultimap(this, stream, distinctKeys); 382 linkedEntries.clear(); // will clear and repopulate entries 383 for (int i = 0; i < size(); i++) { 384 @SuppressWarnings("unchecked") // reading data stored by writeObject 385 K key = (K) stream.readObject(); 386 @SuppressWarnings("unchecked") // reading data stored by writeObject 387 V value = (V) stream.readObject(); 388 linkedEntries.add(Maps.immutableEntry(key, value)); 389 } 390 } 391 392 @GwtIncompatible("java serialization not supported") 393 private static final long serialVersionUID = 0; 394 }