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