001 /* 002 * Copyright (C) 2007 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 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkArgument; 020 import static com.google.common.base.Preconditions.checkNotNull; 021 import static com.google.common.base.Preconditions.checkState; 022 023 import com.google.common.annotations.GwtCompatible; 024 import com.google.common.base.Objects; 025 import com.google.common.collect.Multiset.Entry; 026 import com.google.common.primitives.Ints; 027 028 import java.io.Serializable; 029 import java.util.AbstractSet; 030 import java.util.Collection; 031 import java.util.Collections; 032 import java.util.Iterator; 033 import java.util.NoSuchElementException; 034 import java.util.Set; 035 036 import javax.annotation.Nullable; 037 038 /** 039 * Provides static utility methods for creating and working with {@link 040 * Multiset} instances. 041 * 042 * @author Kevin Bourrillion 043 * @author Mike Bostock 044 * @author Louis Wasserman 045 * @since 2 (imported from Google Collections Library) 046 */ 047 @GwtCompatible 048 public final class Multisets { 049 private Multisets() {} 050 051 /** 052 * Returns an unmodifiable view of the specified multiset. Query operations on 053 * the returned multiset "read through" to the specified multiset, and 054 * attempts to modify the returned multiset result in an 055 * {@link UnsupportedOperationException}. 056 * 057 * <p>The returned multiset will be serializable if the specified multiset is 058 * serializable. 059 * 060 * @param multiset the multiset for which an unmodifiable view is to be 061 * generated 062 * @return an unmodifiable view of the multiset 063 */ 064 public static <E> Multiset<E> unmodifiableMultiset( 065 Multiset<? extends E> multiset) { 066 return new UnmodifiableMultiset<E>(checkNotNull(multiset)); 067 } 068 069 private static class UnmodifiableMultiset<E> 070 extends ForwardingMultiset<E> implements Serializable { 071 final Multiset<? extends E> delegate; 072 073 UnmodifiableMultiset(Multiset<? extends E> delegate) { 074 this.delegate = delegate; 075 } 076 077 @SuppressWarnings("unchecked") 078 @Override protected Multiset<E> delegate() { 079 // This is safe because all non-covariant methods are overriden 080 return (Multiset<E>) delegate; 081 } 082 083 transient Set<E> elementSet; 084 085 @Override public Set<E> elementSet() { 086 Set<E> es = elementSet; 087 return (es == null) 088 ? elementSet = Collections.<E>unmodifiableSet(delegate.elementSet()) 089 : es; 090 } 091 092 transient Set<Multiset.Entry<E>> entrySet; 093 094 @SuppressWarnings("unchecked") 095 @Override public Set<Multiset.Entry<E>> entrySet() { 096 Set<Multiset.Entry<E>> es = entrySet; 097 return (es == null) 098 // Safe because the returned set is made unmodifiable and Entry 099 // itself is readonly 100 ? entrySet = (Set) Collections.unmodifiableSet(delegate.entrySet()) 101 : es; 102 } 103 104 @SuppressWarnings("unchecked") 105 @Override public Iterator<E> iterator() { 106 // Safe because the returned Iterator is made unmodifiable 107 return (Iterator<E>) Iterators.unmodifiableIterator(delegate.iterator()); 108 } 109 110 @Override public boolean add(E element) { 111 throw new UnsupportedOperationException(); 112 } 113 114 @Override public int add(E element, int occurences) { 115 throw new UnsupportedOperationException(); 116 } 117 118 @Override public boolean addAll(Collection<? extends E> elementsToAdd) { 119 throw new UnsupportedOperationException(); 120 } 121 122 @Override public boolean remove(Object element) { 123 throw new UnsupportedOperationException(); 124 } 125 126 @Override public int remove(Object element, int occurrences) { 127 throw new UnsupportedOperationException(); 128 } 129 130 @Override public boolean removeAll(Collection<?> elementsToRemove) { 131 throw new UnsupportedOperationException(); 132 } 133 134 @Override public boolean retainAll(Collection<?> elementsToRetain) { 135 throw new UnsupportedOperationException(); 136 } 137 138 @Override public void clear() { 139 throw new UnsupportedOperationException(); 140 } 141 142 @Override public int setCount(E element, int count) { 143 throw new UnsupportedOperationException(); 144 } 145 146 @Override public boolean setCount(E element, int oldCount, int newCount) { 147 throw new UnsupportedOperationException(); 148 } 149 150 private static final long serialVersionUID = 0; 151 } 152 153 /** 154 * Returns an immutable multiset entry with the specified element and count. 155 * 156 * @param e the element to be associated with the returned entry 157 * @param n the count to be associated with the returned entry 158 * @throws IllegalArgumentException if {@code n} is negative 159 */ 160 public static <E> Multiset.Entry<E> immutableEntry( 161 @Nullable final E e, final int n) { 162 checkArgument(n >= 0); 163 return new AbstractEntry<E>() { 164 @Override 165 public E getElement() { 166 return e; 167 } 168 @Override 169 public int getCount() { 170 return n; 171 } 172 }; 173 } 174 175 /** 176 * Returns a multiset view of the specified set. The multiset is backed by the 177 * set, so changes to the set are reflected in the multiset, and vice versa. 178 * If the set is modified while an iteration over the multiset is in progress 179 * (except through the iterator's own {@code remove} operation) the results of 180 * the iteration are undefined. 181 * 182 * <p>The multiset supports element removal, which removes the corresponding 183 * element from the set. It does not support the {@code add} or {@code addAll} 184 * operations, nor does it support the use of {@code setCount} to add 185 * elements. 186 * 187 * <p>The returned multiset will be serializable if the specified set is 188 * serializable. The multiset is threadsafe if the set is threadsafe. 189 * 190 * @param set the backing set for the returned multiset view 191 */ 192 static <E> Multiset<E> forSet(Set<E> set) { 193 return new SetMultiset<E>(set); 194 } 195 196 /** @see Multisets#forSet */ 197 private static class SetMultiset<E> extends ForwardingCollection<E> 198 implements Multiset<E>, Serializable { 199 final Set<E> delegate; 200 201 SetMultiset(Set<E> set) { 202 delegate = checkNotNull(set); 203 } 204 205 @Override protected Set<E> delegate() { 206 return delegate; 207 } 208 209 @Override 210 public int count(Object element) { 211 return delegate.contains(element) ? 1 : 0; 212 } 213 214 @Override 215 public int add(E element, int occurrences) { 216 throw new UnsupportedOperationException(); 217 } 218 219 @Override 220 public int remove(Object element, int occurrences) { 221 if (occurrences == 0) { 222 return count(element); 223 } 224 checkArgument(occurrences > 0); 225 return delegate.remove(element) ? 1 : 0; 226 } 227 228 transient Set<E> elementSet; 229 230 @Override 231 public Set<E> elementSet() { 232 Set<E> es = elementSet; 233 return (es == null) ? elementSet = new ElementSet() : es; 234 } 235 236 transient Set<Entry<E>> entrySet; 237 238 @Override 239 public Set<Entry<E>> entrySet() { 240 Set<Entry<E>> es = entrySet; 241 return (es == null) ? entrySet = new EntrySet() : es; 242 } 243 244 @Override public boolean add(E o) { 245 throw new UnsupportedOperationException(); 246 } 247 248 @Override public boolean addAll(Collection<? extends E> c) { 249 throw new UnsupportedOperationException(); 250 } 251 252 @Override 253 public int setCount(E element, int count) { 254 checkNonnegative(count, "count"); 255 256 if (count == count(element)) { 257 return count; 258 } else if (count == 0) { 259 remove(element); 260 return 1; 261 } else { 262 throw new UnsupportedOperationException(); 263 } 264 } 265 266 @Override 267 public boolean setCount(E element, int oldCount, int newCount) { 268 return setCountImpl(this, element, oldCount, newCount); 269 } 270 271 @Override public boolean equals(@Nullable Object object) { 272 if (object instanceof Multiset) { 273 Multiset<?> that = (Multiset<?>) object; 274 return this.size() == that.size() && delegate.equals(that.elementSet()); 275 } 276 return false; 277 } 278 279 @Override public int hashCode() { 280 int sum = 0; 281 for (E e : this) { 282 sum += ((e == null) ? 0 : e.hashCode()) ^ 1; 283 } 284 return sum; 285 } 286 287 /** @see SetMultiset#elementSet */ 288 class ElementSet extends ForwardingSet<E> { 289 @Override protected Set<E> delegate() { 290 return delegate; 291 } 292 293 @Override public boolean add(E o) { 294 throw new UnsupportedOperationException(); 295 } 296 297 @Override public boolean addAll(Collection<? extends E> c) { 298 throw new UnsupportedOperationException(); 299 } 300 } 301 302 /** @see SetMultiset#entrySet */ 303 class EntrySet extends AbstractSet<Entry<E>> { 304 @Override public int size() { 305 return delegate.size(); 306 } 307 @Override public Iterator<Entry<E>> iterator() { 308 return new Iterator<Entry<E>>() { 309 final Iterator<E> elements = delegate.iterator(); 310 311 @Override 312 public boolean hasNext() { 313 return elements.hasNext(); 314 } 315 @Override 316 public Entry<E> next() { 317 return immutableEntry(elements.next(), 1); 318 } 319 @Override 320 public void remove() { 321 elements.remove(); 322 } 323 }; 324 } 325 // TODO(kevinb): faster contains, remove 326 } 327 328 private static final long serialVersionUID = 0; 329 } 330 331 /** 332 * Returns the expected number of distinct elements given the specified 333 * elements. The number of distinct elements is only computed if {@code 334 * elements} is an instance of {@code Multiset}; otherwise the default value 335 * of 11 is returned. 336 */ 337 static int inferDistinctElements(Iterable<?> elements) { 338 if (elements instanceof Multiset) { 339 return ((Multiset<?>) elements).elementSet().size(); 340 } 341 return 11; // initial capacity will be rounded up to 16 342 } 343 344 /** 345 * Returns an unmodifiable <b>view</b> of the intersection of two multisets. 346 * An element's count in the multiset is the smaller of its counts in the two 347 * backing multisets. The iteration order of the returned multiset matches the 348 * element set of {@code multiset1}, with repeated occurrences of the same 349 * element appearing consecutively. 350 * 351 * <p>Results are undefined if {@code multiset1} and {@code multiset2} are 352 * based on different equivalence relations (as {@code HashMultiset} and 353 * {@code TreeMultiset} are). 354 * 355 * @since 2 356 */ 357 public static <E> Multiset<E> intersection( 358 final Multiset<E> multiset1, final Multiset<?> multiset2) { 359 checkNotNull(multiset1); 360 checkNotNull(multiset2); 361 362 return new AbstractMultiset<E>() { 363 @Override public int count(Object element) { 364 int count1 = multiset1.count(element); 365 return (count1 == 0) ? 0 : Math.min(count1, multiset2.count(element)); 366 } 367 368 @Override Set<E> createElementSet() { 369 return Sets.intersection( 370 multiset1.elementSet(), multiset2.elementSet()); 371 } 372 373 @Override public Set<Entry<E>> entrySet() { 374 return entrySet; 375 } 376 377 final Set<Entry<E>> entrySet = new AbstractSet<Entry<E>>() { 378 @Override public Iterator<Entry<E>> iterator() { 379 final Iterator<Entry<E>> iterator1 = multiset1.entrySet().iterator(); 380 return new AbstractIterator<Entry<E>>() { 381 @Override protected Entry<E> computeNext() { 382 while (iterator1.hasNext()) { 383 Entry<E> entry1 = iterator1.next(); 384 E element = entry1.getElement(); 385 int count 386 = Math.min(entry1.getCount(), multiset2.count(element)); 387 if (count > 0) { 388 return Multisets.immutableEntry(element, count); 389 } 390 } 391 return endOfData(); 392 } 393 }; 394 } 395 396 @Override public int size() { 397 return elementSet().size(); 398 } 399 400 @Override public boolean contains(Object o) { 401 if (o instanceof Entry) { 402 Entry<?> entry = (Entry<?>) o; 403 int entryCount = entry.getCount(); 404 return (entryCount > 0) 405 && (count(entry.getElement()) == entryCount); 406 } 407 return false; 408 } 409 410 @Override public boolean isEmpty() { 411 return elementSet().isEmpty(); 412 } 413 }; 414 }; 415 } 416 417 /** 418 * Implementation of the {@code equals}, {@code hashCode}, and 419 * {@code toString} methods of {@link Multiset.Entry}. 420 */ 421 abstract static class AbstractEntry<E> implements Multiset.Entry<E> { 422 /** 423 * Indicates whether an object equals this entry, following the behavior 424 * specified in {@link Multiset.Entry#equals}. 425 */ 426 @Override public boolean equals(@Nullable Object object) { 427 if (object instanceof Multiset.Entry) { 428 Multiset.Entry<?> that = (Multiset.Entry<?>) object; 429 return this.getCount() == that.getCount() 430 && Objects.equal(this.getElement(), that.getElement()); 431 } 432 return false; 433 } 434 435 /** 436 * Return this entry's hash code, following the behavior specified in 437 * {@link Multiset.Entry#hashCode}. 438 */ 439 @Override public int hashCode() { 440 E e = getElement(); 441 return ((e == null) ? 0 : e.hashCode()) ^ getCount(); 442 } 443 444 /** 445 * Returns a string representation of this multiset entry. The string 446 * representation consists of the associated element if the associated count 447 * is one, and otherwise the associated element followed by the characters 448 * " x " (space, x and space) followed by the count. Elements and counts are 449 * converted to strings as by {@code String.valueOf}. 450 */ 451 @Override public String toString() { 452 String text = String.valueOf(getElement()); 453 int n = getCount(); 454 return (n == 1) ? text : (text + " x " + n); 455 } 456 } 457 458 /** 459 * An implementation of {@link Multiset#equals}. 460 */ 461 static boolean equalsImpl(Multiset<?> multiset, @Nullable Object object) { 462 if (object == multiset) { 463 return true; 464 } 465 if (object instanceof Multiset) { 466 Multiset<?> that = (Multiset<?>) object; 467 /* 468 * We can't simply check whether the entry sets are equal, since that 469 * approach fails when a TreeMultiset has a comparator that returns 0 470 * when passed unequal elements. 471 */ 472 473 if (multiset.size() != that.size() 474 || multiset.entrySet().size() != that.entrySet().size()) { 475 return false; 476 } 477 for (Entry<?> entry : that.entrySet()) { 478 if (multiset.count(entry.getElement()) != entry.getCount()) { 479 return false; 480 } 481 } 482 return true; 483 } 484 return false; 485 } 486 487 /** 488 * An implementation of {@link Multiset#addAll}. 489 */ 490 static <E> boolean addAllImpl( 491 Multiset<E> self, Collection<? extends E> elements) { 492 if (elements.isEmpty()) { 493 return false; 494 } 495 if (elements instanceof Multiset) { 496 Multiset<? extends E> that = cast(elements); 497 for (Entry<? extends E> entry : that.entrySet()) { 498 self.add(entry.getElement(), entry.getCount()); 499 } 500 } else { 501 Iterators.addAll(self, elements.iterator()); 502 } 503 return true; 504 } 505 506 /** 507 * An implementation of {@link Multiset#removeAll}. 508 */ 509 static boolean removeAllImpl( 510 Multiset<?> self, Collection<?> elementsToRemove) { 511 Collection<?> collection = (elementsToRemove instanceof Multiset) 512 ? ((Multiset<?>) elementsToRemove).elementSet() : elementsToRemove; 513 514 return self.elementSet().removeAll(collection); 515 } 516 517 /** 518 * An implementation of {@link Multiset#retainAll}. 519 */ 520 static boolean retainAllImpl( 521 Multiset<?> self, Collection<?> elementsToRetain) { 522 Collection<?> collection = (elementsToRetain instanceof Multiset) 523 ? ((Multiset<?>) elementsToRetain).elementSet() : elementsToRetain; 524 525 return self.elementSet().retainAll(collection); 526 } 527 528 /** 529 * An implementation of {@link Multiset#setCount(Object, int)}. 530 */ 531 static <E> int setCountImpl(Multiset<E> self, E element, int count) { 532 checkNonnegative(count, "count"); 533 534 int oldCount = self.count(element); 535 536 int delta = count - oldCount; 537 if (delta > 0) { 538 self.add(element, delta); 539 } else if (delta < 0) { 540 self.remove(element, -delta); 541 } 542 543 return oldCount; 544 } 545 546 /** 547 * An implementation of {@link Multiset#setCount(Object, int, int)}. 548 */ 549 static <E> boolean setCountImpl( 550 Multiset<E> self, E element, int oldCount, int newCount) { 551 checkNonnegative(oldCount, "oldCount"); 552 checkNonnegative(newCount, "newCount"); 553 554 if (self.count(element) == oldCount) { 555 self.setCount(element, newCount); 556 return true; 557 } else { 558 return false; 559 } 560 } 561 562 /** 563 * An implementation of {@link Multiset#elementSet}. 564 */ 565 static <E> Set<E> elementSetImpl(Multiset<E> self){ 566 return new ElementSetImpl<E>(self); 567 } 568 569 private static final class ElementSetImpl<E> extends AbstractSet<E> 570 implements Serializable { 571 private final Multiset<E> multiset; 572 573 ElementSetImpl(Multiset<E> multiset) { 574 this.multiset = multiset; 575 } 576 577 @Override public boolean add(E e) { 578 throw new UnsupportedOperationException(); 579 } 580 581 @Override public boolean addAll(Collection<? extends E> c) { 582 throw new UnsupportedOperationException(); 583 } 584 585 @Override public void clear() { 586 multiset.clear(); 587 } 588 589 @Override public boolean contains(Object o) { 590 return multiset.contains(o); 591 } 592 593 @Override public boolean containsAll(Collection<?> c) { 594 return multiset.containsAll(c); 595 } 596 597 @Override public boolean isEmpty() { 598 return multiset.isEmpty(); 599 } 600 601 @Override public Iterator<E> iterator() { 602 final Iterator<Entry<E>> entryIterator = multiset.entrySet().iterator(); 603 return new Iterator<E>() { 604 605 @Override public boolean hasNext() { 606 return entryIterator.hasNext(); 607 } 608 609 @Override public E next() { 610 return entryIterator.next().getElement(); 611 } 612 613 @Override public void remove() { 614 entryIterator.remove(); 615 } 616 }; 617 } 618 619 @Override public boolean remove(Object o) { 620 int count = multiset.count(o); 621 if (count > 0) { 622 multiset.remove(o, count); 623 return true; 624 } 625 return false; 626 } 627 628 @Override public int size() { 629 return multiset.entrySet().size(); 630 } 631 632 private static final long serialVersionUID = 0; 633 } 634 635 /** 636 * An implementation of {@link Multiset#iterator}. 637 */ 638 static <E> Iterator<E> iteratorImpl(Multiset<E> multiset) { 639 return new MultisetIteratorImpl<E>( 640 multiset, multiset.entrySet().iterator()); 641 } 642 643 static final class MultisetIteratorImpl<E> implements Iterator<E> { 644 private final Multiset<E> multiset; 645 private final Iterator<Entry<E>> entryIterator; 646 private Entry<E> currentEntry; 647 /** Count of subsequent elements equal to current element */ 648 private int laterCount; 649 /** Count of all elements equal to current element */ 650 private int totalCount; 651 private boolean canRemove; 652 653 MultisetIteratorImpl( 654 Multiset<E> multiset, Iterator<Entry<E>> entryIterator) { 655 this.multiset = multiset; 656 this.entryIterator = entryIterator; 657 } 658 659 @Override 660 public boolean hasNext() { 661 return laterCount > 0 || entryIterator.hasNext(); 662 } 663 664 @Override 665 public E next() { 666 if (!hasNext()) { 667 throw new NoSuchElementException(); 668 } 669 if (laterCount == 0) { 670 currentEntry = entryIterator.next(); 671 totalCount = laterCount = currentEntry.getCount(); 672 } 673 laterCount--; 674 canRemove = true; 675 return currentEntry.getElement(); 676 } 677 678 @Override 679 public void remove() { 680 checkState( 681 canRemove, "no calls to next() since the last call to remove()"); 682 if (totalCount == 1) { 683 entryIterator.remove(); 684 } else { 685 multiset.remove(currentEntry.getElement()); 686 } 687 totalCount--; 688 canRemove = false; 689 } 690 } 691 692 /** 693 * An implementation of {@link Multiset#size}. 694 */ 695 static int sizeImpl(Multiset<?> multiset) { 696 long size = 0; 697 for (Entry<?> entry : multiset.entrySet()) { 698 size += entry.getCount(); 699 } 700 return Ints.saturatedCast(size); 701 } 702 703 static void checkNonnegative(int count, String name) { 704 checkArgument(count >= 0, "%s cannot be negative: %s", name, count); 705 } 706 707 /** 708 * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 709 */ 710 static <T> Multiset<T> cast(Iterable<T> iterable) { 711 return (Multiset<T>) iterable; 712 } 713 }