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 java.util.Objects.requireNonNull; 021 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.GwtIncompatible; 024import com.google.common.annotations.J2ktIncompatible; 025import com.google.common.annotations.VisibleForTesting; 026import com.google.errorprone.annotations.CanIgnoreReturnValue; 027import com.google.errorprone.annotations.DoNotCall; 028import com.google.errorprone.annotations.concurrent.LazyInit; 029import com.google.j2objc.annotations.WeakOuter; 030import java.io.InvalidObjectException; 031import java.io.ObjectInputStream; 032import java.io.Serializable; 033import java.util.Arrays; 034import java.util.Collection; 035import java.util.Collections; 036import java.util.Iterator; 037import java.util.List; 038import java.util.function.Function; 039import java.util.function.ToIntFunction; 040import java.util.stream.Collector; 041import org.jspecify.annotations.Nullable; 042 043/** 044 * A {@link Multiset} whose contents will never change, with many other important properties 045 * detailed at {@link ImmutableCollection}. 046 * 047 * <p><b>Grouped iteration.</b> In all current implementations, duplicate elements always appear 048 * consecutively when iterating. Elements iterate in order by the <i>first</i> appearance of that 049 * element when the multiset was created. 050 * 051 * <p>See the Guava User Guide article on <a href= 052 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>. 053 * 054 * @author Jared Levy 055 * @author Louis Wasserman 056 * @since 2.0 057 */ 058@GwtCompatible(serializable = true, emulated = true) 059@SuppressWarnings("serial") // we're overriding default serialization 060public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializationDependencies<E> 061 implements Multiset<E> { 062 063 /** 064 * Returns a {@code Collector} that accumulates the input elements into a new {@code 065 * ImmutableMultiset}. Elements iterate in order by the <i>first</i> appearance of that element in 066 * encounter order. 067 * 068 * @since 21.0 069 */ 070 public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() { 071 return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1); 072 } 073 074 /** 075 * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose 076 * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to 077 * the result of applying {@code countFunction} to the inputs. 078 * 079 * <p>If the mapped elements contain duplicates (according to {@link Object#equals}), the first 080 * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of 081 * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. 082 * 083 * @since 22.0 084 */ 085 public static <T extends @Nullable Object, E> 086 Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset( 087 Function<? super T, ? extends E> elementFunction, 088 ToIntFunction<? super T> countFunction) { 089 return CollectCollectors.toImmutableMultiset(elementFunction, countFunction); 090 } 091 092 /** 093 * Returns the empty immutable multiset. 094 * 095 * <p><b>Performance note:</b> the instance returned is a singleton. 096 */ 097 @SuppressWarnings("unchecked") // all supported methods are covariant 098 public static <E> ImmutableMultiset<E> of() { 099 return (ImmutableMultiset<E>) RegularImmutableMultiset.EMPTY; 100 } 101 102 /** 103 * Returns an immutable multiset containing a single element. 104 * 105 * @throws NullPointerException if the element is null 106 * @since 6.0 (source-compatible since 2.0) 107 */ 108 public static <E> ImmutableMultiset<E> of(E e1) { 109 return copyFromElements(e1); 110 } 111 112 /** 113 * Returns an immutable multiset containing the given elements, in order. 114 * 115 * @throws NullPointerException if any element is null 116 * @since 6.0 (source-compatible since 2.0) 117 */ 118 public static <E> ImmutableMultiset<E> of(E e1, E e2) { 119 return copyFromElements(e1, e2); 120 } 121 122 /** 123 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 124 * described in the class documentation. 125 * 126 * @throws NullPointerException if any element is null 127 * @since 6.0 (source-compatible since 2.0) 128 */ 129 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) { 130 return copyFromElements(e1, e2, e3); 131 } 132 133 /** 134 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 135 * described in the class documentation. 136 * 137 * @throws NullPointerException if any element is null 138 * @since 6.0 (source-compatible since 2.0) 139 */ 140 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) { 141 return copyFromElements(e1, e2, e3, e4); 142 } 143 144 /** 145 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 146 * described in the class documentation. 147 * 148 * @throws NullPointerException if any element is null 149 * @since 6.0 (source-compatible since 2.0) 150 */ 151 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) { 152 return copyFromElements(e1, e2, e3, e4, e5); 153 } 154 155 /** 156 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 157 * described in the class documentation. 158 * 159 * @throws NullPointerException if any element is null 160 * @since 6.0 (source-compatible since 2.0) 161 */ 162 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { 163 return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); 164 } 165 166 /** 167 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 168 * described in the class documentation. 169 * 170 * @throws NullPointerException if any of {@code elements} is null 171 * @since 6.0 172 */ 173 public static <E> ImmutableMultiset<E> copyOf(E[] elements) { 174 return copyFromElements(elements); 175 } 176 177 /** 178 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 179 * described in the class documentation. 180 * 181 * @throws NullPointerException if any of {@code elements} is null 182 */ 183 public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) { 184 if (elements instanceof ImmutableMultiset) { 185 @SuppressWarnings("unchecked") // all supported methods are covariant 186 ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements; 187 if (!result.isPartialView()) { 188 return result; 189 } 190 } 191 192 Multiset<? extends E> multiset = 193 (elements instanceof Multiset) 194 ? (Multiset<? extends E>) elements 195 : LinkedHashMultiset.create(elements); 196 197 return copyFromEntries(multiset.entrySet()); 198 } 199 200 /** 201 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 202 * described in the class documentation. 203 * 204 * @throws NullPointerException if any of {@code elements} is null 205 */ 206 public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) { 207 Multiset<E> multiset = LinkedHashMultiset.create(); 208 Iterators.addAll(multiset, elements); 209 return copyFromEntries(multiset.entrySet()); 210 } 211 212 private static <E> ImmutableMultiset<E> copyFromElements(E... elements) { 213 Multiset<E> multiset = LinkedHashMultiset.create(); 214 Collections.addAll(multiset, elements); 215 return copyFromEntries(multiset.entrySet()); 216 } 217 218 static <E> ImmutableMultiset<E> copyFromEntries( 219 Collection<? extends Entry<? extends E>> entries) { 220 if (entries.isEmpty()) { 221 return of(); 222 } else { 223 return RegularImmutableMultiset.create(entries); 224 } 225 } 226 227 ImmutableMultiset() {} 228 229 @Override 230 public UnmodifiableIterator<E> iterator() { 231 final Iterator<Entry<E>> entryIterator = entrySet().iterator(); 232 return new UnmodifiableIterator<E>() { 233 int remaining; 234 @Nullable E element; 235 236 @Override 237 public boolean hasNext() { 238 return (remaining > 0) || entryIterator.hasNext(); 239 } 240 241 @Override 242 public E next() { 243 if (remaining <= 0) { 244 Entry<E> entry = entryIterator.next(); 245 element = entry.getElement(); 246 remaining = entry.getCount(); 247 } 248 remaining--; 249 /* 250 * requireNonNull is safe because `remaining` starts at 0, forcing us to initialize 251 * `element` above. After that, we never clear it. 252 */ 253 return requireNonNull(element); 254 } 255 }; 256 } 257 258 @LazyInit private transient @Nullable ImmutableList<E> asList; 259 260 @Override 261 public ImmutableList<E> asList() { 262 ImmutableList<E> result = asList; 263 return (result == null) ? asList = super.asList() : result; 264 } 265 266 @Override 267 public boolean contains(@Nullable Object object) { 268 return count(object) > 0; 269 } 270 271 /** 272 * Guaranteed to throw an exception and leave the collection unmodified. 273 * 274 * @throws UnsupportedOperationException always 275 * @deprecated Unsupported operation. 276 */ 277 @CanIgnoreReturnValue 278 @Deprecated 279 @Override 280 @DoNotCall("Always throws UnsupportedOperationException") 281 public final int add(E element, int occurrences) { 282 throw new UnsupportedOperationException(); 283 } 284 285 /** 286 * Guaranteed to throw an exception and leave the collection unmodified. 287 * 288 * @throws UnsupportedOperationException always 289 * @deprecated Unsupported operation. 290 */ 291 @CanIgnoreReturnValue 292 @Deprecated 293 @Override 294 @DoNotCall("Always throws UnsupportedOperationException") 295 public final int remove(@Nullable Object element, int occurrences) { 296 throw new UnsupportedOperationException(); 297 } 298 299 /** 300 * Guaranteed to throw an exception and leave the collection unmodified. 301 * 302 * @throws UnsupportedOperationException always 303 * @deprecated Unsupported operation. 304 */ 305 @CanIgnoreReturnValue 306 @Deprecated 307 @Override 308 @DoNotCall("Always throws UnsupportedOperationException") 309 public final int setCount(E element, int count) { 310 throw new UnsupportedOperationException(); 311 } 312 313 /** 314 * Guaranteed to throw an exception and leave the collection unmodified. 315 * 316 * @throws UnsupportedOperationException always 317 * @deprecated Unsupported operation. 318 */ 319 @CanIgnoreReturnValue 320 @Deprecated 321 @Override 322 @DoNotCall("Always throws UnsupportedOperationException") 323 public final boolean setCount(E element, int oldCount, int newCount) { 324 throw new UnsupportedOperationException(); 325 } 326 327 @GwtIncompatible // not present in emulated superclass 328 @Override 329 int copyIntoArray(@Nullable Object[] dst, int offset) { 330 for (Multiset.Entry<E> entry : entrySet()) { 331 Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); 332 offset += entry.getCount(); 333 } 334 return offset; 335 } 336 337 @Override 338 public boolean equals(@Nullable Object object) { 339 return Multisets.equalsImpl(this, object); 340 } 341 342 @Override 343 public int hashCode() { 344 return Sets.hashCodeImpl(entrySet()); 345 } 346 347 @Override 348 public String toString() { 349 return entrySet().toString(); 350 } 351 352 /** @since 21.0 (present with return type {@code Set} since 2.0) */ 353 @Override 354 public abstract ImmutableSet<E> elementSet(); 355 356 @LazyInit private transient @Nullable ImmutableSet<Entry<E>> entrySet; 357 358 @Override 359 public ImmutableSet<Entry<E>> entrySet() { 360 ImmutableSet<Entry<E>> es = entrySet; 361 return (es == null) ? (entrySet = createEntrySet()) : es; 362 } 363 364 private ImmutableSet<Entry<E>> createEntrySet() { 365 return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet(); 366 } 367 368 abstract Entry<E> getEntry(int index); 369 370 @WeakOuter 371 private final class EntrySet extends IndexedImmutableSet<Entry<E>> { 372 @Override 373 boolean isPartialView() { 374 return ImmutableMultiset.this.isPartialView(); 375 } 376 377 @Override 378 Entry<E> get(int index) { 379 return getEntry(index); 380 } 381 382 @Override 383 public int size() { 384 return elementSet().size(); 385 } 386 387 @Override 388 public boolean contains(@Nullable Object o) { 389 if (o instanceof Entry) { 390 Entry<?> entry = (Entry<?>) o; 391 if (entry.getCount() <= 0) { 392 return false; 393 } 394 int count = count(entry.getElement()); 395 return count == entry.getCount(); 396 } 397 return false; 398 } 399 400 @Override 401 public int hashCode() { 402 return ImmutableMultiset.this.hashCode(); 403 } 404 405 @GwtIncompatible 406 @J2ktIncompatible 407 @Override 408 Object writeReplace() { 409 return new EntrySetSerializedForm<E>(ImmutableMultiset.this); 410 } 411 412 @GwtIncompatible 413 @J2ktIncompatible 414 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 415 throw new InvalidObjectException("Use EntrySetSerializedForm"); 416 } 417 418 @J2ktIncompatible private static final long serialVersionUID = 0; 419 } 420 421 @GwtIncompatible 422 @J2ktIncompatible 423 static class EntrySetSerializedForm<E> implements Serializable { 424 final ImmutableMultiset<E> multiset; 425 426 EntrySetSerializedForm(ImmutableMultiset<E> multiset) { 427 this.multiset = multiset; 428 } 429 430 Object readResolve() { 431 return multiset.entrySet(); 432 } 433 } 434 435 @GwtIncompatible 436 @J2ktIncompatible 437 @Override 438 Object writeReplace() { 439 return new SerializedForm(this); 440 } 441 442 @GwtIncompatible 443 @J2ktIncompatible 444 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 445 throw new InvalidObjectException("Use SerializedForm"); 446 } 447 448 /** 449 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 450 * Builder} constructor. 451 */ 452 public static <E> Builder<E> builder() { 453 return new Builder<>(); 454 } 455 456 /** 457 * A builder for creating immutable multiset instances, especially {@code public static final} 458 * multisets ("constant multisets"). Example: 459 * 460 * <pre>{@code 461 * public static final ImmutableMultiset<Bean> BEANS = 462 * new ImmutableMultiset.Builder<Bean>() 463 * .addCopies(Bean.COCOA, 4) 464 * .addCopies(Bean.GARDEN, 6) 465 * .addCopies(Bean.RED, 8) 466 * .addCopies(Bean.BLACK_EYED, 10) 467 * .build(); 468 * }</pre> 469 * 470 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 471 * multiple multisets in series. 472 * 473 * @since 2.0 474 */ 475 public static class Builder<E> extends ImmutableCollection.Builder<E> { 476 final Multiset<E> contents; 477 478 /** 479 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 480 * ImmutableMultiset#builder}. 481 */ 482 public Builder() { 483 this(LinkedHashMultiset.<E>create()); 484 } 485 486 Builder(Multiset<E> contents) { 487 this.contents = contents; 488 } 489 490 /** 491 * Adds {@code element} to the {@code ImmutableMultiset}. 492 * 493 * @param element the element to add 494 * @return this {@code Builder} object 495 * @throws NullPointerException if {@code element} is null 496 */ 497 @CanIgnoreReturnValue 498 @Override 499 public Builder<E> add(E element) { 500 contents.add(checkNotNull(element)); 501 return this; 502 } 503 504 /** 505 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 506 * 507 * @param elements the elements to add 508 * @return this {@code Builder} object 509 * @throws NullPointerException if {@code elements} is null or contains a null element 510 */ 511 @CanIgnoreReturnValue 512 @Override 513 public Builder<E> add(E... elements) { 514 super.add(elements); 515 return this; 516 } 517 518 /** 519 * Adds a number of occurrences of an element to this {@code ImmutableMultiset}. 520 * 521 * @param element the element to add 522 * @param occurrences the number of occurrences of the element to add. May be zero, in which 523 * case no change will be made. 524 * @return this {@code Builder} object 525 * @throws NullPointerException if {@code element} is null 526 * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation 527 * would result in more than {@link Integer#MAX_VALUE} occurrences of the element 528 */ 529 @CanIgnoreReturnValue 530 public Builder<E> addCopies(E element, int occurrences) { 531 contents.add(checkNotNull(element), occurrences); 532 return this; 533 } 534 535 /** 536 * Adds or removes the necessary occurrences of an element such that the element attains the 537 * desired count. 538 * 539 * @param element the element to add or remove occurrences of 540 * @param count the desired count of the element in this multiset 541 * @return this {@code Builder} object 542 * @throws NullPointerException if {@code element} is null 543 * @throws IllegalArgumentException if {@code count} is negative 544 */ 545 @CanIgnoreReturnValue 546 public Builder<E> setCount(E element, int count) { 547 contents.setCount(checkNotNull(element), count); 548 return this; 549 } 550 551 /** 552 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 553 * 554 * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset} 555 * @return this {@code Builder} object 556 * @throws NullPointerException if {@code elements} is null or contains a null element 557 */ 558 @CanIgnoreReturnValue 559 @Override 560 public Builder<E> addAll(Iterable<? extends E> elements) { 561 if (elements instanceof Multiset) { 562 Multiset<? extends E> multiset = (Multiset<? extends E>) elements; 563 multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n)); 564 } else { 565 super.addAll(elements); 566 } 567 return this; 568 } 569 570 /** 571 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 572 * 573 * @param elements the elements to add to the {@code ImmutableMultiset} 574 * @return this {@code Builder} object 575 * @throws NullPointerException if {@code elements} is null or contains a null element 576 */ 577 @CanIgnoreReturnValue 578 @Override 579 public Builder<E> addAll(Iterator<? extends E> elements) { 580 super.addAll(elements); 581 return this; 582 } 583 584 /** 585 * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code 586 * Builder}. 587 */ 588 @Override 589 public ImmutableMultiset<E> build() { 590 return copyOf(contents); 591 } 592 593 @VisibleForTesting 594 ImmutableMultiset<E> buildJdkBacked() { 595 if (contents.isEmpty()) { 596 return of(); 597 } 598 return JdkBackedImmutableMultiset.create(contents.entrySet()); 599 } 600 } 601 602 static final class ElementSet<E> extends ImmutableSet.Indexed<E> { 603 private final List<Entry<E>> entries; 604 // TODO(cpovirk): @Weak? 605 private final Multiset<E> delegate; 606 607 ElementSet(List<Entry<E>> entries, Multiset<E> delegate) { 608 this.entries = entries; 609 this.delegate = delegate; 610 } 611 612 @Override 613 E get(int index) { 614 return entries.get(index).getElement(); 615 } 616 617 @Override 618 public boolean contains(@Nullable Object object) { 619 return delegate.contains(object); 620 } 621 622 @Override 623 boolean isPartialView() { 624 return true; 625 } 626 627 @Override 628 public int size() { 629 return entries.size(); 630 } 631 632 // redeclare to help optimizers with b/310253115 633 @SuppressWarnings("RedundantOverride") 634 @Override 635 @J2ktIncompatible // serialization 636 @GwtIncompatible // serialization 637 Object writeReplace() { 638 return super.writeReplace(); 639 } 640 } 641 642 @J2ktIncompatible 643 static final class SerializedForm implements Serializable { 644 final Object[] elements; 645 final int[] counts; 646 647 // "extends Object" works around https://github.com/typetools/checker-framework/issues/3013 648 SerializedForm(Multiset<? extends Object> multiset) { 649 int distinct = multiset.entrySet().size(); 650 elements = new Object[distinct]; 651 counts = new int[distinct]; 652 int i = 0; 653 for (Entry<? extends Object> entry : multiset.entrySet()) { 654 elements[i] = entry.getElement(); 655 counts[i] = entry.getCount(); 656 i++; 657 } 658 } 659 660 Object readResolve() { 661 LinkedHashMultiset<Object> multiset = LinkedHashMultiset.create(elements.length); 662 for (int i = 0; i < elements.length; i++) { 663 multiset.add(elements[i], counts[i]); 664 } 665 return ImmutableMultiset.copyOf(multiset); 666 } 667 668 private static final long serialVersionUID = 0; 669 } 670 671 private static final long serialVersionUID = 0xcafebabe; 672}