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