diff options
Diffstat (limited to 'guava/src/com/google/common/collect/Multisets.java')
-rw-r--r-- | guava/src/com/google/common/collect/Multisets.java | 562 |
1 files changed, 222 insertions, 340 deletions
diff --git a/guava/src/com/google/common/collect/Multisets.java b/guava/src/com/google/common/collect/Multisets.java index d0ab028..dbd54c3 100644 --- a/guava/src/com/google/common/collect/Multisets.java +++ b/guava/src/com/google/common/collect/Multisets.java @@ -18,33 +18,32 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Multiset.Entry; -import com.google.common.primitives.Ints; +import static com.google.common.base.Preconditions.checkState; import java.io.Serializable; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; +import java.util.SortedSet; import javax.annotation.Nullable; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.collect.Multiset.Entry; +import com.google.common.primitives.Ints; + /** * Provides static utility methods for creating and working with {@link * Multiset} instances. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multisets"> - * {@code Multisets}</a>. - * * @author Kevin Bourrillion * @author Mike Bostock * @author Louis Wasserman @@ -194,10 +193,92 @@ public final class Multisets { @Beta public static <E> SortedMultiset<E> unmodifiableSortedMultiset( SortedMultiset<E> sortedMultiset) { - // it's in its own file so it can be emulated for GWT return new UnmodifiableSortedMultiset<E>(checkNotNull(sortedMultiset)); } + private static final class UnmodifiableSortedMultiset<E> + extends UnmodifiableMultiset<E> implements SortedMultiset<E> { + private UnmodifiableSortedMultiset(SortedMultiset<E> delegate) { + super(delegate); + } + + @Override + protected SortedMultiset<E> delegate() { + return (SortedMultiset<E>) super.delegate(); + } + + @Override + public Comparator<? super E> comparator() { + return delegate().comparator(); + } + + @Override + SortedSet<E> createElementSet() { + return Collections.unmodifiableSortedSet(delegate().elementSet()); + } + + @Override + public SortedSet<E> elementSet() { + return (SortedSet<E>) super.elementSet(); + } + + private transient UnmodifiableSortedMultiset<E> descendingMultiset; + + @Override + public SortedMultiset<E> descendingMultiset() { + UnmodifiableSortedMultiset<E> result = descendingMultiset; + if (result == null) { + result = new UnmodifiableSortedMultiset<E>( + delegate().descendingMultiset()); + result.descendingMultiset = this; + return descendingMultiset = result; + } + return result; + } + + @Override + public Entry<E> firstEntry() { + return delegate().firstEntry(); + } + + @Override + public Entry<E> lastEntry() { + return delegate().lastEntry(); + } + + @Override + public Entry<E> pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public Entry<E> pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { + return unmodifiableSortedMultiset( + delegate().headMultiset(upperBound, boundType)); + } + + @Override + public SortedMultiset<E> subMultiset( + E lowerBound, BoundType lowerBoundType, + E upperBound, BoundType upperBoundType) { + return unmodifiableSortedMultiset(delegate().subMultiset( + lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + @Override + public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + return unmodifiableSortedMultiset( + delegate().tailMultiset(lowerBound, boundType)); + } + + private static final long serialVersionUID = 0; + } + /** * Returns an immutable multiset entry with the specified element and count. * The entry will be serializable if {@code e} is. @@ -235,125 +316,152 @@ public final class Multisets { } /** - * Returns a view of the elements of {@code unfiltered} that satisfy a predicate. The returned - * multiset is a live view of {@code unfiltered}; changes to one affect the other. - * - * <p>The resulting multiset's iterators, and those of its {@code entrySet()} and - * {@code elementSet()}, do not support {@code remove()}. However, all other multiset methods - * supported by {@code unfiltered} are supported by the returned multiset. When given an element - * that doesn't satisfy the predicate, the multiset's {@code add()} and {@code addAll()} methods - * throw an {@link IllegalArgumentException}. When methods such as {@code removeAll()} and - * {@code clear()} are called on the filtered multiset, only elements that satisfy the filter - * will be removed from the underlying multiset. + * Returns a multiset view of the specified set. The multiset is backed by the + * set, so changes to the set are reflected in the multiset, and vice versa. + * If the set is modified while an iteration over the multiset is in progress + * (except through the iterator's own {@code remove} operation) the results of + * the iteration are undefined. * - * <p>The returned multiset isn't threadsafe or serializable, even if {@code unfiltered} is. + * <p>The multiset supports element removal, which removes the corresponding + * element from the set. It does not support the {@code add} or {@code addAll} + * operations, nor does it support the use of {@code setCount} to add + * elements. * - * <p>Many of the filtered multiset's methods, such as {@code size()}, iterate across every - * element in the underlying multiset and determine which elements satisfy the filter. When a - * live view is <i>not</i> needed, it may be faster to copy the returned multiset and use the - * copy. + * <p>The returned multiset will be serializable if the specified set is + * serializable. The multiset is threadsafe if the set is threadsafe. * - * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as - * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See - * {@link Iterables#filter(Iterable, Class)} for related functionality.) - * - * @since 14.0 + * @param set the backing set for the returned multiset view */ - @Beta - public static <E> Multiset<E> filter(Multiset<E> unfiltered, Predicate<? super E> predicate) { - if (unfiltered instanceof FilteredMultiset) { - // Support clear(), removeAll(), and retainAll() when filtering a filtered - // collection. - FilteredMultiset<E> filtered = (FilteredMultiset<E>) unfiltered; - Predicate<E> combinedPredicate - = Predicates.<E>and(filtered.predicate, predicate); - return new FilteredMultiset<E>(filtered.unfiltered, combinedPredicate); - } - return new FilteredMultiset<E>(unfiltered, predicate); + static <E> Multiset<E> forSet(Set<E> set) { + return new SetMultiset<E>(set); } - private static final class FilteredMultiset<E> extends AbstractMultiset<E> { - final Multiset<E> unfiltered; - final Predicate<? super E> predicate; + /** @see Multisets#forSet */ + private static class SetMultiset<E> extends ForwardingCollection<E> + implements Multiset<E>, Serializable { + final Set<E> delegate; - FilteredMultiset(Multiset<E> unfiltered, Predicate<? super E> predicate) { - this.unfiltered = checkNotNull(unfiltered); - this.predicate = checkNotNull(predicate); + SetMultiset(Set<E> set) { + delegate = checkNotNull(set); } - @Override - Set<E> createElementSet() { - return Sets.filter(unfiltered.elementSet(), predicate); + @Override protected Set<E> delegate() { + return delegate; } @Override - Set<Entry<E>> createEntrySet() { - return Sets.filter(unfiltered.entrySet(), new Predicate<Entry<E>>() { - @Override - public boolean apply(Entry<E> entry) { - return predicate.apply(entry.getElement()); - } - }); + public int count(Object element) { + return delegate.contains(element) ? 1 : 0; } @Override - Iterator<Entry<E>> entryIterator() { - throw new AssertionError("should never be called"); + public int add(E element, int occurrences) { + throw new UnsupportedOperationException(); } @Override - int distinctElements() { - return elementSet().size(); + public int remove(Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0); + return delegate.remove(element) ? 1 : 0; } + transient Set<E> elementSet; + @Override - public boolean contains(@Nullable Object element) { - return count(element) > 0; + public Set<E> elementSet() { + Set<E> es = elementSet; + return (es == null) ? elementSet = new ElementSet() : es; } - @Override - public int count(@Nullable Object element) { - int count = unfiltered.count(element); - if (count > 0) { - @SuppressWarnings("unchecked") // element is equal to an E - E e = (E) element; - return predicate.apply(e) ? count : 0; + transient Set<Entry<E>> entrySet; + + @Override public Set<Entry<E>> entrySet() { + Set<Entry<E>> es = entrySet; + if (es == null) { + es = entrySet = new EntrySet<E>() { + @Override Multiset<E> multiset() { + return SetMultiset.this; + } + + @Override public Iterator<Entry<E>> iterator() { + return Iterators.transform(delegate.iterator(), + new Function<E, Entry<E>>() { + @Override public Entry<E> apply(E elem) { + return immutableEntry(elem, 1); + } + }); + } + + @Override public int size() { + return delegate.size(); + } + }; } - return 0; + return es; } - @Override - public int add(@Nullable E element, int occurrences) { - checkArgument(predicate.apply(element), - "Element %s does not match predicate %s", element, predicate); - return unfiltered.add(element, occurrences); + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); } @Override - public int remove(@Nullable Object element, int occurrences) { - Multisets.checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(element); + public int setCount(E element, int count) { + checkNonnegative(count, "count"); + + if (count == count(element)) { + return count; + } else if (count == 0) { + remove(element); + return 1; } else { - return contains(element) ? unfiltered.remove(element, occurrences) : 0; + throw new UnsupportedOperationException(); } } @Override - public boolean removeAll(Collection<?> c) { - return elementSet().removeAll(c); + public boolean setCount(E element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); } - @Override - public boolean retainAll(Collection<?> c) { - return elementSet().retainAll(c); + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multiset) { + Multiset<?> that = (Multiset<?>) object; + return this.size() == that.size() && delegate.equals(that.elementSet()); + } + return false; } - @Override - public void clear() { - elementSet().clear(); + @Override public int hashCode() { + int sum = 0; + for (E e : this) { + sum += ((e == null) ? 0 : e.hashCode()) ^ 1; + } + return sum; + } + + /** @see SetMultiset#elementSet */ + class ElementSet extends ForwardingSet<E> { + @Override protected Set<E> delegate() { + return delegate; + } + + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); + } } + + private static final long serialVersionUID = 0; } /** @@ -370,93 +478,16 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the union of two multisets. - * In the returned multiset, the count of each element is the <i>maximum</i> - * of its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that are - * not contained in {@code multiset1}, with repeated occurrences of the same + * Returns an unmodifiable <b>view</b> of the intersection of two multisets. + * An element's count in the multiset is the smaller of its counts in the two + * backing multisets. The iteration order of the returned multiset matches the + * element set of {@code multiset1}, with repeated occurrences of the same * element appearing consecutively. * * <p>Results are undefined if {@code multiset1} and {@code multiset2} are * based on different equivalence relations (as {@code HashMultiset} and * {@code TreeMultiset} are). * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> union( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int count(Object element) { - return Math.max(multiset1.count(element), multiset2.count(element)); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = Math.max(entry1.getCount(), multiset2.count(element)); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the intersection of two multisets. - * In the returned multiset, the count of each element is the <i>minimum</i> - * of its counts in the two backing multisets, with elements that would have - * a count of 0 not included. The iteration order of the returned multiset - * matches that of the element set of {@code multiset1}, with repeated - * occurrences of the same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * * @since 2.0 */ public static <E> Multiset<E> intersection( @@ -488,88 +519,7 @@ public final class Multisets { E element = entry1.getElement(); int count = Math.min(entry1.getCount(), multiset2.count(element)); if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the sum of two multisets. - * In the returned multiset, the count of each element is the <i>sum</i> of - * its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that that - * are not contained in {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> sum( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int size() { - return multiset1.size() + multiset2.size(); - } - - @Override - public int count(Object element) { - return multiset1.count(element) + multiset2.count(element); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() + multiset2.count(element); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); + return Multisets.immutableEntry(element, count); } } return endOfData(); @@ -585,66 +535,12 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the difference of two multisets. - * In the returned multiset, the count of each element is the result of the - * <i>zero-truncated subtraction</i> of its count in the second multiset from - * its count in the first multiset, with elements that would have a count of - * 0 not included. The iteration order of the returned multiset matches that - * of the element set of {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> difference( - final Multiset<E> multiset1, final Multiset<?> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public int count(@Nullable Object element) { - int count1 = multiset1.count(element); - return (count1 == 0) ? 0 : - Math.max(0, count1 - multiset2.count(element)); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<Entry<E>> iterator1 = multiset1.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - while (iterator1.hasNext()) { - Entry<E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() - multiset2.count(element); - if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return Iterators.size(entryIterator()); - } - }; - } - - /** * Returns {@code true} if {@code subMultiset.count(o) <= * superMultiset.count(o)} for all {@code o}. * * @since 10.0 */ + @Beta public static boolean containsOccurrences( Multiset<?> superMultiset, Multiset<?> subMultiset) { checkNotNull(superMultiset); @@ -677,7 +573,7 @@ public final class Multisets { * of this operation * @since 10.0 */ - public static boolean retainOccurrences(Multiset<?> multisetToModify, + @Beta public static boolean retainOccurrences(Multiset<?> multisetToModify, Multiset<?> multisetToRetain) { return retainOccurrencesImpl(multisetToModify, multisetToRetain); } @@ -729,7 +625,7 @@ public final class Multisets { * this operation * @since 10.0 */ - public static boolean removeOccurrences( + @Beta public static boolean removeOccurrences( Multiset<?> multisetToModify, Multiset<?> occurrencesToRemove) { return removeOccurrencesImpl(multisetToModify, occurrencesToRemove); } @@ -864,7 +760,6 @@ public final class Multisets { */ static boolean retainAllImpl( Multiset<?> self, Collection<?> elementsToRetain) { - checkNotNull(elementsToRetain); Collection<?> collection = (elementsToRetain instanceof Multiset) ? ((Multiset<?>) elementsToRetain).elementSet() : elementsToRetain; @@ -905,7 +800,7 @@ public final class Multisets { } } - abstract static class ElementSet<E> extends Sets.ImprovedAbstractSet<E> { + static abstract class ElementSet<E> extends AbstractSet<E> { abstract Multiset<E> multiset(); @Override public void clear() { @@ -925,12 +820,12 @@ public final class Multisets { } @Override public Iterator<E> iterator() { - return new TransformedIterator<Entry<E>, E>(multiset().entrySet().iterator()) { - @Override - E transform(Entry<E> entry) { - return entry.getElement(); - } - }; + return Iterators.transform(multiset().entrySet().iterator(), + new Function<Entry<E>, E>() { + @Override public E apply(Entry<E> entry) { + return entry.getElement(); + } + }); } @Override @@ -948,14 +843,11 @@ public final class Multisets { } } - abstract static class EntrySet<E> extends Sets.ImprovedAbstractSet<Entry<E>> { + static abstract class EntrySet<E> extends AbstractSet<Entry<E>>{ abstract Multiset<E> multiset(); @Override public boolean contains(@Nullable Object o) { if (o instanceof Entry) { - /* - * The GWT compiler wrongly issues a warning here. - */ @SuppressWarnings("cast") Entry<?> entry = (Entry<?>) o; if (entry.getCount() <= 0) { @@ -968,21 +860,10 @@ public final class Multisets { return false; } - // GWT compiler warning; see contains(). @SuppressWarnings("cast") - @Override public boolean remove(Object object) { - if (object instanceof Multiset.Entry) { - Entry<?> entry = (Entry<?>) object; - Object element = entry.getElement(); - int entryCount = entry.getCount(); - if (entryCount != 0) { - // Safe as long as we never add a new entry, which we won't. - @SuppressWarnings("unchecked") - Multiset<Object> multiset = (Multiset) multiset(); - return multiset.setCount(element, entryCount, 0); - } - } - return false; + @Override public boolean remove(Object o) { + return contains(o) + && multiset().elementSet().remove(((Entry<?>) o).getElement()); } @Override public void clear() { @@ -1035,7 +916,8 @@ public final class Multisets { @Override public void remove() { - Iterators.checkRemove(canRemove); + checkState( + canRemove, "no calls to next() since the last call to remove()"); if (totalCount == 1) { entryIterator.remove(); } else { |