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.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.GwtIncompatible; 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.function.Function; 033import java.util.function.ToIntFunction; 034import java.util.stream.Collector; 035import org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl; 036import org.checkerframework.checker.nullness.compatqual.NullableDecl; 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 * @since 21.0 064 */ 065 @Beta 066 public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() { 067 return 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 checkNotNull(elementFunction); 084 checkNotNull(countFunction); 085 return Collector.of( 086 LinkedHashMultiset::create, 087 (multiset, t) -> 088 multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)), 089 (multiset1, multiset2) -> { 090 multiset1.addAll(multiset2); 091 return multiset1; 092 }, 093 (Multiset<E> multiset) -> copyFromEntries(multiset.entrySet())); 094 } 095 096 /** Returns the empty immutable multiset. */ 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 {@code element} is null 106 * @since 6.0 (source-compatible since 2.0) 107 */ 108 @SuppressWarnings("unchecked") // generic array created but never written 109 public static <E> ImmutableMultiset<E> of(E element) { 110 return copyFromElements(element); 111 } 112 113 /** 114 * Returns an immutable multiset containing the given elements, in order. 115 * 116 * @throws NullPointerException if any element is null 117 * @since 6.0 (source-compatible since 2.0) 118 */ 119 @SuppressWarnings("unchecked") // 120 public static <E> ImmutableMultiset<E> of(E e1, E e2) { 121 return copyFromElements(e1, e2); 122 } 123 124 /** 125 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 126 * described in the class documentation. 127 * 128 * @throws NullPointerException if any element is null 129 * @since 6.0 (source-compatible since 2.0) 130 */ 131 @SuppressWarnings("unchecked") // 132 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) { 133 return copyFromElements(e1, e2, e3); 134 } 135 136 /** 137 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 138 * described in the class documentation. 139 * 140 * @throws NullPointerException if any element is null 141 * @since 6.0 (source-compatible since 2.0) 142 */ 143 @SuppressWarnings("unchecked") // 144 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) { 145 return copyFromElements(e1, e2, e3, e4); 146 } 147 148 /** 149 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 150 * described in the class documentation. 151 * 152 * @throws NullPointerException if any element is null 153 * @since 6.0 (source-compatible since 2.0) 154 */ 155 @SuppressWarnings("unchecked") // 156 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) { 157 return copyFromElements(e1, e2, e3, e4, e5); 158 } 159 160 /** 161 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 162 * described in the class documentation. 163 * 164 * @throws NullPointerException if any element is null 165 * @since 6.0 (source-compatible since 2.0) 166 */ 167 @SuppressWarnings("unchecked") // 168 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { 169 return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); 170 } 171 172 /** 173 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 174 * described in the class documentation. 175 * 176 * @throws NullPointerException if any of {@code elements} is null 177 * @since 6.0 178 */ 179 public static <E> ImmutableMultiset<E> copyOf(E[] elements) { 180 return copyFromElements(elements); 181 } 182 183 /** 184 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 185 * described in the class documentation. 186 * 187 * @throws NullPointerException if any of {@code elements} is null 188 */ 189 public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) { 190 if (elements instanceof ImmutableMultiset) { 191 @SuppressWarnings("unchecked") // all supported methods are covariant 192 ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements; 193 if (!result.isPartialView()) { 194 return result; 195 } 196 } 197 198 Multiset<? extends E> multiset = 199 (elements instanceof Multiset) 200 ? Multisets.cast(elements) 201 : LinkedHashMultiset.create(elements); 202 203 return copyFromEntries(multiset.entrySet()); 204 } 205 206 private static <E> ImmutableMultiset<E> copyFromElements(E... elements) { 207 Multiset<E> multiset = LinkedHashMultiset.create(); 208 Collections.addAll(multiset, elements); 209 return copyFromEntries(multiset.entrySet()); 210 } 211 212 static <E> ImmutableMultiset<E> copyFromEntries( 213 Collection<? extends Entry<? extends E>> entries) { 214 if (entries.isEmpty()) { 215 return of(); 216 } else { 217 return new RegularImmutableMultiset<E>(entries); 218 } 219 } 220 221 /** 222 * Returns an immutable multiset containing the given elements, in the "grouped iteration order" 223 * described in the class documentation. 224 * 225 * @throws NullPointerException if any of {@code elements} is null 226 */ 227 public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) { 228 Multiset<E> multiset = LinkedHashMultiset.create(); 229 Iterators.addAll(multiset, elements); 230 return copyFromEntries(multiset.entrySet()); 231 } 232 233 ImmutableMultiset() {} 234 235 @Override 236 public UnmodifiableIterator<E> iterator() { 237 final Iterator<Entry<E>> entryIterator = entrySet().iterator(); 238 return new UnmodifiableIterator<E>() { 239 int remaining; 240 @MonotonicNonNullDecl E element; 241 242 @Override 243 public boolean hasNext() { 244 return (remaining > 0) || entryIterator.hasNext(); 245 } 246 247 @Override 248 public E next() { 249 if (remaining <= 0) { 250 Entry<E> entry = entryIterator.next(); 251 element = entry.getElement(); 252 remaining = entry.getCount(); 253 } 254 remaining--; 255 return element; 256 } 257 }; 258 } 259 260 @LazyInit private transient ImmutableList<E> asList; 261 262 @Override 263 public ImmutableList<E> asList() { 264 ImmutableList<E> result = asList; 265 return (result == null) ? asList = super.asList() : result; 266 } 267 268 @Override 269 public boolean contains(@NullableDecl Object object) { 270 return count(object) > 0; 271 } 272 273 /** 274 * Guaranteed to throw an exception and leave the collection unmodified. 275 * 276 * @throws UnsupportedOperationException always 277 * @deprecated Unsupported operation. 278 */ 279 @CanIgnoreReturnValue 280 @Deprecated 281 @Override 282 public final int add(E element, int occurrences) { 283 throw new UnsupportedOperationException(); 284 } 285 286 /** 287 * Guaranteed to throw an exception and leave the collection unmodified. 288 * 289 * @throws UnsupportedOperationException always 290 * @deprecated Unsupported operation. 291 */ 292 @CanIgnoreReturnValue 293 @Deprecated 294 @Override 295 public final int remove(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 public final int setCount(E element, int count) { 309 throw new UnsupportedOperationException(); 310 } 311 312 /** 313 * Guaranteed to throw an exception and leave the collection unmodified. 314 * 315 * @throws UnsupportedOperationException always 316 * @deprecated Unsupported operation. 317 */ 318 @CanIgnoreReturnValue 319 @Deprecated 320 @Override 321 public final boolean setCount(E element, int oldCount, int newCount) { 322 throw new UnsupportedOperationException(); 323 } 324 325 @GwtIncompatible // not present in emulated superclass 326 @Override 327 int copyIntoArray(Object[] dst, int offset) { 328 for (Multiset.Entry<E> entry : entrySet()) { 329 Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); 330 offset += entry.getCount(); 331 } 332 return offset; 333 } 334 335 @Override 336 public boolean equals(@NullableDecl Object object) { 337 return Multisets.equalsImpl(this, object); 338 } 339 340 @Override 341 public int hashCode() { 342 return Sets.hashCodeImpl(entrySet()); 343 } 344 345 @Override 346 public String toString() { 347 return entrySet().toString(); 348 } 349 350 /** @since 21.0 (present with return type {@code Set} since 2.0) */ 351 @Override 352 public abstract ImmutableSet<E> elementSet(); 353 354 @LazyInit private transient ImmutableSet<Entry<E>> entrySet; 355 356 @Override 357 public ImmutableSet<Entry<E>> entrySet() { 358 ImmutableSet<Entry<E>> es = entrySet; 359 return (es == null) ? (entrySet = createEntrySet()) : es; 360 } 361 362 private ImmutableSet<Entry<E>> createEntrySet() { 363 return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet(); 364 } 365 366 abstract Entry<E> getEntry(int index); 367 368 @WeakOuter 369 private final class EntrySet extends ImmutableSet.Indexed<Entry<E>> { 370 @Override 371 boolean isPartialView() { 372 return ImmutableMultiset.this.isPartialView(); 373 } 374 375 @Override 376 Entry<E> get(int index) { 377 return getEntry(index); 378 } 379 380 @Override 381 public int size() { 382 return elementSet().size(); 383 } 384 385 @Override 386 public boolean contains(Object o) { 387 if (o instanceof Entry) { 388 Entry<?> entry = (Entry<?>) o; 389 if (entry.getCount() <= 0) { 390 return false; 391 } 392 int count = count(entry.getElement()); 393 return count == entry.getCount(); 394 } 395 return false; 396 } 397 398 @Override 399 public int hashCode() { 400 return ImmutableMultiset.this.hashCode(); 401 } 402 403 // We can't label this with @Override, because it doesn't override anything 404 // in the GWT emulated version. 405 // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead 406 Object writeReplace() { 407 return new EntrySetSerializedForm<E>(ImmutableMultiset.this); 408 } 409 410 private static final long serialVersionUID = 0; 411 } 412 413 static class EntrySetSerializedForm<E> implements Serializable { 414 final ImmutableMultiset<E> multiset; 415 416 EntrySetSerializedForm(ImmutableMultiset<E> multiset) { 417 this.multiset = multiset; 418 } 419 420 Object readResolve() { 421 return multiset.entrySet(); 422 } 423 } 424 425 // We can't label this with @Override, because it doesn't override anything 426 // in the GWT emulated version. 427 abstract Object writeReplace(); 428 429 /** 430 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 431 * Builder} constructor. 432 */ 433 public static <E> Builder<E> builder() { 434 return new Builder<E>(); 435 } 436 437 /** 438 * A builder for creating immutable multiset instances, especially {@code public static final} 439 * multisets ("constant multisets"). Example: 440 * 441 * <pre>{@code 442 * public static final ImmutableMultiset<Bean> BEANS = 443 * new ImmutableMultiset.Builder<Bean>() 444 * .addCopies(Bean.COCOA, 4) 445 * .addCopies(Bean.GARDEN, 6) 446 * .addCopies(Bean.RED, 8) 447 * .addCopies(Bean.BLACK_EYED, 10) 448 * .build(); 449 * }</pre> 450 * 451 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 452 * multiple multisets in series. 453 * 454 * @since 2.0 455 */ 456 public static class Builder<E> extends ImmutableCollection.Builder<E> { 457 final Multiset<E> contents; 458 459 /** 460 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 461 * ImmutableMultiset#builder}. 462 */ 463 public Builder() { 464 this(LinkedHashMultiset.<E>create()); 465 } 466 467 Builder(Multiset<E> contents) { 468 this.contents = contents; 469 } 470 471 /** 472 * Adds {@code element} to the {@code ImmutableMultiset}. 473 * 474 * @param element the element to add 475 * @return this {@code Builder} object 476 * @throws NullPointerException if {@code element} is null 477 */ 478 @CanIgnoreReturnValue 479 @Override 480 public Builder<E> add(E element) { 481 contents.add(checkNotNull(element)); 482 return this; 483 } 484 485 /** 486 * Adds a number of occurrences of an element to this {@code ImmutableMultiset}. 487 * 488 * @param element the element to add 489 * @param occurrences the number of occurrences of the element to add. May be zero, in which 490 * case no change will be made. 491 * @return this {@code Builder} object 492 * @throws NullPointerException if {@code element} is null 493 * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation 494 * would result in more than {@link Integer#MAX_VALUE} occurrences of the element 495 */ 496 @CanIgnoreReturnValue 497 public Builder<E> addCopies(E element, int occurrences) { 498 contents.add(checkNotNull(element), occurrences); 499 return this; 500 } 501 502 /** 503 * Adds or removes the necessary occurrences of an element such that the element attains the 504 * desired count. 505 * 506 * @param element the element to add or remove occurrences of 507 * @param count the desired count of the element in this multiset 508 * @return this {@code Builder} object 509 * @throws NullPointerException if {@code element} is null 510 * @throws IllegalArgumentException if {@code count} is negative 511 */ 512 @CanIgnoreReturnValue 513 public Builder<E> setCount(E element, int count) { 514 contents.setCount(checkNotNull(element), count); 515 return this; 516 } 517 518 /** 519 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 520 * 521 * @param elements the elements to add 522 * @return this {@code Builder} object 523 * @throws NullPointerException if {@code elements} is null or contains a null element 524 */ 525 @CanIgnoreReturnValue 526 @Override 527 public Builder<E> add(E... elements) { 528 super.add(elements); 529 return this; 530 } 531 532 /** 533 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 534 * 535 * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset} 536 * @return this {@code Builder} object 537 * @throws NullPointerException if {@code elements} is null or contains a null element 538 */ 539 @CanIgnoreReturnValue 540 @Override 541 public Builder<E> addAll(Iterable<? extends E> elements) { 542 if (elements instanceof Multiset) { 543 Multiset<? extends E> multiset = Multisets.cast(elements); 544 multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n)); 545 } else { 546 super.addAll(elements); 547 } 548 return this; 549 } 550 551 /** 552 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 553 * 554 * @param elements the elements 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(Iterator<? extends E> elements) { 561 super.addAll(elements); 562 return this; 563 } 564 565 /** 566 * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code 567 * Builder}. 568 */ 569 @Override 570 public ImmutableMultiset<E> build() { 571 return copyOf(contents); 572 } 573 } 574}