1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.GwtCompatible; 22 import com.google.common.collect.Multiset.Entry; 23 import com.google.common.primitives.Ints; 24 25 import java.io.Serializable; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.Iterator; 31 import java.util.List; 32 import java.util.Set; 33 34 import javax.annotation.Nullable; 35 36 /** 37 * An immutable hash-based multiset. Does not permit null elements. 38 * 39 * <p>Its iterator orders elements according to the first appearance of the 40 * element among the items passed to the factory method or builder. When the 41 * multiset contains multiple instances of an element, those instances are 42 * consecutive in the iteration order. 43 * 44 * @author Jared Levy 45 * @author Louis Wasserman 46 * @since 2.0 (imported from Google Collections Library) 47 */ 48 @GwtCompatible(serializable = true) 49 @SuppressWarnings("serial") // we're overriding default serialization 50 // TODO(user): write an efficient asList() implementation 51 public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> 52 implements Multiset<E> { 53 54 /** 55 * Returns the empty immutable multiset. 56 */ 57 @SuppressWarnings("unchecked") // all supported methods are covariant of()58 public static <E> ImmutableMultiset<E> of() { 59 return (ImmutableMultiset<E>) EmptyImmutableMultiset.INSTANCE; 60 } 61 62 /** 63 * Returns an immutable multiset containing a single element. 64 * 65 * @throws NullPointerException if {@code element} is null 66 * @since 6.0 (source-compatible since 2.0) 67 */ 68 @SuppressWarnings("unchecked") // generic array created but never written of(E element)69 public static <E> ImmutableMultiset<E> of(E element) { 70 return copyOfInternal(element); 71 } 72 73 /** 74 * Returns an immutable multiset containing the given elements, in order. 75 * 76 * @throws NullPointerException if any element is null 77 * @since 6.0 (source-compatible since 2.0) 78 */ 79 @SuppressWarnings("unchecked") // of(E e1, E e2)80 public static <E> ImmutableMultiset<E> of(E e1, E e2) { 81 return copyOfInternal(e1, e2); 82 } 83 84 /** 85 * Returns an immutable multiset containing the given elements, in order. 86 * 87 * @throws NullPointerException if any element is null 88 * @since 6.0 (source-compatible since 2.0) 89 */ 90 @SuppressWarnings("unchecked") // of(E e1, E e2, E e3)91 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) { 92 return copyOfInternal(e1, e2, e3); 93 } 94 95 /** 96 * Returns an immutable multiset containing the given elements, in order. 97 * 98 * @throws NullPointerException if any element is null 99 * @since 6.0 (source-compatible since 2.0) 100 */ 101 @SuppressWarnings("unchecked") // of(E e1, E e2, E e3, E e4)102 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) { 103 return copyOfInternal(e1, e2, e3, e4); 104 } 105 106 /** 107 * Returns an immutable multiset containing the given elements, in order. 108 * 109 * @throws NullPointerException if any element is null 110 * @since 6.0 (source-compatible since 2.0) 111 */ 112 @SuppressWarnings("unchecked") // of(E e1, E e2, E e3, E e4, E e5)113 public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) { 114 return copyOfInternal(e1, e2, e3, e4, e5); 115 } 116 117 /** 118 * Returns an immutable multiset containing the given elements, in order. 119 * 120 * @throws NullPointerException if any element is null 121 * @since 6.0 (source-compatible since 2.0) 122 */ 123 @SuppressWarnings("unchecked") // of( E e1, E e2, E e3, E e4, E e5, E e6, E... others)124 public static <E> ImmutableMultiset<E> of( 125 E e1, E e2, E e3, E e4, E e5, E e6, E... others) { 126 int size = others.length + 6; 127 List<E> all = new ArrayList<E>(size); 128 Collections.addAll(all, e1, e2, e3, e4, e5, e6); 129 Collections.addAll(all, others); 130 return copyOf(all); 131 } 132 133 /** 134 * Returns an immutable multiset containing the given elements. 135 * 136 * <p>The multiset is ordered by the first occurrence of each element. For 137 * example, {@code ImmutableMultiset.of(2, 3, 1, 3)} yields a multiset with 138 * elements in the order {@code 2, 3, 3, 1}. 139 * 140 * @throws NullPointerException if any of {@code elements} is null 141 * @deprecated use {@link #copyOf(Object[])}. <b>This method is scheduled for 142 * deletion in January 2012.</b> 143 * @since 2.0 (changed from varargs in 6.0) 144 */ 145 @Deprecated of(E[] elements)146 public static <E> ImmutableMultiset<E> of(E[] elements) { 147 return copyOf(Arrays.asList(elements)); 148 } 149 150 /** 151 * Returns an immutable multiset containing the given elements. 152 * 153 * <p>The multiset is ordered by the first occurrence of each element. For 154 * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset 155 * with elements in the order {@code 2, 3, 3, 1}. 156 * 157 * @throws NullPointerException if any of {@code elements} is null 158 * @since 6.0 159 */ copyOf(E[] elements)160 public static <E> ImmutableMultiset<E> copyOf(E[] elements) { 161 return copyOf(Arrays.asList(elements)); 162 } 163 164 /** 165 * Returns an immutable multiset containing the given elements. 166 * 167 * <p>The multiset is ordered by the first occurrence of each element. For 168 * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields 169 * a multiset with elements in the order {@code 2, 3, 3, 1}. 170 * 171 * <p>Despite the method name, this method attempts to avoid actually copying 172 * the data when it is safe to do so. The exact circumstances under which a 173 * copy will or will not be performed are undocumented and subject to change. 174 * 175 * <p><b>Note:</b> Despite what the method name suggests, if {@code elements} 176 * is an {@code ImmutableMultiset}, no copy will actually be performed, and 177 * the given multiset itself will be returned. 178 * 179 * @throws NullPointerException if any of {@code elements} is null 180 */ copyOf( Iterable<? extends E> elements)181 public static <E> ImmutableMultiset<E> copyOf( 182 Iterable<? extends E> elements) { 183 if (elements instanceof ImmutableMultiset) { 184 @SuppressWarnings("unchecked") // all supported methods are covariant 185 ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements; 186 if (!result.isPartialView()) { 187 return result; 188 } 189 } 190 191 Multiset<? extends E> multiset = (elements instanceof Multiset) 192 ? Multisets.cast(elements) 193 : LinkedHashMultiset.create(elements); 194 195 return copyOfInternal(multiset); 196 } 197 copyOfInternal(E... elements)198 private static <E> ImmutableMultiset<E> copyOfInternal(E... elements) { 199 return copyOf(Arrays.asList(elements)); 200 } 201 copyOfInternal( Multiset<? extends E> multiset)202 private static <E> ImmutableMultiset<E> copyOfInternal( 203 Multiset<? extends E> multiset) { 204 return copyFromEntries(multiset.entrySet()); 205 } 206 copyFromEntries( Collection<? extends Entry<? extends E>> entries)207 static <E> ImmutableMultiset<E> copyFromEntries( 208 Collection<? extends Entry<? extends E>> entries) { 209 long size = 0; 210 ImmutableMap.Builder<E, Integer> builder = ImmutableMap.builder(); 211 for (Entry<? extends E> entry : entries) { 212 int count = entry.getCount(); 213 if (count > 0) { 214 // Since ImmutableMap.Builder throws an NPE if an element is null, no 215 // other null checks are needed. 216 builder.put(entry.getElement(), count); 217 size += count; 218 } 219 } 220 221 if (size == 0) { 222 return of(); 223 } 224 return new RegularImmutableMultiset<E>(builder.build(), Ints.saturatedCast(size)); 225 } 226 227 /** 228 * Returns an immutable multiset containing the given elements. 229 * 230 * <p>The multiset is ordered by the first occurrence of each element. For 231 * example, 232 * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3).iterator())} 233 * yields a multiset with elements in the order {@code 2, 3, 3, 1}. 234 * 235 * @throws NullPointerException if any of {@code elements} is null 236 */ copyOf( Iterator<? extends E> elements)237 public static <E> ImmutableMultiset<E> copyOf( 238 Iterator<? extends E> elements) { 239 Multiset<E> multiset = LinkedHashMultiset.create(); 240 Iterators.addAll(multiset, elements); 241 return copyOfInternal(multiset); 242 } 243 ImmutableMultiset()244 ImmutableMultiset() {} 245 iterator()246 @Override public UnmodifiableIterator<E> iterator() { 247 final Iterator<Entry<E>> entryIterator = entryIterator(); 248 249 return new UnmodifiableIterator<E>() { 250 int remaining; 251 E element; 252 253 @Override 254 public boolean hasNext() { 255 return (remaining > 0) || entryIterator.hasNext(); 256 } 257 258 @Override 259 public E next() { 260 if (remaining <= 0) { 261 Entry<E> entry = entryIterator.next(); 262 element = entry.getElement(); 263 remaining = entry.getCount(); 264 } 265 remaining--; 266 return element; 267 } 268 }; 269 } 270 271 @Override contains(@ullable Object object)272 public boolean contains(@Nullable Object object) { 273 return count(object) > 0; 274 } 275 276 @Override containsAll(Collection<?> targets)277 public boolean containsAll(Collection<?> targets) { 278 return elementSet().containsAll(targets); 279 } 280 281 /** 282 * Guaranteed to throw an exception and leave the collection unmodified. 283 * 284 * @throws UnsupportedOperationException always 285 */ 286 @Override add(E element, int occurrences)287 public final int add(E 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 */ 296 @Override remove(Object element, int occurrences)297 public final int remove(Object element, int occurrences) { 298 throw new UnsupportedOperationException(); 299 } 300 301 /** 302 * Guaranteed to throw an exception and leave the collection unmodified. 303 * 304 * @throws UnsupportedOperationException always 305 */ 306 @Override setCount(E element, int count)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 */ 316 @Override setCount(E element, int oldCount, int newCount)317 public final boolean setCount(E element, int oldCount, int newCount) { 318 throw new UnsupportedOperationException(); 319 } 320 equals(@ullable Object object)321 @Override public boolean equals(@Nullable Object object) { 322 if (object == this) { 323 return true; 324 } 325 if (object instanceof Multiset) { 326 Multiset<?> that = (Multiset<?>) object; 327 if (this.size() != that.size()) { 328 return false; 329 } 330 for (Entry<?> entry : that.entrySet()) { 331 if (count(entry.getElement()) != entry.getCount()) { 332 return false; 333 } 334 } 335 return true; 336 } 337 return false; 338 } 339 hashCode()340 @Override public int hashCode() { 341 return Sets.hashCodeImpl(entrySet()); 342 } 343 toString()344 @Override public String toString() { 345 return entrySet().toString(); 346 } 347 348 private transient ImmutableSet<Entry<E>> entrySet; 349 350 @Override entrySet()351 public Set<Entry<E>> entrySet() { 352 ImmutableSet<Entry<E>> es = entrySet; 353 return (es == null) ? (entrySet = createEntrySet()) : es; 354 } 355 entryIterator()356 abstract UnmodifiableIterator<Entry<E>> entryIterator(); 357 distinctElements()358 abstract int distinctElements(); 359 createEntrySet()360 ImmutableSet<Entry<E>> createEntrySet() { 361 return new EntrySet<E>(this); 362 } 363 364 static class EntrySet<E> extends ImmutableSet<Entry<E>> { 365 transient final ImmutableMultiset<E> multiset; 366 EntrySet(ImmutableMultiset<E> multiset)367 public EntrySet(ImmutableMultiset<E> multiset) { 368 this.multiset = multiset; 369 } 370 371 @Override iterator()372 public UnmodifiableIterator<Entry<E>> iterator() { 373 return multiset.entryIterator(); 374 } 375 376 @Override size()377 public int size() { 378 return multiset.distinctElements(); 379 } 380 381 @Override isPartialView()382 boolean isPartialView() { 383 return multiset.isPartialView(); 384 } 385 386 @Override contains(Object o)387 public boolean contains(Object o) { 388 if (o instanceof Entry) { 389 Entry<?> entry = (Entry<?>) o; 390 if (entry.getCount() <= 0) { 391 return false; 392 } 393 int count = multiset.count(entry.getElement()); 394 return count == entry.getCount(); 395 } 396 return false; 397 } 398 399 /* 400 * TODO(hhchan): Revert once we have a separate, manual emulation of this 401 * class. 402 */ 403 @Override toArray()404 public Object[] toArray() { 405 Object[] newArray = new Object[size()]; 406 return toArray(newArray); 407 } 408 409 /* 410 * TODO(hhchan): Revert once we have a separate, manual emulation of this 411 * class. 412 */ 413 @Override toArray(T[] other)414 public <T> T[] toArray(T[] other) { 415 int size = size(); 416 if (other.length < size) { 417 other = ObjectArrays.newArray(other, size); 418 } else if (other.length > size) { 419 other[size] = null; 420 } 421 422 // Writes will produce ArrayStoreException when the toArray() doc requires 423 Object[] otherAsObjectArray = other; 424 int index = 0; 425 for (Entry<?> element : this) { 426 otherAsObjectArray[index++] = element; 427 } 428 return other; 429 } 430 431 @Override hashCode()432 public int hashCode() { 433 return multiset.hashCode(); 434 } 435 436 // We can't label this with @Override, because it doesn't override anything 437 // in the GWT emulated version. writeReplace()438 Object writeReplace() { 439 return new EntrySetSerializedForm<E>(multiset); 440 } 441 442 static class EntrySetSerializedForm<E> implements Serializable { 443 final ImmutableMultiset<E> multiset; 444 EntrySetSerializedForm(ImmutableMultiset<E> multiset)445 EntrySetSerializedForm(ImmutableMultiset<E> multiset) { 446 this.multiset = multiset; 447 } 448 readResolve()449 Object readResolve() { 450 return multiset.entrySet(); 451 } 452 } 453 454 private static final long serialVersionUID = 0; 455 } 456 457 private static class SerializedForm implements Serializable { 458 final Object[] elements; 459 final int[] counts; 460 SerializedForm(Multiset<?> multiset)461 SerializedForm(Multiset<?> multiset) { 462 int distinct = multiset.entrySet().size(); 463 elements = new Object[distinct]; 464 counts = new int[distinct]; 465 int i = 0; 466 for (Entry<?> entry : multiset.entrySet()) { 467 elements[i] = entry.getElement(); 468 counts[i] = entry.getCount(); 469 i++; 470 } 471 } 472 readResolve()473 Object readResolve() { 474 LinkedHashMultiset<Object> multiset = 475 LinkedHashMultiset.create(elements.length); 476 for (int i = 0; i < elements.length; i++) { 477 multiset.add(elements[i], counts[i]); 478 } 479 return ImmutableMultiset.copyOf(multiset); 480 } 481 482 private static final long serialVersionUID = 0; 483 } 484 485 // We can't label this with @Override, because it doesn't override anything 486 // in the GWT emulated version. writeReplace()487 Object writeReplace() { 488 return new SerializedForm(this); 489 } 490 491 /** 492 * Returns a new builder. The generated builder is equivalent to the builder 493 * created by the {@link Builder} constructor. 494 */ builder()495 public static <E> Builder<E> builder() { 496 return new Builder<E>(); 497 } 498 499 /** 500 * A builder for creating immutable multiset instances, especially {@code 501 * public static final} multisets ("constant multisets"). Example: 502 * <pre> {@code 503 * 504 * public static final ImmutableMultiset<Bean> BEANS = 505 * new ImmutableMultiset.Builder<Bean>() 506 * .addCopies(Bean.COCOA, 4) 507 * .addCopies(Bean.GARDEN, 6) 508 * .addCopies(Bean.RED, 8) 509 * .addCopies(Bean.BLACK_EYED, 10) 510 * .build();}</pre> 511 * 512 * Builder instances can be reused; it is safe to call {@link #build} multiple 513 * times to build multiple multisets in series. 514 * 515 * @since 2.0 (imported from Google Collections Library) 516 */ 517 public static class Builder<E> extends ImmutableCollection.Builder<E> { 518 final Multiset<E> contents; 519 520 /** 521 * Creates a new builder. The returned builder is equivalent to the builder 522 * generated by {@link ImmutableMultiset#builder}. 523 */ Builder()524 public Builder() { 525 this(LinkedHashMultiset.<E>create()); 526 } 527 Builder(Multiset<E> contents)528 Builder(Multiset<E> contents) { 529 this.contents = contents; 530 } 531 532 /** 533 * Adds {@code element} to the {@code ImmutableMultiset}. 534 * 535 * @param element the element to add 536 * @return this {@code Builder} object 537 * @throws NullPointerException if {@code element} is null 538 */ add(E element)539 @Override public Builder<E> add(E element) { 540 contents.add(checkNotNull(element)); 541 return this; 542 } 543 544 /** 545 * Adds a number of occurrences of an element to this {@code 546 * ImmutableMultiset}. 547 * 548 * @param element the element to add 549 * @param occurrences the number of occurrences of the element to add. May 550 * be zero, in which case no change will be made. 551 * @return this {@code Builder} object 552 * @throws NullPointerException if {@code element} is null 553 * @throws IllegalArgumentException if {@code occurrences} is negative, or 554 * if this operation would result in more than {@link Integer#MAX_VALUE} 555 * occurrences of the element 556 */ addCopies(E element, int occurrences)557 public Builder<E> addCopies(E element, int occurrences) { 558 contents.add(checkNotNull(element), occurrences); 559 return this; 560 } 561 562 /** 563 * Adds or removes the necessary occurrences of an element such that the 564 * element attains the desired count. 565 * 566 * @param element the element to add or remove occurrences of 567 * @param count the desired count of the element in this multiset 568 * @return this {@code Builder} object 569 * @throws NullPointerException if {@code element} is null 570 * @throws IllegalArgumentException if {@code count} is negative 571 */ setCount(E element, int count)572 public Builder<E> setCount(E element, int count) { 573 contents.setCount(checkNotNull(element), count); 574 return this; 575 } 576 577 /** 578 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 579 * 580 * @param elements the elements to add 581 * @return this {@code Builder} object 582 * @throws NullPointerException if {@code elements} is null or contains a 583 * null element 584 */ add(E... elements)585 @Override public Builder<E> add(E... elements) { 586 super.add(elements); 587 return this; 588 } 589 590 /** 591 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 592 * 593 * @param elements the {@code Iterable} to add to the {@code 594 * ImmutableMultiset} 595 * @return this {@code Builder} object 596 * @throws NullPointerException if {@code elements} is null or contains a 597 * null element 598 */ addAll(Iterable<? extends E> elements)599 @Override public Builder<E> addAll(Iterable<? extends E> elements) { 600 if (elements instanceof Multiset) { 601 Multiset<? extends E> multiset = Multisets.cast(elements); 602 for (Entry<? extends E> entry : multiset.entrySet()) { 603 addCopies(entry.getElement(), entry.getCount()); 604 } 605 } else { 606 super.addAll(elements); 607 } 608 return this; 609 } 610 611 /** 612 * Adds each element of {@code elements} to the {@code ImmutableMultiset}. 613 * 614 * @param elements the elements to add to the {@code ImmutableMultiset} 615 * @return this {@code Builder} object 616 * @throws NullPointerException if {@code elements} is null or contains a 617 * null element 618 */ addAll(Iterator<? extends E> elements)619 @Override public Builder<E> addAll(Iterator<? extends E> elements) { 620 super.addAll(elements); 621 return this; 622 } 623 624 /** 625 * Returns a newly-created {@code ImmutableMultiset} based on the contents 626 * of the {@code Builder}. 627 */ build()628 @Override public ImmutableMultiset<E> build() { 629 return copyOf(contents); 630 } 631 } 632 } 633