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