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