aboutsummaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/collect/AbstractMapBasedMultiset.java')
-rw-r--r--guava/src/com/google/common/collect/AbstractMapBasedMultiset.java116
1 files changed, 110 insertions, 6 deletions
diff --git a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java
index 6f14380..362386c 100644
--- a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java
+++ b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java
@@ -28,6 +28,7 @@ import com.google.common.primitives.Ints;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.io.Serializable;
+import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
@@ -37,7 +38,7 @@ import javax.annotation.Nullable;
/**
* Basic implementation of {@code Multiset<E>} backed by an instance of {@code
- * Map<E, Count>}.
+ * Map<E, AtomicInteger>}.
*
* <p>For serialization to work, the subclass must specify explicit {@code
* readObject} and {@code writeObject} methods.
@@ -63,6 +64,10 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
this.size = super.size();
}
+ Map<E, Count> backingMap() {
+ return backingMap;
+ }
+
/** Used during deserialization only. The backing map must be empty. */
void setBackingMap(Map<E, Count> backingMap) {
this.backingMap = backingMap;
@@ -119,7 +124,8 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
@Override
public void remove() {
- Iterators.checkRemove(toRemove != null);
+ checkState(toRemove != null,
+ "no calls to next() since the last call to remove()");
size -= toRemove.getValue().getAndSet(0);
backingEntries.remove();
toRemove = null;
@@ -153,7 +159,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
/*
* Not subclassing AbstractMultiset$MultisetIterator because next() needs to
- * retrieve the Map.Entry<E, Count> entry, which can then be used for
+ * retrieve the Map.Entry<E, AtomicInteger> entry, which can then be used for
* a more efficient remove() call.
*/
private class MapBasedMultisetIterator implements Iterator<E> {
@@ -199,8 +205,14 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
}
@Override public int count(@Nullable Object element) {
- Count frequency = Maps.safeGet(backingMap, element);
- return (frequency == null) ? 0 : frequency.get();
+ try {
+ Count frequency = backingMap.get(element);
+ return (frequency == null) ? 0 : frequency.get();
+ } catch (NullPointerException e) {
+ return 0;
+ } catch (ClassCastException e) {
+ return 0;
+ }
}
// Optional Operations - Modification Operations
@@ -261,7 +273,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
}
// Roughly a 33% performance improvement over AbstractMultiset.setCount().
- @Override public int setCount(@Nullable E element, int count) {
+ @Override public int setCount(E element, int count) {
checkNonnegative(count, "count");
Count existingCounter;
@@ -290,6 +302,98 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
return i.getAndSet(count);
}
+ private int removeAllOccurrences(@Nullable Object element,
+ Map<E, Count> map) {
+ Count frequency = map.remove(element);
+ if (frequency == null) {
+ return 0;
+ }
+ int numberRemoved = frequency.getAndSet(0);
+ size -= numberRemoved;
+ return numberRemoved;
+ }
+
+ // Views
+
+ @Override Set<E> createElementSet() {
+ return new MapBasedElementSet(backingMap);
+ }
+
+ // TODO(user): once TreeMultiset is replaced with a SortedMultiset
+ // implementation, replace this with a subclass of Multisets.ElementSet.
+ class MapBasedElementSet extends ForwardingSet<E> {
+
+ // This mapping is the usually the same as 'backingMap', but can be a
+ // submap in some implementations.
+ private final Map<E, Count> map;
+ private final Set<E> delegate;
+
+ MapBasedElementSet(Map<E, Count> map) {
+ this.map = map;
+ delegate = map.keySet();
+ }
+
+ @Override protected Set<E> delegate() {
+ return delegate;
+ }
+
+ @Override public Iterator<E> iterator() {
+ final Iterator<Map.Entry<E, Count>> entries
+ = map.entrySet().iterator();
+ return new Iterator<E>() {
+ Map.Entry<E, Count> toRemove;
+
+ @Override
+ public boolean hasNext() {
+ return entries.hasNext();
+ }
+
+ @Override
+ public E next() {
+ toRemove = entries.next();
+ return toRemove.getKey();
+ }
+
+ @Override
+ public void remove() {
+ checkState(toRemove != null,
+ "no calls to next() since the last call to remove()");
+ size -= toRemove.getValue().getAndSet(0);
+ entries.remove();
+ toRemove = null;
+ }
+ };
+ }
+
+ @Override public boolean remove(Object element) {
+ return removeAllOccurrences(element, map) != 0;
+ }
+
+ @Override public boolean removeAll(Collection<?> elementsToRemove) {
+ return Iterators.removeAll(iterator(), elementsToRemove);
+ }
+
+ @Override public boolean retainAll(Collection<?> elementsToRetain) {
+ return Iterators.retainAll(iterator(), elementsToRetain);
+ }
+
+ @Override public void clear() {
+ if (map == backingMap) {
+ AbstractMapBasedMultiset.this.clear();
+ } else {
+ Iterator<E> i = iterator();
+ while (i.hasNext()) {
+ i.next();
+ i.remove();
+ }
+ }
+ }
+
+ public Map<E, Count> getMap() {
+ return map;
+ }
+ }
+
// Don't allow default serialization.
@GwtIncompatible("java.io.ObjectStreamException")
@SuppressWarnings("unused") // actually used during deserialization