001/*
002 * Copyright (C) 2009 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;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.base.MoreObjects;
025import com.google.common.base.Preconditions;
026import com.google.errorprone.annotations.CanIgnoreReturnValue;
027import com.google.errorprone.annotations.concurrent.LazyInit;
028import com.google.j2objc.annotations.RetainedWith;
029import com.google.j2objc.annotations.Weak;
030import java.io.IOException;
031import java.io.InvalidObjectException;
032import java.io.ObjectInputStream;
033import java.io.ObjectOutputStream;
034import java.util.Arrays;
035import java.util.Collection;
036import java.util.Comparator;
037import java.util.List;
038import java.util.Map;
039import java.util.Map.Entry;
040import java.util.function.Function;
041import java.util.stream.Collector;
042import java.util.stream.Collectors;
043import java.util.stream.Stream;
044import javax.annotation.Nullable;
045
046/**
047 * A {@link SetMultimap} whose contents will never change, with many other important properties
048 * detailed at {@link ImmutableCollection}.
049 *
050 * <p>See the Guava User Guide article on <a href=
051 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">
052 * immutable collections</a>.
053 *
054 * @author Mike Ward
055 * @since 2.0
056 */
057@GwtCompatible(serializable = true, emulated = true)
058public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V>
059    implements SetMultimap<K, V> {
060  /**
061   * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap}
062   * whose keys and values are the result of applying the provided mapping functions to the input
063   * elements.
064   *
065   * <p>For streams with {@linkplain java.util.stream#Ordering defined encounter order}, that order
066   * is preserved, but entries are {@linkplain ImmutableMultimap#iteration grouped by key}.
067   *
068   * Example:
069   * <pre>   {@code
070   *
071   *   static final Multimap<Character, String> FIRST_LETTER_MULTIMAP =
072   *       Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
073   *           .collect(toImmutableSetMultimap(str -> str.charAt(0), str -> str.substring(1)));
074   *
075   *   // is equivalent to
076   *
077   *   static final Multimap<Character, String> FIRST_LETTER_MULTIMAP =
078   *       new ImmutableSetMultimap.Builder<Character, String>()
079   *           .put('b', "anana")
080   *           .putAll('a', "pple", "sparagus")
081   *           .putAll('c', "arrot", "herry")
082   *           .build();}</pre>
083   * @since 21.0
084   */
085  @Beta
086  public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
087      Function<? super T, ? extends K> keyFunction,
088      Function<? super T, ? extends V> valueFunction) {
089    checkNotNull(keyFunction, "keyFunction");
090    checkNotNull(valueFunction, "valueFunction");
091    return Collector.of(
092        ImmutableSetMultimap::<K, V>builder,
093        (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)),
094        ImmutableSetMultimap.Builder::combine,
095        ImmutableSetMultimap.Builder::build);
096  }
097
098  /**
099   * Returns a {@code Collector} accumulating entries into an {@code ImmutableSetMultimap}. Each
100   * input element is mapped to a key and a stream of values, each of which are put into the
101   * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the
102   * streams of values.
103   *
104   * <p>Example:
105   *
106   * <pre>{@code
107   * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP =
108   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
109   *         .collect(
110   *             flatteningToImmutableSetMultimap(
111   *                  str -> str.charAt(0),
112   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
113   *
114   * // is equivalent to
115   *
116   * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP =
117   *     ImmutableSetMultimap.<Character, Character>builder()
118   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
119   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
120   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
121   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
122   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
123   *         .build();
124   *
125   * // after deduplication, the resulting multimap is equivalent to
126   *
127   * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP =
128   *     ImmutableSetMultimap.<Character, Character>builder()
129   *         .putAll('b', Arrays.asList('a', 'n'))
130   *         .putAll('a', Arrays.asList('p', 'l', 'e', 's', 'a', 'r', 'g', 'u'))
131   *         .putAll('c', Arrays.asList('a', 'r', 'o', 't', 'h', 'e', 'y'))
132   *         .build();
133   * }
134   * }</pre>
135   *
136   * @since 21.0
137   */
138  @Beta
139  public static <T, K, V>
140      Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap(
141          Function<? super T, ? extends K> keyFunction,
142          Function<? super T, ? extends Stream<? extends V>> valuesFunction) {
143    checkNotNull(keyFunction);
144    checkNotNull(valuesFunction);
145    return Collectors.collectingAndThen(
146        Multimaps.flatteningToMultimap(
147            input -> checkNotNull(keyFunction.apply(input)),
148            input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull),
149            MultimapBuilder.linkedHashKeys().linkedHashSetValues()::<K, V>build),
150        ImmutableSetMultimap::copyOf);
151  }
152
153  /** Returns the empty multimap. */
154  // Casting is safe because the multimap will never hold any elements.
155  @SuppressWarnings("unchecked")
156  public static <K, V> ImmutableSetMultimap<K, V> of() {
157    return (ImmutableSetMultimap<K, V>) EmptyImmutableSetMultimap.INSTANCE;
158  }
159
160  /**
161   * Returns an immutable multimap containing a single entry.
162   */
163  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) {
164    ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
165    builder.put(k1, v1);
166    return builder.build();
167  }
168
169  /**
170   * Returns an immutable multimap containing the given entries, in order.
171   * Repeated occurrences of an entry (according to {@link Object#equals}) after
172   * the first are ignored.
173   */
174  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) {
175    ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
176    builder.put(k1, v1);
177    builder.put(k2, v2);
178    return builder.build();
179  }
180
181  /**
182   * Returns an immutable multimap containing the given entries, in order.
183   * Repeated occurrences of an entry (according to {@link Object#equals}) after
184   * the first are ignored.
185   */
186  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
187    ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
188    builder.put(k1, v1);
189    builder.put(k2, v2);
190    builder.put(k3, v3);
191    return builder.build();
192  }
193
194  /**
195   * Returns an immutable multimap containing the given entries, in order.
196   * Repeated occurrences of an entry (according to {@link Object#equals}) after
197   * the first are ignored.
198   */
199  public static <K, V> ImmutableSetMultimap<K, V> of(
200      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
201    ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
202    builder.put(k1, v1);
203    builder.put(k2, v2);
204    builder.put(k3, v3);
205    builder.put(k4, v4);
206    return builder.build();
207  }
208
209  /**
210   * Returns an immutable multimap containing the given entries, in order.
211   * Repeated occurrences of an entry (according to {@link Object#equals}) after
212   * the first are ignored.
213   */
214  public static <K, V> ImmutableSetMultimap<K, V> of(
215      K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
216    ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder();
217    builder.put(k1, v1);
218    builder.put(k2, v2);
219    builder.put(k3, v3);
220    builder.put(k4, v4);
221    builder.put(k5, v5);
222    return builder.build();
223  }
224
225  // looking for of() with > 5 entries? Use the builder instead.
226
227  /**
228   * Returns a new {@link Builder}.
229   */
230  public static <K, V> Builder<K, V> builder() {
231    return new Builder<K, V>();
232  }
233
234  /**
235   * A builder for creating immutable {@code SetMultimap} instances, especially
236   * {@code public static final} multimaps ("constant multimaps"). Example:
237   * <pre>   {@code
238   *
239   *   static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP =
240   *       new ImmutableSetMultimap.Builder<String, Integer>()
241   *           .put("one", 1)
242   *           .putAll("several", 1, 2, 3)
243   *           .putAll("many", 1, 2, 3, 4, 5)
244   *           .build();}</pre>
245   *
246   * <p>Builder instances can be reused; it is safe to call {@link #build} multiple
247   * times to build multiple multimaps in series. Each multimap contains the
248   * key-value mappings in the previously created multimaps.
249   *
250   * @since 2.0
251   */
252  public static final class Builder<K, V> extends ImmutableMultimap.Builder<K, V> {
253    /**
254     * Creates a new builder. The returned builder is equivalent to the builder
255     * generated by {@link ImmutableSetMultimap#builder}.
256     */
257    public Builder() {
258      super(MultimapBuilder.linkedHashKeys().linkedHashSetValues().<K, V>build());
259    }
260
261    /**
262     * Adds a key-value mapping to the built multimap if it is not already
263     * present.
264     */
265    @CanIgnoreReturnValue
266    @Override
267    public Builder<K, V> put(K key, V value) {
268      builderMultimap.put(checkNotNull(key), checkNotNull(value));
269      return this;
270    }
271
272    /**
273     * Adds an entry to the built multimap if it is not already present.
274     *
275     * @since 11.0
276     */
277    @CanIgnoreReturnValue
278    @Override
279    public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
280      builderMultimap.put(checkNotNull(entry.getKey()), checkNotNull(entry.getValue()));
281      return this;
282    }
283
284    /**
285     * {@inheritDoc}
286     *
287     * @since 19.0
288     */
289    @CanIgnoreReturnValue
290    @Beta
291    @Override
292    public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) {
293      super.putAll(entries);
294      return this;
295    }
296
297    @CanIgnoreReturnValue
298    @Override
299    public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
300      Collection<V> collection = builderMultimap.get(checkNotNull(key));
301      for (V value : values) {
302        collection.add(checkNotNull(value));
303      }
304      return this;
305    }
306
307    @CanIgnoreReturnValue
308    @Override
309    public Builder<K, V> putAll(K key, V... values) {
310      return putAll(key, Arrays.asList(values));
311    }
312
313    @CanIgnoreReturnValue
314    @Override
315    public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) {
316      for (Entry<? extends K, ? extends Collection<? extends V>> entry :
317          multimap.asMap().entrySet()) {
318        putAll(entry.getKey(), entry.getValue());
319      }
320      return this;
321    }
322
323    @CanIgnoreReturnValue
324    @Override
325    Builder<K, V> combine(ImmutableMultimap.Builder<K, V> other) {
326      super.combine(other);
327      return this;
328    }
329
330    /**
331     * {@inheritDoc}
332     *
333     * @since 8.0
334     */
335    @CanIgnoreReturnValue
336    @Override
337    public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
338      this.keyComparator = checkNotNull(keyComparator);
339      return this;
340    }
341
342    /**
343     * Specifies the ordering of the generated multimap's values for each key.
344     *
345     * <p>If this method is called, the sets returned by the {@code get()}
346     * method of the generated multimap and its {@link Multimap#asMap()} view
347     * are {@link ImmutableSortedSet} instances. However, serialization does not
348     * preserve that property, though it does maintain the key and value
349     * ordering.
350     *
351     * @since 8.0
352     */
353    // TODO: Make serialization behavior consistent.
354    @CanIgnoreReturnValue
355    @Override
356    public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
357      super.orderValuesBy(valueComparator);
358      return this;
359    }
360
361    /**
362     * Returns a newly-created immutable set multimap.
363     */
364    @Override
365    public ImmutableSetMultimap<K, V> build() {
366      if (keyComparator != null) {
367        Multimap<K, V> sortedCopy =
368            MultimapBuilder.linkedHashKeys().linkedHashSetValues().<K, V>build();
369        List<Map.Entry<K, Collection<V>>> entries =
370            Ordering.from(keyComparator)
371                .<K>onKeys()
372                .immutableSortedCopy(builderMultimap.asMap().entrySet());
373        for (Map.Entry<K, Collection<V>> entry : entries) {
374          sortedCopy.putAll(entry.getKey(), entry.getValue());
375        }
376        builderMultimap = sortedCopy;
377      }
378      return copyOf(builderMultimap, valueComparator);
379    }
380  }
381
382  /**
383   * Returns an immutable set multimap containing the same mappings as
384   * {@code multimap}. The generated multimap's key and value orderings
385   * correspond to the iteration ordering of the {@code multimap.asMap()} view.
386   * Repeated occurrences of an entry in the multimap after the first are
387   * ignored.
388   *
389   * <p>Despite the method name, this method attempts to avoid actually copying
390   * the data when it is safe to do so. The exact circumstances under which a
391   * copy will or will not be performed are undocumented and subject to change.
392   *
393   * @throws NullPointerException if any key or value in {@code multimap} is
394   *     null
395   */
396  public static <K, V> ImmutableSetMultimap<K, V> copyOf(
397      Multimap<? extends K, ? extends V> multimap) {
398    return copyOf(multimap, null);
399  }
400
401  private static <K, V> ImmutableSetMultimap<K, V> copyOf(
402      Multimap<? extends K, ? extends V> multimap, Comparator<? super V> valueComparator) {
403    checkNotNull(multimap); // eager for GWT
404    if (multimap.isEmpty() && valueComparator == null) {
405      return of();
406    }
407
408    if (multimap instanceof ImmutableSetMultimap) {
409      @SuppressWarnings("unchecked") // safe since multimap is not writable
410      ImmutableSetMultimap<K, V> kvMultimap = (ImmutableSetMultimap<K, V>) multimap;
411      if (!kvMultimap.isPartialView()) {
412        return kvMultimap;
413      }
414    }
415
416    ImmutableMap.Builder<K, ImmutableSet<V>> builder =
417        new ImmutableMap.Builder<K, ImmutableSet<V>>(multimap.asMap().size());
418    int size = 0;
419
420    for (Entry<? extends K, ? extends Collection<? extends V>> entry :
421        multimap.asMap().entrySet()) {
422      K key = entry.getKey();
423      Collection<? extends V> values = entry.getValue();
424      ImmutableSet<V> set = valueSet(valueComparator, values);
425      if (!set.isEmpty()) {
426        builder.put(key, set);
427        size += set.size();
428      }
429    }
430
431    return new ImmutableSetMultimap<K, V>(builder.build(), size, valueComparator);
432  }
433
434  /**
435   * Returns an immutable multimap containing the specified entries.  The
436   * returned multimap iterates over keys in the order they were first
437   * encountered in the input, and the values for each key are iterated in the
438   * order they were encountered.  If two values for the same key are
439   * {@linkplain Object#equals equal}, the first value encountered is used.
440   *
441   * @throws NullPointerException if any key, value, or entry is null
442   * @since 19.0
443   */
444  @Beta
445  public static <K, V> ImmutableSetMultimap<K, V> copyOf(
446      Iterable<? extends Entry<? extends K, ? extends V>> entries) {
447    return new Builder<K, V>().putAll(entries).build();
448  }
449
450  /**
451   * Returned by get() when a missing key is provided. Also holds the
452   * comparator, if any, used for values.
453   */
454  private final transient ImmutableSet<V> emptySet;
455
456  ImmutableSetMultimap(
457      ImmutableMap<K, ImmutableSet<V>> map,
458      int size,
459      @Nullable Comparator<? super V> valueComparator) {
460    super(map, size);
461    this.emptySet = emptySet(valueComparator);
462  }
463
464  // views
465
466  /**
467   * Returns an immutable set of the values for the given key.  If no mappings
468   * in the multimap have the provided key, an empty immutable set is returned.
469   * The values are in the same order as the parameters used to build this
470   * multimap.
471   */
472  @Override
473  public ImmutableSet<V> get(@Nullable K key) {
474    // This cast is safe as its type is known in constructor.
475    ImmutableSet<V> set = (ImmutableSet<V>) map.get(key);
476    return MoreObjects.firstNonNull(set, emptySet);
477  }
478
479  @LazyInit
480  @RetainedWith
481  private transient ImmutableSetMultimap<V, K> inverse;
482
483  /**
484   * {@inheritDoc}
485   *
486   * <p>Because an inverse of a set multimap cannot contain multiple pairs with
487   * the same key and value, this method returns an {@code ImmutableSetMultimap}
488   * rather than the {@code ImmutableMultimap} specified in the {@code
489   * ImmutableMultimap} class.
490   *
491   * @since 11.0
492   */
493  public ImmutableSetMultimap<V, K> inverse() {
494    ImmutableSetMultimap<V, K> result = inverse;
495    return (result == null) ? (inverse = invert()) : result;
496  }
497
498  private ImmutableSetMultimap<V, K> invert() {
499    Builder<V, K> builder = builder();
500    for (Entry<K, V> entry : entries()) {
501      builder.put(entry.getValue(), entry.getKey());
502    }
503    ImmutableSetMultimap<V, K> invertedMultimap = builder.build();
504    invertedMultimap.inverse = this;
505    return invertedMultimap;
506  }
507
508  /**
509   * Guaranteed to throw an exception and leave the multimap unmodified.
510   *
511   * @throws UnsupportedOperationException always
512   * @deprecated Unsupported operation.
513   */
514  @CanIgnoreReturnValue
515  @Deprecated
516  @Override
517  public ImmutableSet<V> removeAll(Object key) {
518    throw new UnsupportedOperationException();
519  }
520
521  /**
522   * Guaranteed to throw an exception and leave the multimap unmodified.
523   *
524   * @throws UnsupportedOperationException always
525   * @deprecated Unsupported operation.
526   */
527  @CanIgnoreReturnValue
528  @Deprecated
529  @Override
530  public ImmutableSet<V> replaceValues(K key, Iterable<? extends V> values) {
531    throw new UnsupportedOperationException();
532  }
533
534  private transient ImmutableSet<Entry<K, V>> entries;
535
536  /**
537   * Returns an immutable collection of all key-value pairs in the multimap.
538   * Its iterator traverses the values for the first key, the values for the
539   * second key, and so on.
540   */
541  @Override
542  public ImmutableSet<Entry<K, V>> entries() {
543    ImmutableSet<Entry<K, V>> result = entries;
544    return result == null ? (entries = new EntrySet<K, V>(this)) : result;
545  }
546
547  private static final class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> {
548    @Weak private final transient ImmutableSetMultimap<K, V> multimap;
549
550    EntrySet(ImmutableSetMultimap<K, V> multimap) {
551      this.multimap = multimap;
552    }
553
554    @Override
555    public boolean contains(@Nullable Object object) {
556      if (object instanceof Entry) {
557        Entry<?, ?> entry = (Entry<?, ?>) object;
558        return multimap.containsEntry(entry.getKey(), entry.getValue());
559      }
560      return false;
561    }
562
563    @Override
564    public int size() {
565      return multimap.size();
566    }
567
568    @Override
569    public UnmodifiableIterator<Entry<K, V>> iterator() {
570      return multimap.entryIterator();
571    }
572
573    @Override
574    boolean isPartialView() {
575      return false;
576    }
577  }
578
579  private static <V> ImmutableSet<V> valueSet(
580      @Nullable Comparator<? super V> valueComparator, Collection<? extends V> values) {
581    return (valueComparator == null)
582        ? ImmutableSet.copyOf(values)
583        : ImmutableSortedSet.copyOf(valueComparator, values);
584  }
585
586  private static <V> ImmutableSet<V> emptySet(@Nullable Comparator<? super V> valueComparator) {
587    return (valueComparator == null)
588        ? ImmutableSet.<V>of()
589        : ImmutableSortedSet.<V>emptySet(valueComparator);
590  }
591
592  private static <V> ImmutableSet.Builder<V> valuesBuilder(
593      @Nullable Comparator<? super V> valueComparator) {
594    return (valueComparator == null)
595        ? new ImmutableSet.Builder<V>()
596        : new ImmutableSortedSet.Builder<V>(valueComparator);
597  }
598
599  /**
600   * @serialData number of distinct keys, and then for each distinct key: the
601   *     key, the number of values for that key, and the key's values
602   */
603  @GwtIncompatible // java.io.ObjectOutputStream
604  private void writeObject(ObjectOutputStream stream) throws IOException {
605    stream.defaultWriteObject();
606    stream.writeObject(valueComparator());
607    Serialization.writeMultimap(this, stream);
608  }
609
610  @Nullable
611  Comparator<? super V> valueComparator() {
612    return emptySet instanceof ImmutableSortedSet
613        ? ((ImmutableSortedSet<V>) emptySet).comparator()
614        : null;
615  }
616
617  @GwtIncompatible // java.io.ObjectInputStream
618  // Serialization type safety is at the caller's mercy.
619  @SuppressWarnings("unchecked")
620  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
621    stream.defaultReadObject();
622    Comparator<Object> valueComparator = (Comparator<Object>) stream.readObject();
623    int keyCount = stream.readInt();
624    if (keyCount < 0) {
625      throw new InvalidObjectException("Invalid key count " + keyCount);
626    }
627    ImmutableMap.Builder<Object, ImmutableSet<Object>> builder = ImmutableMap.builder();
628    int tmpSize = 0;
629
630    for (int i = 0; i < keyCount; i++) {
631      Object key = stream.readObject();
632      int valueCount = stream.readInt();
633      if (valueCount <= 0) {
634        throw new InvalidObjectException("Invalid value count " + valueCount);
635      }
636
637      ImmutableSet.Builder<Object> valuesBuilder = valuesBuilder(valueComparator);
638      for (int j = 0; j < valueCount; j++) {
639        valuesBuilder.add(stream.readObject());
640      }
641      ImmutableSet<Object> valueSet = valuesBuilder.build();
642      if (valueSet.size() != valueCount) {
643        throw new InvalidObjectException("Duplicate key-value pairs exist for key " + key);
644      }
645      builder.put(key, valueSet);
646      tmpSize += valueCount;
647    }
648
649    ImmutableMap<Object, ImmutableSet<Object>> tmpMap;
650    try {
651      tmpMap = builder.build();
652    } catch (IllegalArgumentException e) {
653      throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e);
654    }
655
656    FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap);
657    FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize);
658    FieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator));
659  }
660
661  @GwtIncompatible // not needed in emulated source.
662  private static final long serialVersionUID = 0;
663}