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 static com.google.common.base.Preconditions.checkArgument;
020
021 import com.google.common.annotations.GwtCompatible;
022 import com.google.common.annotations.GwtIncompatible;
023 import com.google.common.annotations.VisibleForTesting;
024 import com.google.common.base.Objects;
025 import com.google.common.primitives.Ints;
026
027 import java.io.IOException;
028 import java.io.ObjectInputStream;
029 import java.io.ObjectOutputStream;
030 import java.util.Arrays;
031 import java.util.Collection;
032 import java.util.ConcurrentModificationException;
033 import java.util.Iterator;
034 import java.util.LinkedHashMap;
035 import java.util.LinkedHashSet;
036 import java.util.Map;
037 import java.util.NoSuchElementException;
038 import java.util.Set;
039
040 import javax.annotation.Nullable;
041
042 /**
043 * Implementation of {@code Multimap} that does not allow duplicate key-value
044 * entries and that returns collections whose iterators follow the ordering in
045 * which the data was added to the multimap.
046 *
047 * <p>The collections returned by {@code keySet}, {@code keys}, and {@code
048 * asMap} iterate through the keys in the order they were first added to the
049 * multimap. Similarly, {@code get}, {@code removeAll}, and {@code
050 * replaceValues} return collections that iterate through the values in the
051 * order they were added. The collections generated by {@code entries} and
052 * {@code values} iterate across the key-value mappings in the order they were
053 * added to the multimap.
054 *
055 * <p>The iteration ordering of the collections generated by {@code keySet},
056 * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of
057 * keys remains unchanged, adding or removing mappings does not affect the key
058 * iteration order. However, if you remove all values associated with a key and
059 * then add the key back to the multimap, that key will come last in the key
060 * iteration order.
061 *
062 * <p>The multimap does not store duplicate key-value pairs. Adding a new
063 * key-value pair equal to an existing key-value pair has no effect.
064 *
065 * <p>Keys and values may be null. All optional multimap methods are supported,
066 * and all returned views are modifiable.
067 *
068 * <p>This class is not threadsafe when any concurrent operations update the
069 * multimap. Concurrent read operations will work correctly. To allow concurrent
070 * update operations, wrap your multimap with a call to {@link
071 * Multimaps#synchronizedSetMultimap}.
072 *
073 * <p>See the Guava User Guide article on <a href=
074 * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
075 * {@code Multimap}</a>.
076 *
077 * @author Jared Levy
078 * @author Louis Wasserman
079 * @since 2.0 (imported from Google Collections Library)
080 */
081 @GwtCompatible(serializable = true, emulated = true)
082 public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
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>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY);
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>(
104 Maps.capacity(expectedKeys),
105 Maps.capacity(expectedValuesPerKey));
106 }
107
108 /**
109 * Constructs a {@code LinkedHashMultimap} with the same mappings as the
110 * specified multimap. If a key-value mapping appears multiple times in the
111 * input multimap, it only appears once in the constructed multimap. The new
112 * multimap has the same {@link Multimap#entries()} iteration order as the
113 * input multimap, except for excluding duplicate mappings.
114 *
115 * @param multimap the multimap whose contents are copied to this multimap
116 */
117 public static <K, V> LinkedHashMultimap<K, V> create(
118 Multimap<? extends K, ? extends V> multimap) {
119 LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY);
120 result.putAll(multimap);
121 return result;
122 }
123
124 private interface ValueSetLink<K, V> {
125 ValueSetLink<K, V> getPredecessorInValueSet();
126 ValueSetLink<K, V> getSuccessorInValueSet();
127
128 void setPredecessorInValueSet(ValueSetLink<K, V> entry);
129 void setSuccessorInValueSet(ValueSetLink<K, V> entry);
130 }
131
132 private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) {
133 pred.setSuccessorInValueSet(succ);
134 succ.setPredecessorInValueSet(pred);
135 }
136
137 private static <K, V> void succeedsInMultimap(
138 ValueEntry<K, V> pred, ValueEntry<K, V> succ) {
139 pred.setSuccessorInMultimap(succ);
140 succ.setPredecessorInMultimap(pred);
141 }
142
143 private static <K, V> void deleteFromValueSet(ValueSetLink<K, V> entry) {
144 succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet());
145 }
146
147 private static <K, V> void deleteFromMultimap(ValueEntry<K, V> entry) {
148 succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap());
149 }
150
151 /**
152 * LinkedHashMultimap entries are in no less than three coexisting linked lists:
153 * a row in the hash table for a Set<V> associated with a key, the linked list
154 * of insertion-ordered entries in that Set<V>, and the linked list of entries
155 * in the LinkedHashMultimap as a whole.
156 */
157 private static final class ValueEntry<K, V> extends AbstractMapEntry<K, V>
158 implements ValueSetLink<K, V> {
159 final K key;
160 final V value;
161 final int valueHash;
162
163 @Nullable ValueEntry<K, V> nextInValueSetHashRow;
164
165 ValueSetLink<K, V> predecessorInValueSet;
166 ValueSetLink<K, V> successorInValueSet;
167
168 ValueEntry<K, V> predecessorInMultimap;
169 ValueEntry<K, V> successorInMultimap;
170
171 ValueEntry(@Nullable K key, @Nullable V value, int valueHash,
172 @Nullable ValueEntry<K, V> nextInValueSetHashRow) {
173 this.key = key;
174 this.value = value;
175 this.valueHash = valueHash;
176 this.nextInValueSetHashRow = nextInValueSetHashRow;
177 }
178
179
180 @Override
181 public K getKey() {
182 return key;
183 }
184
185
186 @Override
187 public V getValue() {
188 return value;
189 }
190
191
192 public ValueSetLink<K, V> getPredecessorInValueSet() {
193 return predecessorInValueSet;
194 }
195
196
197 public ValueSetLink<K, V> getSuccessorInValueSet() {
198 return successorInValueSet;
199 }
200
201
202 public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
203 predecessorInValueSet = entry;
204 }
205
206
207 public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
208 successorInValueSet = entry;
209 }
210
211 public ValueEntry<K, V> getPredecessorInMultimap() {
212 return predecessorInMultimap;
213 }
214
215 public ValueEntry<K, V> getSuccessorInMultimap() {
216 return successorInMultimap;
217 }
218
219 public void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) {
220 this.successorInMultimap = multimapSuccessor;
221 }
222
223 public void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) {
224 this.predecessorInMultimap = multimapPredecessor;
225 }
226 }
227
228 private static final int DEFAULT_KEY_CAPACITY = 16;
229 private static final int DEFAULT_VALUE_SET_CAPACITY = 2;
230
231 private static final int MAX_VALUE_SET_TABLE_SIZE = Ints.MAX_POWER_OF_TWO;
232
233 @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY;
234 private transient ValueEntry<K, V> multimapHeaderEntry;
235
236 private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) {
237 super(new LinkedHashMap<K, Collection<V>>(keyCapacity));
238
239 checkArgument(valueSetCapacity >= 0,
240 "expectedValuesPerKey must be >= 0 but was %s", valueSetCapacity);
241
242 this.valueSetCapacity = valueSetCapacity;
243 this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
244 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
245 }
246
247 /**
248 * {@inheritDoc}
249 *
250 * <p>Creates an empty {@code LinkedHashSet} for a collection of values for
251 * one key.
252 *
253 * @return a new {@code LinkedHashSet} containing a collection of values for
254 * one key
255 */
256
257 @Override
258 Set<V> createCollection() {
259 return new LinkedHashSet<V>(valueSetCapacity);
260 }
261
262 /**
263 * {@inheritDoc}
264 *
265 * <p>Creates a decorated insertion-ordered set that also keeps track of the
266 * order in which key-value pairs are added to the multimap.
267 *
268 * @param key key to associate with values in the collection
269 * @return a new decorated set containing a collection of values for one key
270 */
271
272 @Override
273 Collection<V> createCollection(K key) {
274 return new ValueSet(key, valueSetCapacity);
275 }
276
277 /**
278 * {@inheritDoc}
279 *
280 * <p>If {@code values} is not empty and the multimap already contains a
281 * mapping for {@code key}, the {@code keySet()} ordering is unchanged.
282 * However, the provided values always come last in the {@link #entries()} and
283 * {@link #values()} iteration orderings.
284 */
285
286 @Override
287 public Set<V> replaceValues(K key, Iterable<? extends V> values) {
288 return super.replaceValues(key, values);
289 }
290
291 /**
292 * Returns a set of all key-value pairs. Changes to the returned set will
293 * update the underlying multimap, and vice versa. The entries set does not
294 * support the {@code add} or {@code addAll} operations.
295 *
296 * <p>The iterator generated by the returned set traverses the entries in the
297 * order they were added to the multimap.
298 *
299 * <p>Each entry is an immutable snapshot of a key-value mapping in the
300 * multimap, taken at the time the entry is returned by a method call to the
301 * collection or its iterator.
302 */
303
304 @Override
305 public Set<Map.Entry<K, V>> entries() {
306 return super.entries();
307 }
308
309 /**
310 * Returns a collection of all values in the multimap. Changes to the returned
311 * collection will update the underlying multimap, and vice versa.
312 *
313 * <p>The iterator generated by the returned collection traverses the values
314 * in the order they were added to the multimap.
315 */
316
317 @Override
318 public Collection<V> values() {
319 return super.values();
320 }
321
322 @VisibleForTesting
323 final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> {
324 /*
325 * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory
326 * consumption.
327 */
328
329 private final K key;
330 private ValueEntry<K, V>[] hashTable;
331 private int size = 0;
332 private int modCount = 0;
333
334 // We use the set object itself as the end of the linked list, avoiding an unnecessary
335 // entry object per key.
336 private ValueSetLink<K, V> firstEntry;
337 private ValueSetLink<K, V> lastEntry;
338
339 ValueSet(K key, int expectedValues) {
340 this.key = key;
341 this.firstEntry = this;
342 this.lastEntry = this;
343 // Round expected values up to a power of 2 to get the table size.
344 int tableSize = Integer.highestOneBit(Math.max(expectedValues, 2) - 1) << 1;
345 if (tableSize < 0) {
346 tableSize = MAX_VALUE_SET_TABLE_SIZE;
347 }
348
349 @SuppressWarnings("unchecked")
350 ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize];
351 this.hashTable = hashTable;
352 }
353
354
355 public ValueSetLink<K, V> getPredecessorInValueSet() {
356 return lastEntry;
357 }
358
359
360 public ValueSetLink<K, V> getSuccessorInValueSet() {
361 return firstEntry;
362 }
363
364
365 public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
366 lastEntry = entry;
367 }
368
369
370 public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
371 firstEntry = entry;
372 }
373
374
375 @Override
376 public Iterator<V> iterator() {
377 return new Iterator<V>() {
378 ValueSetLink<K, V> nextEntry = firstEntry;
379 ValueEntry<K, V> toRemove;
380 int expectedModCount = modCount;
381
382 private void checkForComodification() {
383 if (modCount != expectedModCount) {
384 throw new ConcurrentModificationException();
385 }
386 }
387
388
389 public boolean hasNext() {
390 checkForComodification();
391 return nextEntry != ValueSet.this;
392 }
393
394
395 public V next() {
396 if (!hasNext()) {
397 throw new NoSuchElementException();
398 }
399 ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry;
400 V result = entry.getValue();
401 toRemove = entry;
402 nextEntry = entry.getSuccessorInValueSet();
403 return result;
404 }
405
406
407 public void remove() {
408 checkForComodification();
409 Iterators.checkRemove(toRemove != null);
410 Object o = toRemove.getValue();
411 int hash = (o == null) ? 0 : o.hashCode();
412 int row = Hashing.smear(hash) & (hashTable.length - 1);
413 ValueEntry<K, V> prev = null;
414 for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
415 prev = entry, entry = entry.nextInValueSetHashRow) {
416 if (entry == toRemove) {
417 if (prev == null) {
418 // first entry in row
419 hashTable[row] = entry.nextInValueSetHashRow;
420 } else {
421 prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
422 }
423 deleteFromValueSet(toRemove);
424 deleteFromMultimap(toRemove);
425 size--;
426 expectedModCount = ++modCount;
427 break;
428 }
429 }
430 toRemove = null;
431 }
432 };
433 }
434
435
436 @Override
437 public int size() {
438 return size;
439 }
440
441
442 @Override
443 public boolean contains(@Nullable Object o) {
444 int hash = (o == null) ? 0 : o.hashCode();
445 int row = Hashing.smear(hash) & (hashTable.length - 1);
446
447 for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
448 entry = entry.nextInValueSetHashRow) {
449 if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
450 return true;
451 }
452 }
453 return false;
454 }
455
456 /**
457 * The threshold above which the hash table should be rebuilt.
458 */
459 @VisibleForTesting int threshold() {
460 return hashTable.length; // load factor of 1.0
461 }
462
463
464 @Override
465 public boolean add(@Nullable V value) {
466 int hash = (value == null) ? 0 : value.hashCode();
467 int row = Hashing.smear(hash) & (hashTable.length - 1);
468
469 ValueEntry<K, V> rowHead = hashTable[row];
470 for (ValueEntry<K, V> entry = rowHead; entry != null;
471 entry = entry.nextInValueSetHashRow) {
472 if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) {
473 return false;
474 }
475 }
476
477 ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead);
478 succeedsInValueSet(lastEntry, newEntry);
479 succeedsInValueSet(newEntry, this);
480 succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry);
481 succeedsInMultimap(newEntry, multimapHeaderEntry);
482 hashTable[row] = newEntry;
483 size++;
484 modCount++;
485 rehashIfNecessary();
486 return true;
487 }
488
489 private void rehashIfNecessary() {
490 if (size > threshold() && hashTable.length < MAX_VALUE_SET_TABLE_SIZE) {
491 @SuppressWarnings("unchecked")
492 ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2];
493 this.hashTable = hashTable;
494 int mask = hashTable.length - 1;
495 for (ValueSetLink<K, V> entry = firstEntry;
496 entry != this; entry = entry.getSuccessorInValueSet()) {
497 ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
498 int row = Hashing.smear(valueEntry.valueHash) & mask;
499 valueEntry.nextInValueSetHashRow = hashTable[row];
500 hashTable[row] = valueEntry;
501 }
502 }
503 }
504
505
506 @Override
507 public boolean remove(@Nullable Object o) {
508 int hash = (o == null) ? 0 : o.hashCode();
509 int row = Hashing.smear(hash) & (hashTable.length - 1);
510
511 ValueEntry<K, V> prev = null;
512 for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
513 prev = entry, entry = entry.nextInValueSetHashRow) {
514 if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
515 if (prev == null) {
516 // first entry in the row
517 hashTable[row] = entry.nextInValueSetHashRow;
518 } else {
519 prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
520 }
521 deleteFromValueSet(entry);
522 deleteFromMultimap(entry);
523 size--;
524 modCount++;
525 return true;
526 }
527 }
528 return false;
529 }
530
531
532 @Override
533 public void clear() {
534 Arrays.fill(hashTable, null);
535 size = 0;
536 for (ValueSetLink<K, V> entry = firstEntry;
537 entry != this; entry = entry.getSuccessorInValueSet()) {
538 ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
539 deleteFromMultimap(valueEntry);
540 }
541 succeedsInValueSet(this, this);
542 modCount++;
543 }
544 }
545
546
547 @Override
548 Iterator<Map.Entry<K, V>> createEntryIterator() {
549 return new Iterator<Map.Entry<K, V>>() {
550 ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap;
551 ValueEntry<K, V> toRemove;
552
553
554 public boolean hasNext() {
555 return nextEntry != multimapHeaderEntry;
556 }
557
558
559 public Map.Entry<K, V> next() {
560 if (!hasNext()) {
561 throw new NoSuchElementException();
562 }
563 ValueEntry<K, V> result = nextEntry;
564 toRemove = result;
565 nextEntry = nextEntry.successorInMultimap;
566 return result;
567 }
568
569
570 public void remove() {
571 Iterators.checkRemove(toRemove != null);
572 LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue());
573 toRemove = null;
574 }
575 };
576 }
577
578
579 @Override
580 public void clear() {
581 super.clear();
582 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
583 }
584
585 /**
586 * @serialData the expected values per key, the number of distinct keys,
587 * the number of entries, and the entries in order
588 */
589 @GwtIncompatible("java.io.ObjectOutputStream")
590 private void writeObject(ObjectOutputStream stream) throws IOException {
591 stream.defaultWriteObject();
592 stream.writeInt(valueSetCapacity);
593 stream.writeInt(keySet().size());
594 for (K key : keySet()) {
595 stream.writeObject(key);
596 }
597 stream.writeInt(size());
598 for (Map.Entry<K, V> entry : entries()) {
599 stream.writeObject(entry.getKey());
600 stream.writeObject(entry.getValue());
601 }
602 }
603
604 @GwtIncompatible("java.io.ObjectInputStream")
605 private void readObject(ObjectInputStream stream)
606 throws IOException, ClassNotFoundException {
607 stream.defaultReadObject();
608 multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
609 succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
610 valueSetCapacity = stream.readInt();
611 int distinctKeys = stream.readInt();
612 Map<K, Collection<V>> map =
613 new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys));
614 for (int i = 0; i < distinctKeys; i++) {
615 @SuppressWarnings("unchecked")
616 K key = (K) stream.readObject();
617 map.put(key, createCollection(key));
618 }
619 int entries = stream.readInt();
620 for (int i = 0; i < entries; i++) {
621 @SuppressWarnings("unchecked")
622 K key = (K) stream.readObject();
623 @SuppressWarnings("unchecked")
624 V value = (V) stream.readObject();
625 map.get(key).add(value);
626 }
627 setMap(map);
628 }
629
630 @GwtIncompatible("java serialization not supported")
631 private static final long serialVersionUID = 1;
632 }