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