Coverage Summary for Class: ImmutableSetMultimap (com.google.common.collect)

Class Method, % Line, %
ImmutableSetMultimap 26.9% (7/26) 21.7% (25/115)
ImmutableSetMultimap$Builder 33.3% (4/12) 30.8% (8/26)
ImmutableSetMultimap$EntrySet 0% (0/5) 0% (0/10)
ImmutableSetMultimap$SetFieldSettersHolder 0% (0/2) 0% (0/2)
Total 24.4% (11/45) 21.6% (33/153)


1 /* 2  * Copyright (C) 2009 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  * http://www.apache.org/licenses/LICENSE-2.0 9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16  17 package com.google.common.collect; 18  19 import static com.google.common.base.Preconditions.checkNotNull; 20  21 import com.google.common.annotations.Beta; 22 import com.google.common.annotations.GwtCompatible; 23 import com.google.common.annotations.GwtIncompatible; 24 import com.google.common.base.MoreObjects; 25 import com.google.errorprone.annotations.CanIgnoreReturnValue; 26 import com.google.errorprone.annotations.DoNotCall; 27 import com.google.errorprone.annotations.concurrent.LazyInit; 28 import com.google.j2objc.annotations.RetainedWith; 29 import com.google.j2objc.annotations.Weak; 30 import java.io.IOException; 31 import java.io.InvalidObjectException; 32 import java.io.ObjectInputStream; 33 import java.io.ObjectOutputStream; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Comparator; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.function.Function; 40 import java.util.stream.Collector; 41 import java.util.stream.Stream; 42 import org.checkerframework.checker.nullness.qual.Nullable; 43  44 /** 45  * A {@link SetMultimap} whose contents will never change, with many other important properties 46  * detailed at {@link ImmutableCollection}. 47  * 48  * <p><b>Warning:</b> As in all {@link SetMultimap}s, do not modify either a key <i>or a value</i> 49  * of a {@code ImmutableSetMultimap} in a way that affects its {@link Object#equals} behavior. 50  * Undefined behavior and bugs will result. 51  * 52  * <p>See the Guava User Guide article on <a href= 53  * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>. 54  * 55  * @author Mike Ward 56  * @since 2.0 57  */ 58 @GwtCompatible(serializable = true, emulated = true) 59 public class ImmutableSetMultimap<K, V> extends ImmutableMultimap<K, V> 60  implements SetMultimap<K, V> { 61  /** 62  * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap} 63  * whose keys and values are the result of applying the provided mapping functions to the input 64  * elements. 65  * 66  * <p>For streams with defined encounter order (as defined in the Ordering section of the {@link 67  * java.util.stream} Javadoc), that order is preserved, but entries are <a 68  * href="ImmutableMultimap.html#iteration">grouped by key</a>. 69  * 70  * <p>Example: 71  * 72  * <pre>{@code 73  * static final Multimap<Character, String> FIRST_LETTER_MULTIMAP = 74  * Stream.of("banana", "apple", "carrot", "asparagus", "cherry") 75  * .collect(toImmutableSetMultimap(str -> str.charAt(0), str -> str.substring(1))); 76  * 77  * // is equivalent to 78  * 79  * static final Multimap<Character, String> FIRST_LETTER_MULTIMAP = 80  * new ImmutableSetMultimap.Builder<Character, String>() 81  * .put('b', "anana") 82  * .putAll('a', "pple", "sparagus") 83  * .putAll('c', "arrot", "herry") 84  * .build(); 85  * }</pre> 86  * 87  * @since 21.0 88  */ 89  public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap( 90  Function<? super T, ? extends K> keyFunction, 91  Function<? super T, ? extends V> valueFunction) { 92  return CollectCollectors.toImmutableSetMultimap(keyFunction, valueFunction); 93  } 94  95  /** 96  * Returns a {@code Collector} accumulating entries into an {@code ImmutableSetMultimap}. Each 97  * input element is mapped to a key and a stream of values, each of which are put into the 98  * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the 99  * streams of values. 100  * 101  * <p>Example: 102  * 103  * <pre>{@code 104  * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP = 105  * Stream.of("banana", "apple", "carrot", "asparagus", "cherry") 106  * .collect( 107  * flatteningToImmutableSetMultimap( 108  * str -> str.charAt(0), 109  * str -> str.substring(1).chars().mapToObj(c -> (char) c)); 110  * 111  * // is equivalent to 112  * 113  * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP = 114  * ImmutableSetMultimap.<Character, Character>builder() 115  * .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a')) 116  * .putAll('a', Arrays.asList('p', 'p', 'l', 'e')) 117  * .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't')) 118  * .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's')) 119  * .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y')) 120  * .build(); 121  * 122  * // after deduplication, the resulting multimap is equivalent to 123  * 124  * static final ImmutableSetMultimap<Character, Character> FIRST_LETTER_MULTIMAP = 125  * ImmutableSetMultimap.<Character, Character>builder() 126  * .putAll('b', Arrays.asList('a', 'n')) 127  * .putAll('a', Arrays.asList('p', 'l', 'e', 's', 'a', 'r', 'g', 'u')) 128  * .putAll('c', Arrays.asList('a', 'r', 'o', 't', 'h', 'e', 'y')) 129  * .build(); 130  * } 131  * }</pre> 132  * 133  * @since 21.0 134  */ 135  public static <T, K, V> 136  Collector<T, ?, ImmutableSetMultimap<K, V>> flatteningToImmutableSetMultimap( 137  Function<? super T, ? extends K> keyFunction, 138  Function<? super T, ? extends Stream<? extends V>> valuesFunction) { 139  return CollectCollectors.flatteningToImmutableSetMultimap(keyFunction, valuesFunction); 140  } 141  142  /** 143  * Returns the empty multimap. 144  * 145  * <p><b>Performance note:</b> the instance returned is a singleton. 146  */ 147  // Casting is safe because the multimap will never hold any elements. 148  @SuppressWarnings("unchecked") 149  public static <K, V> ImmutableSetMultimap<K, V> of() { 150  return (ImmutableSetMultimap<K, V>) EmptyImmutableSetMultimap.INSTANCE; 151  } 152  153  /** Returns an immutable multimap containing a single entry. */ 154  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1) { 155  ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 156  builder.put(k1, v1); 157  return builder.build(); 158  } 159  160  /** 161  * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of 162  * an entry (according to {@link Object#equals}) after the first are ignored. 163  */ 164  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2) { 165  ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 166  builder.put(k1, v1); 167  builder.put(k2, v2); 168  return builder.build(); 169  } 170  171  /** 172  * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of 173  * an entry (according to {@link Object#equals}) after the first are ignored. 174  */ 175  public static <K, V> ImmutableSetMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) { 176  ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 177  builder.put(k1, v1); 178  builder.put(k2, v2); 179  builder.put(k3, v3); 180  return builder.build(); 181  } 182  183  /** 184  * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of 185  * an entry (according to {@link Object#equals}) after the first are ignored. 186  */ 187  public static <K, V> ImmutableSetMultimap<K, V> of( 188  K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 189  ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 190  builder.put(k1, v1); 191  builder.put(k2, v2); 192  builder.put(k3, v3); 193  builder.put(k4, v4); 194  return builder.build(); 195  } 196  197  /** 198  * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of 199  * an entry (according to {@link Object#equals}) after the first are ignored. 200  */ 201  public static <K, V> ImmutableSetMultimap<K, V> of( 202  K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { 203  ImmutableSetMultimap.Builder<K, V> builder = ImmutableSetMultimap.builder(); 204  builder.put(k1, v1); 205  builder.put(k2, v2); 206  builder.put(k3, v3); 207  builder.put(k4, v4); 208  builder.put(k5, v5); 209  return builder.build(); 210  } 211  212  // looking for of() with > 5 entries? Use the builder instead. 213  214  /** Returns a new {@link Builder}. */ 215  public static <K, V> Builder<K, V> builder() { 216  return new Builder<>(); 217  } 218  219  /** 220  * A builder for creating immutable {@code SetMultimap} instances, especially {@code public static 221  * final} multimaps ("constant multimaps"). Example: 222  * 223  * <pre>{@code 224  * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = 225  * new ImmutableSetMultimap.Builder<String, Integer>() 226  * .put("one", 1) 227  * .putAll("several", 1, 2, 3) 228  * .putAll("many", 1, 2, 3, 4, 5) 229  * .build(); 230  * }</pre> 231  * 232  * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 233  * multiple multimaps in series. Each multimap contains the key-value mappings in the previously 234  * created multimaps. 235  * 236  * @since 2.0 237  */ 238  public static final class Builder<K, V> extends ImmutableMultimap.Builder<K, V> { 239  /** 240  * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 241  * ImmutableSetMultimap#builder}. 242  */ 243  public Builder() { 244  super(); 245  } 246  247  @Override 248  Collection<V> newMutableValueCollection() { 249  return Platform.preservesInsertionOrderOnAddsSet(); 250  } 251  252  /** Adds a key-value mapping to the built multimap if it is not already present. */ 253  @CanIgnoreReturnValue 254  @Override 255  public Builder<K, V> put(K key, V value) { 256  super.put(key, value); 257  return this; 258  } 259  260  /** 261  * Adds an entry to the built multimap if it is not already present. 262  * 263  * @since 11.0 264  */ 265  @CanIgnoreReturnValue 266  @Override 267  public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { 268  super.put(entry); 269  return this; 270  } 271  272  /** 273  * {@inheritDoc} 274  * 275  * @since 19.0 276  */ 277  @CanIgnoreReturnValue 278  @Beta 279  @Override 280  public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) { 281  super.putAll(entries); 282  return this; 283  } 284  285  @CanIgnoreReturnValue 286  @Override 287  public Builder<K, V> putAll(K key, Iterable<? extends V> values) { 288  super.putAll(key, values); 289  return this; 290  } 291  292  @CanIgnoreReturnValue 293  @Override 294  public Builder<K, V> putAll(K key, V... values) { 295  return putAll(key, Arrays.asList(values)); 296  } 297  298  @CanIgnoreReturnValue 299  @Override 300  public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) { 301  for (Entry<? extends K, ? extends Collection<? extends V>> entry : 302  multimap.asMap().entrySet()) { 303  putAll(entry.getKey(), entry.getValue()); 304  } 305  return this; 306  } 307  308  @CanIgnoreReturnValue 309  @Override 310  Builder<K, V> combine(ImmutableMultimap.Builder<K, V> other) { 311  super.combine(other); 312  return this; 313  } 314  315  /** 316  * {@inheritDoc} 317  * 318  * @since 8.0 319  */ 320  @CanIgnoreReturnValue 321  @Override 322  public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { 323  super.orderKeysBy(keyComparator); 324  return this; 325  } 326  327  /** 328  * Specifies the ordering of the generated multimap's values for each key. 329  * 330  * <p>If this method is called, the sets returned by the {@code get()} method of the generated 331  * multimap and its {@link Multimap#asMap()} view are {@link ImmutableSortedSet} instances. 332  * However, serialization does not preserve that property, though it does maintain the key and 333  * value ordering. 334  * 335  * @since 8.0 336  */ 337  // TODO: Make serialization behavior consistent. 338  @CanIgnoreReturnValue 339  @Override 340  public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { 341  super.orderValuesBy(valueComparator); 342  return this; 343  } 344  345  /** Returns a newly-created immutable set multimap. */ 346  @Override 347  public ImmutableSetMultimap<K, V> build() { 348  Collection<Map.Entry<K, Collection<V>>> mapEntries = builderMap.entrySet(); 349  if (keyComparator != null) { 350  mapEntries = Ordering.from(keyComparator).<K>onKeys().immutableSortedCopy(mapEntries); 351  } 352  return fromMapEntries(mapEntries, valueComparator); 353  } 354  } 355  356  /** 357  * Returns an immutable set multimap containing the same mappings as {@code multimap}. The 358  * generated multimap's key and value orderings correspond to the iteration ordering of the {@code 359  * multimap.asMap()} view. Repeated occurrences of an entry in the multimap after the first are 360  * ignored. 361  * 362  * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 363  * safe to do so. The exact circumstances under which a copy will or will not be performed are 364  * undocumented and subject to change. 365  * 366  * @throws NullPointerException if any key or value in {@code multimap} is null 367  */ 368  public static <K, V> ImmutableSetMultimap<K, V> copyOf( 369  Multimap<? extends K, ? extends V> multimap) { 370  return copyOf(multimap, null); 371  } 372  373  private static <K, V> ImmutableSetMultimap<K, V> copyOf( 374  Multimap<? extends K, ? extends V> multimap, Comparator<? super V> valueComparator) { 375  checkNotNull(multimap); // eager for GWT 376  if (multimap.isEmpty() && valueComparator == null) { 377  return of(); 378  } 379  380  if (multimap instanceof ImmutableSetMultimap) { 381  @SuppressWarnings("unchecked") // safe since multimap is not writable 382  ImmutableSetMultimap<K, V> kvMultimap = (ImmutableSetMultimap<K, V>) multimap; 383  if (!kvMultimap.isPartialView()) { 384  return kvMultimap; 385  } 386  } 387  388  return fromMapEntries(multimap.asMap().entrySet(), valueComparator); 389  } 390  391  /** 392  * Returns an immutable multimap containing the specified entries. The returned multimap iterates 393  * over keys in the order they were first encountered in the input, and the values for each key 394  * are iterated in the order they were encountered. If two values for the same key are {@linkplain 395  * Object#equals equal}, the first value encountered is used. 396  * 397  * @throws NullPointerException if any key, value, or entry is null 398  * @since 19.0 399  */ 400  @Beta 401  public static <K, V> ImmutableSetMultimap<K, V> copyOf( 402  Iterable<? extends Entry<? extends K, ? extends V>> entries) { 403  return new Builder<K, V>().putAll(entries).build(); 404  } 405  406  /** Creates an ImmutableSetMultimap from an asMap.entrySet. */ 407  static <K, V> ImmutableSetMultimap<K, V> fromMapEntries( 408  Collection<? extends Map.Entry<? extends K, ? extends Collection<? extends V>>> mapEntries, 409  @Nullable Comparator<? super V> valueComparator) { 410  if (mapEntries.isEmpty()) { 411  return of(); 412  } 413  ImmutableMap.Builder<K, ImmutableSet<V>> builder = 414  new ImmutableMap.Builder<>(mapEntries.size()); 415  int size = 0; 416  417  for (Entry<? extends K, ? extends Collection<? extends V>> entry : mapEntries) { 418  K key = entry.getKey(); 419  Collection<? extends V> values = entry.getValue(); 420  ImmutableSet<V> set = valueSet(valueComparator, values); 421  if (!set.isEmpty()) { 422  builder.put(key, set); 423  size += set.size(); 424  } 425  } 426  427  return new ImmutableSetMultimap<>(builder.build(), size, valueComparator); 428  } 429  430  /** 431  * Returned by get() when a missing key is provided. Also holds the comparator, if any, used for 432  * values. 433  */ 434  private final transient ImmutableSet<V> emptySet; 435  436  ImmutableSetMultimap( 437  ImmutableMap<K, ImmutableSet<V>> map, 438  int size, 439  @Nullable Comparator<? super V> valueComparator) { 440  super(map, size); 441  this.emptySet = emptySet(valueComparator); 442  } 443  444  // views 445  446  /** 447  * Returns an immutable set of the values for the given key. If no mappings in the multimap have 448  * the provided key, an empty immutable set is returned. The values are in the same order as the 449  * parameters used to build this multimap. 450  */ 451  @Override 452  public ImmutableSet<V> get(@Nullable K key) { 453  // This cast is safe as its type is known in constructor. 454  ImmutableSet<V> set = (ImmutableSet<V>) map.get(key); 455  return MoreObjects.firstNonNull(set, emptySet); 456  } 457  458  @LazyInit @RetainedWith private transient @Nullable ImmutableSetMultimap<V, K> inverse; 459  460  /** 461  * {@inheritDoc} 462  * 463  * <p>Because an inverse of a set multimap cannot contain multiple pairs with the same key and 464  * value, this method returns an {@code ImmutableSetMultimap} rather than the {@code 465  * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. 466  */ 467  @Override 468  public ImmutableSetMultimap<V, K> inverse() { 469  ImmutableSetMultimap<V, K> result = inverse; 470  return (result == null) ? (inverse = invert()) : result; 471  } 472  473  private ImmutableSetMultimap<V, K> invert() { 474  Builder<V, K> builder = builder(); 475  for (Entry<K, V> entry : entries()) { 476  builder.put(entry.getValue(), entry.getKey()); 477  } 478  ImmutableSetMultimap<V, K> invertedMultimap = builder.build(); 479  invertedMultimap.inverse = this; 480  return invertedMultimap; 481  } 482  483  /** 484  * Guaranteed to throw an exception and leave the multimap unmodified. 485  * 486  * @throws UnsupportedOperationException always 487  * @deprecated Unsupported operation. 488  */ 489  @CanIgnoreReturnValue 490  @Deprecated 491  @Override 492  @DoNotCall("Always throws UnsupportedOperationException") 493  public final ImmutableSet<V> removeAll(Object key) { 494  throw new UnsupportedOperationException(); 495  } 496  497  /** 498  * Guaranteed to throw an exception and leave the multimap unmodified. 499  * 500  * @throws UnsupportedOperationException always 501  * @deprecated Unsupported operation. 502  */ 503  @CanIgnoreReturnValue 504  @Deprecated 505  @Override 506  @DoNotCall("Always throws UnsupportedOperationException") 507  public final ImmutableSet<V> replaceValues(K key, Iterable<? extends V> values) { 508  throw new UnsupportedOperationException(); 509  } 510  511  @LazyInit @RetainedWith private transient @Nullable ImmutableSet<Entry<K, V>> entries; 512  513  /** 514  * Returns an immutable collection of all key-value pairs in the multimap. Its iterator traverses 515  * the values for the first key, the values for the second key, and so on. 516  */ 517  @Override 518  public ImmutableSet<Entry<K, V>> entries() { 519  ImmutableSet<Entry<K, V>> result = entries; 520  return result == null ? (entries = new EntrySet<>(this)) : result; 521  } 522  523  private static final class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> { 524  @Weak private final transient ImmutableSetMultimap<K, V> multimap; 525  526  EntrySet(ImmutableSetMultimap<K, V> multimap) { 527  this.multimap = multimap; 528  } 529  530  @Override 531  public boolean contains(@Nullable Object object) { 532  if (object instanceof Entry) { 533  Entry<?, ?> entry = (Entry<?, ?>) object; 534  return multimap.containsEntry(entry.getKey(), entry.getValue()); 535  } 536  return false; 537  } 538  539  @Override 540  public int size() { 541  return multimap.size(); 542  } 543  544  @Override 545  public UnmodifiableIterator<Entry<K, V>> iterator() { 546  return multimap.entryIterator(); 547  } 548  549  @Override 550  boolean isPartialView() { 551  return false; 552  } 553  } 554  555  private static <V> ImmutableSet<V> valueSet( 556  @Nullable Comparator<? super V> valueComparator, Collection<? extends V> values) { 557  return (valueComparator == null) 558  ? ImmutableSet.copyOf(values) 559  : ImmutableSortedSet.copyOf(valueComparator, values); 560  } 561  562  private static <V> ImmutableSet<V> emptySet(@Nullable Comparator<? super V> valueComparator) { 563  return (valueComparator == null) 564  ? ImmutableSet.<V>of() 565  : ImmutableSortedSet.<V>emptySet(valueComparator); 566  } 567  568  private static <V> ImmutableSet.Builder<V> valuesBuilder( 569  @Nullable Comparator<? super V> valueComparator) { 570  return (valueComparator == null) 571  ? new ImmutableSet.Builder<V>() 572  : new ImmutableSortedSet.Builder<V>(valueComparator); 573  } 574  575  /** 576  * @serialData number of distinct keys, and then for each distinct key: the key, the number of 577  * values for that key, and the key's values 578  */ 579  @GwtIncompatible // java.io.ObjectOutputStream 580  private void writeObject(ObjectOutputStream stream) throws IOException { 581  stream.defaultWriteObject(); 582  stream.writeObject(valueComparator()); 583  Serialization.writeMultimap(this, stream); 584  } 585  586  @Nullable 587  Comparator<? super V> valueComparator() { 588  return emptySet instanceof ImmutableSortedSet 589  ? ((ImmutableSortedSet<V>) emptySet).comparator() 590  : null; 591  } 592  593  @GwtIncompatible // java serialization 594  private static final class SetFieldSettersHolder { 595  static final Serialization.FieldSetter<ImmutableSetMultimap> EMPTY_SET_FIELD_SETTER = 596  Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); 597  } 598  599  @GwtIncompatible // java.io.ObjectInputStream 600  // Serialization type safety is at the caller's mercy. 601  @SuppressWarnings("unchecked") 602  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 603  stream.defaultReadObject(); 604  Comparator<Object> valueComparator = (Comparator<Object>) stream.readObject(); 605  int keyCount = stream.readInt(); 606  if (keyCount < 0) { 607  throw new InvalidObjectException("Invalid key count " + keyCount); 608  } 609  ImmutableMap.Builder<Object, ImmutableSet<Object>> builder = ImmutableMap.builder(); 610  int tmpSize = 0; 611  612  for (int i = 0; i < keyCount; i++) { 613  Object key = stream.readObject(); 614  int valueCount = stream.readInt(); 615  if (valueCount <= 0) { 616  throw new InvalidObjectException("Invalid value count " + valueCount); 617  } 618  619  ImmutableSet.Builder<Object> valuesBuilder = valuesBuilder(valueComparator); 620  for (int j = 0; j < valueCount; j++) { 621  valuesBuilder.add(stream.readObject()); 622  } 623  ImmutableSet<Object> valueSet = valuesBuilder.build(); 624  if (valueSet.size() != valueCount) { 625  throw new InvalidObjectException("Duplicate key-value pairs exist for key " + key); 626  } 627  builder.put(key, valueSet); 628  tmpSize += valueCount; 629  } 630  631  ImmutableMap<Object, ImmutableSet<Object>> tmpMap; 632  try { 633  tmpMap = builder.build(); 634  } catch (IllegalArgumentException e) { 635  throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); 636  } 637  638  FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); 639  FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); 640  SetFieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); 641  } 642  643  @GwtIncompatible // not needed in emulated source. 644  private static final long serialVersionUID = 0; 645 }