001/* 002 * Copyright (C) 2008 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.collect.CollectPreconditions.checkEntryNotNull; 021 022import com.google.common.annotations.Beta; 023import com.google.common.annotations.GwtCompatible; 024import com.google.common.annotations.GwtIncompatible; 025import com.google.errorprone.annotations.CanIgnoreReturnValue; 026import com.google.errorprone.annotations.DoNotMock; 027import com.google.j2objc.annotations.Weak; 028import com.google.j2objc.annotations.WeakOuter; 029import java.io.Serializable; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.Collection; 033import java.util.Comparator; 034import java.util.Iterator; 035import java.util.Map; 036import java.util.Map.Entry; 037import java.util.Set; 038import java.util.Spliterator; 039import java.util.function.BiConsumer; 040import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 041import org.checkerframework.checker.nullness.qual.Nullable; 042 043/** 044 * A {@link Multimap} whose contents will never change, with many other important properties 045 * detailed at {@link ImmutableCollection}. 046 * 047 * <p><b>Warning:</b> avoid <i>direct</i> usage of {@link ImmutableMultimap} as a type (as with 048 * {@link Multimap} itself). Prefer subtypes such as {@link ImmutableSetMultimap} or {@link 049 * ImmutableListMultimap}, which have well-defined {@link #equals} semantics, thus avoiding a common 050 * source of bugs and confusion. 051 * 052 * <p><b>Note:</b> every {@link ImmutableMultimap} offers an {@link #inverse} view, so there is no 053 * need for a distinct {@code ImmutableBiMultimap} type. 054 * 055 * <p><a id="iteration"></a> 056 * 057 * <p><b>Key-grouped iteration.</b> All view collections follow the same iteration order. In all 058 * current implementations, the iteration order always keeps multiple entries with the same key 059 * together. Any creation method that would customarily respect insertion order (such as {@link 060 * #copyOf(Multimap)}) instead preserves key-grouped order by inserting entries for an existing key 061 * immediately after the last entry having that key. 062 * 063 * <p>See the Guava User Guide article on <a href= 064 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>. 065 * 066 * @author Jared Levy 067 * @since 2.0 068 */ 069@GwtCompatible(emulated = true) 070public abstract class ImmutableMultimap<K, V> extends BaseImmutableMultimap<K, V> 071 implements Serializable { 072 073 /** Returns an empty multimap. */ 074 public static <K, V> ImmutableMultimap<K, V> of() { 075 return ImmutableListMultimap.of(); 076 } 077 078 /** Returns an immutable multimap containing a single entry. */ 079 public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1) { 080 return ImmutableListMultimap.of(k1, v1); 081 } 082 083 /** Returns an immutable multimap containing the given entries, in order. */ 084 public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2) { 085 return ImmutableListMultimap.of(k1, v1, k2, v2); 086 } 087 088 /** 089 * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion 090 * order described in the <a href="#iteration">class documentation</a>. 091 */ 092 public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) { 093 return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); 094 } 095 096 /** 097 * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion 098 * order described in the <a href="#iteration">class documentation</a>. 099 */ 100 public static <K, V> ImmutableMultimap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { 101 return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); 102 } 103 104 /** 105 * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion 106 * order described in the <a href="#iteration">class documentation</a>. 107 */ 108 public static <K, V> ImmutableMultimap<K, V> of( 109 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { 110 return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); 111 } 112 113 // looking for of() with > 5 entries? Use the builder instead. 114 115 /** 116 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 117 * Builder} constructor. 118 */ 119 public static <K, V> Builder<K, V> builder() { 120 return new Builder<>(); 121 } 122 123 /** 124 * A builder for creating immutable multimap instances, especially {@code public static final} 125 * multimaps ("constant multimaps"). Example: 126 * 127 * <pre>{@code 128 * static final Multimap<String, Integer> STRING_TO_INTEGER_MULTIMAP = 129 * new ImmutableMultimap.Builder<String, Integer>() 130 * .put("one", 1) 131 * .putAll("several", 1, 2, 3) 132 * .putAll("many", 1, 2, 3, 4, 5) 133 * .build(); 134 * }</pre> 135 * 136 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 137 * multiple multimaps in series. Each multimap contains the key-value mappings in the previously 138 * created multimaps. 139 * 140 * @since 2.0 141 */ 142 @DoNotMock 143 public static class Builder<K, V> { 144 Map<K, Collection<V>> builderMap; 145 @MonotonicNonNull Comparator<? super K> keyComparator; 146 @MonotonicNonNull Comparator<? super V> valueComparator; 147 148 /** 149 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 150 * ImmutableMultimap#builder}. 151 */ 152 public Builder() { 153 this.builderMap = Platform.preservesInsertionOrderOnPutsMap(); 154 } 155 156 Collection<V> newMutableValueCollection() { 157 return new ArrayList<>(); 158 } 159 160 /** Adds a key-value mapping to the built multimap. */ 161 @CanIgnoreReturnValue 162 public Builder<K, V> put(K key, V value) { 163 checkEntryNotNull(key, value); 164 Collection<V> valueCollection = builderMap.get(key); 165 if (valueCollection == null) { 166 builderMap.put(key, valueCollection = newMutableValueCollection()); 167 } 168 valueCollection.add(value); 169 return this; 170 } 171 172 /** 173 * Adds an entry to the built multimap. 174 * 175 * @since 11.0 176 */ 177 @CanIgnoreReturnValue 178 public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { 179 return put(entry.getKey(), entry.getValue()); 180 } 181 182 /** 183 * Adds entries to the built multimap. 184 * 185 * @since 19.0 186 */ 187 @CanIgnoreReturnValue 188 @Beta 189 public Builder<K, V> putAll(Iterable<? extends Entry<? extends K, ? extends V>> entries) { 190 for (Entry<? extends K, ? extends V> entry : entries) { 191 put(entry); 192 } 193 return this; 194 } 195 196 /** 197 * Stores a collection of values with the same key in the built multimap. 198 * 199 * @throws NullPointerException if {@code key}, {@code values}, or any element in {@code values} 200 * is null. The builder is left in an invalid state. 201 */ 202 @CanIgnoreReturnValue 203 public Builder<K, V> putAll(K key, Iterable<? extends V> values) { 204 if (key == null) { 205 throw new NullPointerException("null key in entry: null=" + Iterables.toString(values)); 206 } 207 Collection<V> valueCollection = builderMap.get(key); 208 if (valueCollection != null) { 209 for (V value : values) { 210 checkEntryNotNull(key, value); 211 valueCollection.add(value); 212 } 213 return this; 214 } 215 Iterator<? extends V> valuesItr = values.iterator(); 216 if (!valuesItr.hasNext()) { 217 return this; 218 } 219 valueCollection = newMutableValueCollection(); 220 while (valuesItr.hasNext()) { 221 V value = valuesItr.next(); 222 checkEntryNotNull(key, value); 223 valueCollection.add(value); 224 } 225 builderMap.put(key, valueCollection); 226 return this; 227 } 228 229 /** 230 * Stores an array of values with the same key in the built multimap. 231 * 232 * @throws NullPointerException if the key or any value is null. The builder is left in an 233 * invalid state. 234 */ 235 @CanIgnoreReturnValue 236 public Builder<K, V> putAll(K key, V... values) { 237 return putAll(key, Arrays.asList(values)); 238 } 239 240 /** 241 * Stores another multimap's entries in the built multimap. The generated multimap's key and 242 * value orderings correspond to the iteration ordering of the {@code multimap.asMap()} view, 243 * with new keys and values following any existing keys and values. 244 * 245 * @throws NullPointerException if any key or value in {@code multimap} is null. The builder is 246 * left in an invalid state. 247 */ 248 @CanIgnoreReturnValue 249 public Builder<K, V> putAll(Multimap<? extends K, ? extends V> multimap) { 250 for (Entry<? extends K, ? extends Collection<? extends V>> entry : 251 multimap.asMap().entrySet()) { 252 putAll(entry.getKey(), entry.getValue()); 253 } 254 return this; 255 } 256 257 /** 258 * Specifies the ordering of the generated multimap's keys. 259 * 260 * @since 8.0 261 */ 262 @CanIgnoreReturnValue 263 public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { 264 this.keyComparator = checkNotNull(keyComparator); 265 return this; 266 } 267 268 /** 269 * Specifies the ordering of the generated multimap's values for each key. 270 * 271 * @since 8.0 272 */ 273 @CanIgnoreReturnValue 274 public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { 275 this.valueComparator = checkNotNull(valueComparator); 276 return this; 277 } 278 279 @CanIgnoreReturnValue 280 Builder<K, V> combine(Builder<K, V> other) { 281 for (Map.Entry<K, Collection<V>> entry : other.builderMap.entrySet()) { 282 putAll(entry.getKey(), entry.getValue()); 283 } 284 return this; 285 } 286 287 /** Returns a newly-created immutable multimap. */ 288 public ImmutableMultimap<K, V> build() { 289 Collection<Map.Entry<K, Collection<V>>> mapEntries = builderMap.entrySet(); 290 if (keyComparator != null) { 291 mapEntries = Ordering.from(keyComparator).<K>onKeys().immutableSortedCopy(mapEntries); 292 } 293 return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator); 294 } 295 } 296 297 /** 298 * Returns an immutable multimap containing the same mappings as {@code multimap}, in the 299 * "key-grouped" iteration order described in the class documentation. 300 * 301 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 302 * safe to do so. The exact circumstances under which a copy will or will not be performed are 303 * undocumented and subject to change. 304 * 305 * @throws NullPointerException if any key or value in {@code multimap} is null 306 */ 307 public static <K, V> ImmutableMultimap<K, V> copyOf(Multimap<? extends K, ? extends V> multimap) { 308 if (multimap instanceof ImmutableMultimap) { 309 @SuppressWarnings("unchecked") // safe since multimap is not writable 310 ImmutableMultimap<K, V> kvMultimap = (ImmutableMultimap<K, V>) multimap; 311 if (!kvMultimap.isPartialView()) { 312 return kvMultimap; 313 } 314 } 315 return ImmutableListMultimap.copyOf(multimap); 316 } 317 318 /** 319 * Returns an immutable multimap containing the specified entries. The returned multimap iterates 320 * over keys in the order they were first encountered in the input, and the values for each key 321 * are iterated in the order they were encountered. 322 * 323 * @throws NullPointerException if any key, value, or entry is null 324 * @since 19.0 325 */ 326 @Beta 327 public static <K, V> ImmutableMultimap<K, V> copyOf( 328 Iterable<? extends Entry<? extends K, ? extends V>> entries) { 329 return ImmutableListMultimap.copyOf(entries); 330 } 331 332 final transient ImmutableMap<K, ? extends ImmutableCollection<V>> map; 333 final transient int size; 334 335 // These constants allow the deserialization code to set final fields. This 336 // holder class makes sure they are not initialized unless an instance is 337 // deserialized. 338 @GwtIncompatible // java serialization is not supported 339 static class FieldSettersHolder { 340 static final Serialization.FieldSetter<ImmutableMultimap> MAP_FIELD_SETTER = 341 Serialization.getFieldSetter(ImmutableMultimap.class, "map"); 342 static final Serialization.FieldSetter<ImmutableMultimap> SIZE_FIELD_SETTER = 343 Serialization.getFieldSetter(ImmutableMultimap.class, "size"); 344 } 345 346 ImmutableMultimap(ImmutableMap<K, ? extends ImmutableCollection<V>> map, int size) { 347 this.map = map; 348 this.size = size; 349 } 350 351 // mutators (not supported) 352 353 /** 354 * Guaranteed to throw an exception and leave the multimap unmodified. 355 * 356 * @throws UnsupportedOperationException always 357 * @deprecated Unsupported operation. 358 */ 359 @CanIgnoreReturnValue 360 @Deprecated 361 @Override 362 public ImmutableCollection<V> removeAll(Object key) { 363 throw new UnsupportedOperationException(); 364 } 365 366 /** 367 * Guaranteed to throw an exception and leave the multimap unmodified. 368 * 369 * @throws UnsupportedOperationException always 370 * @deprecated Unsupported operation. 371 */ 372 @CanIgnoreReturnValue 373 @Deprecated 374 @Override 375 public ImmutableCollection<V> replaceValues(K key, Iterable<? extends V> values) { 376 throw new UnsupportedOperationException(); 377 } 378 379 /** 380 * Guaranteed to throw an exception and leave the multimap unmodified. 381 * 382 * @throws UnsupportedOperationException always 383 * @deprecated Unsupported operation. 384 */ 385 @Deprecated 386 @Override 387 public void clear() { 388 throw new UnsupportedOperationException(); 389 } 390 391 /** 392 * Returns an immutable collection of the values for the given key. If no mappings in the multimap 393 * have the provided key, an empty immutable collection is returned. The values are in the same 394 * order as the parameters used to build this multimap. 395 */ 396 @Override 397 public abstract ImmutableCollection<V> get(K key); 398 399 /** 400 * Returns an immutable multimap which is the inverse of this one. For every key-value mapping in 401 * the original, the result will have a mapping with key and value reversed. 402 * 403 * @since 11.0 404 */ 405 public abstract ImmutableMultimap<V, K> inverse(); 406 407 /** 408 * Guaranteed to throw an exception and leave the multimap unmodified. 409 * 410 * @throws UnsupportedOperationException always 411 * @deprecated Unsupported operation. 412 */ 413 @CanIgnoreReturnValue 414 @Deprecated 415 @Override 416 public boolean put(K key, V value) { 417 throw new UnsupportedOperationException(); 418 } 419 420 /** 421 * Guaranteed to throw an exception and leave the multimap unmodified. 422 * 423 * @throws UnsupportedOperationException always 424 * @deprecated Unsupported operation. 425 */ 426 @CanIgnoreReturnValue 427 @Deprecated 428 @Override 429 public boolean putAll(K key, Iterable<? extends V> values) { 430 throw new UnsupportedOperationException(); 431 } 432 433 /** 434 * Guaranteed to throw an exception and leave the multimap unmodified. 435 * 436 * @throws UnsupportedOperationException always 437 * @deprecated Unsupported operation. 438 */ 439 @CanIgnoreReturnValue 440 @Deprecated 441 @Override 442 public boolean putAll(Multimap<? extends K, ? extends V> multimap) { 443 throw new UnsupportedOperationException(); 444 } 445 446 /** 447 * Guaranteed to throw an exception and leave the multimap unmodified. 448 * 449 * @throws UnsupportedOperationException always 450 * @deprecated Unsupported operation. 451 */ 452 @CanIgnoreReturnValue 453 @Deprecated 454 @Override 455 public boolean remove(Object key, Object value) { 456 throw new UnsupportedOperationException(); 457 } 458 459 /** 460 * Returns {@code true} if this immutable multimap's implementation contains references to 461 * user-created objects that aren't accessible via this multimap's methods. This is generally used 462 * to determine whether {@code copyOf} implementations should make an explicit copy to avoid 463 * memory leaks. 464 */ 465 boolean isPartialView() { 466 return map.isPartialView(); 467 } 468 469 // accessors 470 471 @Override 472 public boolean containsKey(@Nullable Object key) { 473 return map.containsKey(key); 474 } 475 476 @Override 477 public boolean containsValue(@Nullable Object value) { 478 return value != null && super.containsValue(value); 479 } 480 481 @Override 482 public int size() { 483 return size; 484 } 485 486 // views 487 488 /** 489 * Returns an immutable set of the distinct keys in this multimap, in the same order as they 490 * appear in this multimap. 491 */ 492 @Override 493 public ImmutableSet<K> keySet() { 494 return map.keySet(); 495 } 496 497 @Override 498 Set<K> createKeySet() { 499 throw new AssertionError("unreachable"); 500 } 501 502 /** 503 * Returns an immutable map that associates each key with its corresponding values in the 504 * multimap. Keys and values appear in the same order as in this multimap. 505 */ 506 @Override 507 @SuppressWarnings("unchecked") // a widening cast 508 public ImmutableMap<K, Collection<V>> asMap() { 509 return (ImmutableMap) map; 510 } 511 512 @Override 513 Map<K, Collection<V>> createAsMap() { 514 throw new AssertionError("should never be called"); 515 } 516 517 /** Returns an immutable collection of all key-value pairs in the multimap. */ 518 @Override 519 public ImmutableCollection<Entry<K, V>> entries() { 520 return (ImmutableCollection<Entry<K, V>>) super.entries(); 521 } 522 523 @Override 524 ImmutableCollection<Entry<K, V>> createEntries() { 525 return new EntryCollection<>(this); 526 } 527 528 private static class EntryCollection<K, V> extends ImmutableCollection<Entry<K, V>> { 529 @Weak final ImmutableMultimap<K, V> multimap; 530 531 EntryCollection(ImmutableMultimap<K, V> multimap) { 532 this.multimap = multimap; 533 } 534 535 @Override 536 public UnmodifiableIterator<Entry<K, V>> iterator() { 537 return multimap.entryIterator(); 538 } 539 540 @Override 541 boolean isPartialView() { 542 return multimap.isPartialView(); 543 } 544 545 @Override 546 public int size() { 547 return multimap.size(); 548 } 549 550 @Override 551 public boolean contains(Object object) { 552 if (object instanceof Entry) { 553 Entry<?, ?> entry = (Entry<?, ?>) object; 554 return multimap.containsEntry(entry.getKey(), entry.getValue()); 555 } 556 return false; 557 } 558 559 private static final long serialVersionUID = 0; 560 } 561 562 @Override 563 UnmodifiableIterator<Entry<K, V>> entryIterator() { 564 return new UnmodifiableIterator<Entry<K, V>>() { 565 final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>> asMapItr = 566 map.entrySet().iterator(); 567 K currentKey = null; 568 Iterator<V> valueItr = Iterators.emptyIterator(); 569 570 @Override 571 public boolean hasNext() { 572 return valueItr.hasNext() || asMapItr.hasNext(); 573 } 574 575 @Override 576 public Entry<K, V> next() { 577 if (!valueItr.hasNext()) { 578 Entry<K, ? extends ImmutableCollection<V>> entry = asMapItr.next(); 579 currentKey = entry.getKey(); 580 valueItr = entry.getValue().iterator(); 581 } 582 return Maps.immutableEntry(currentKey, valueItr.next()); 583 } 584 }; 585 } 586 587 @Override 588 Spliterator<Entry<K, V>> entrySpliterator() { 589 return CollectSpliterators.flatMap( 590 asMap().entrySet().spliterator(), 591 keyToValueCollectionEntry -> { 592 K key = keyToValueCollectionEntry.getKey(); 593 Collection<V> valueCollection = keyToValueCollectionEntry.getValue(); 594 return CollectSpliterators.map( 595 valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); 596 }, 597 Spliterator.SIZED | (this instanceof SetMultimap ? Spliterator.DISTINCT : 0), 598 size()); 599 } 600 601 @Override 602 public void forEach(BiConsumer<? super K, ? super V> action) { 603 checkNotNull(action); 604 asMap() 605 .forEach( 606 (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); 607 } 608 609 /** 610 * Returns an immutable multiset containing all the keys in this multimap, in the same order and 611 * with the same frequencies as they appear in this multimap; to get only a single occurrence of 612 * each key, use {@link #keySet}. 613 */ 614 @Override 615 public ImmutableMultiset<K> keys() { 616 return (ImmutableMultiset<K>) super.keys(); 617 } 618 619 @Override 620 ImmutableMultiset<K> createKeys() { 621 return new Keys(); 622 } 623 624 @SuppressWarnings("serial") // Uses writeReplace, not default serialization 625 @WeakOuter 626 class Keys extends ImmutableMultiset<K> { 627 @Override 628 public boolean contains(@Nullable Object object) { 629 return containsKey(object); 630 } 631 632 @Override 633 public int count(@Nullable Object element) { 634 Collection<V> values = map.get(element); 635 return (values == null) ? 0 : values.size(); 636 } 637 638 @Override 639 public ImmutableSet<K> elementSet() { 640 return keySet(); 641 } 642 643 @Override 644 public int size() { 645 return ImmutableMultimap.this.size(); 646 } 647 648 @Override 649 Multiset.Entry<K> getEntry(int index) { 650 Map.Entry<K, ? extends Collection<V>> entry = map.entrySet().asList().get(index); 651 return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); 652 } 653 654 @Override 655 boolean isPartialView() { 656 return true; 657 } 658 659 @GwtIncompatible 660 @Override 661 Object writeReplace() { 662 return new KeysSerializedForm(ImmutableMultimap.this); 663 } 664 } 665 666 @GwtIncompatible 667 private static final class KeysSerializedForm implements Serializable { 668 final ImmutableMultimap<?, ?> multimap; 669 670 KeysSerializedForm(ImmutableMultimap<?, ?> multimap) { 671 this.multimap = multimap; 672 } 673 674 Object readResolve() { 675 return multimap.keys(); 676 } 677 } 678 679 /** 680 * Returns an immutable collection of the values in this multimap. Its iterator traverses the 681 * values for the first key, the values for the second key, and so on. 682 */ 683 @Override 684 public ImmutableCollection<V> values() { 685 return (ImmutableCollection<V>) super.values(); 686 } 687 688 @Override 689 ImmutableCollection<V> createValues() { 690 return new Values<>(this); 691 } 692 693 @Override 694 UnmodifiableIterator<V> valueIterator() { 695 return new UnmodifiableIterator<V>() { 696 Iterator<? extends ImmutableCollection<V>> valueCollectionItr = map.values().iterator(); 697 Iterator<V> valueItr = Iterators.emptyIterator(); 698 699 @Override 700 public boolean hasNext() { 701 return valueItr.hasNext() || valueCollectionItr.hasNext(); 702 } 703 704 @Override 705 public V next() { 706 if (!valueItr.hasNext()) { 707 valueItr = valueCollectionItr.next().iterator(); 708 } 709 return valueItr.next(); 710 } 711 }; 712 } 713 714 private static final class Values<K, V> extends ImmutableCollection<V> { 715 @Weak private final transient ImmutableMultimap<K, V> multimap; 716 717 Values(ImmutableMultimap<K, V> multimap) { 718 this.multimap = multimap; 719 } 720 721 @Override 722 public boolean contains(@Nullable Object object) { 723 return multimap.containsValue(object); 724 } 725 726 @Override 727 public UnmodifiableIterator<V> iterator() { 728 return multimap.valueIterator(); 729 } 730 731 @GwtIncompatible // not present in emulated superclass 732 @Override 733 int copyIntoArray(Object[] dst, int offset) { 734 for (ImmutableCollection<V> valueCollection : multimap.map.values()) { 735 offset = valueCollection.copyIntoArray(dst, offset); 736 } 737 return offset; 738 } 739 740 @Override 741 public int size() { 742 return multimap.size(); 743 } 744 745 @Override 746 boolean isPartialView() { 747 return true; 748 } 749 750 private static final long serialVersionUID = 0; 751 } 752 753 private static final long serialVersionUID = 0; 754}