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