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