Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2007 The Guava Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.google.common.collect;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.CollectPreconditions.checkNonnegative;
 import static com.google.common.collect.CollectPreconditions.checkRemove;
 
 
 import java.util.Map;
 import java.util.Set;
 
 import  javax.annotation.Nullable;

Basic implementation of Multiset<E> backed by an instance of Map<E, Count>.

For serialization to work, the subclass must specify explicit readObject and writeObject methods.

Author(s):
Kevin Bourrillion
 
 @GwtCompatible(emulated = true)
 abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
     implements Serializable {
 
   private transient Map<E, CountbackingMap;
 
   /*
    * Cache the size for efficiency. Using a long lets us avoid the need for
    * overflow checking and ensures that size() will function correctly even if
    * the multiset had once been larger than Integer.MAX_VALUE.
    */
   private transient long size;

  
Standard constructor.
 
   protected AbstractMapBasedMultiset(Map<E, CountbackingMap) {
     this. = checkNotNull(backingMap);
     this. = super.size();
   }

  
Used during deserialization only. The backing map must be empty.
 
   void setBackingMap(Map<E, CountbackingMap) {
     this. = backingMap;
   }
 
   // Required Implementations
 
  

Invoking Multiset.Entry.getCount on an entry in the returned set always returns the current count of that element in the multiset, as opposed to the count at the time the entry was retrieved.

 
   @Override
   public Set<Multiset.Entry<E>> entrySet() {
     return super.entrySet();
   }
 
   @Override
     final Iterator<Map.Entry<E, Count>> backingEntries =
         .entrySet().iterator();
     return new Iterator<Multiset.Entry<E>>() {
       Map.Entry<E, CounttoRemove;
 
       @Override
       public boolean hasNext() {
         return backingEntries.hasNext();
       }
 
       @Override
       public Multiset.Entry<E> next() {
         final Map.Entry<E, CountmapEntry = backingEntries.next();
         = mapEntry;
        return new Multisets.AbstractEntry<E>() {
          @Override
          public E getElement() {
            return mapEntry.getKey();
          }
          @Override
          public int getCount() {
            Count count = mapEntry.getValue();
            if (count == null || count.get() == 0) {
              Count frequency = .get(getElement());
              if (frequency != null) {
                return frequency.get();
              }
            }
            return (count == null) ? 0 : count.get();
          }
        };
      }
      @Override
      public void remove() {
        checkRemove( != null);
         -= .getValue().getAndSet(0);
        backingEntries.remove();
         = null;
      }
    };
  }
  public void clear() {
    for (Count frequency : .values()) {
      frequency.set(0);
    }
    .clear();
     = 0L;
  }
  int distinctElements() {
    return .size();
  }
  // Optimizations - Query Operations
  @Override public int size() {
    return Ints.saturatedCast();
  }
  @Override public Iterator<E> iterator() {
    return new MapBasedMultisetIterator();
  }
  /*
   * Not subclassing AbstractMultiset$MultisetIterator because next() needs to
   * retrieve the Map.Entry<E, Count> entry, which can then be used for
   * a more efficient remove() call.
   */
  private class MapBasedMultisetIterator implements Iterator<E> {
    final Iterator<Map.Entry<E, Count>> entryIterator;
    int occurrencesLeft;
    boolean canRemove;
      this. = .entrySet().iterator();
    }
    @Override
    public boolean hasNext() {
      return  > 0 || .hasNext();
    }
    @Override
    public E next() {
      if ( == 0) {
         = .next();
         = .getValue().get();
      }
      --;
       = true;
      return .getKey();
    }
    @Override
    public void remove() {
      int frequency = .getValue().get();
      if (frequency <= 0) {
        throw new ConcurrentModificationException();
      }
      if (.getValue().addAndGet(-1) == 0) {
        .remove();
      }
      --;
       = false;
    }
  }
  @Override public int count(@Nullable Object element) {
    Count frequency = Maps.safeGet(element);
    return (frequency == null) ? 0 : frequency.get();
  }
  // Optional Operations - Modification Operations

  

Throws:
IllegalArgumentException if the call would result in more than Integer.MAX_VALUE occurrences of element in this multiset.
  @Override public int add(@Nullable E elementint occurrences) {
    if (occurrences == 0) {
      return count(element);
    }
        occurrences > 0, "occurrences cannot be negative: %s"occurrences);
    Count frequency = .get(element);
    int oldCount;
    if (frequency == null) {
      oldCount = 0;
      .put(elementnew Count(occurrences));
    } else {
      oldCount = frequency.get();
      long newCount = (longoldCount + (longoccurrences;
      checkArgument(newCount <= .,
          "too many occurrences: %s"newCount);
      frequency.getAndAdd(occurrences);
    }
     += occurrences;
    return oldCount;
  }
  @Override public int remove(@Nullable Object elementint occurrences) {
    if (occurrences == 0) {
      return count(element);
    }
        occurrences > 0, "occurrences cannot be negative: %s"occurrences);
    Count frequency = .get(element);
    if (frequency == null) {
      return 0;
    }
    int oldCount = frequency.get();
    int numberRemoved;
    if (oldCount > occurrences) {
      numberRemoved = occurrences;
    } else {
      numberRemoved = oldCount;
      .remove(element);
    }
    frequency.addAndGet(-numberRemoved);
     -= numberRemoved;
    return oldCount;
  }
  // Roughly a 33% performance improvement over AbstractMultiset.setCount().
  @Override public int setCount(@Nullable E elementint count) {
    checkNonnegative(count"count");
    Count existingCounter;
    int oldCount;
    if (count == 0) {
      existingCounter = .remove(element);
      oldCount = getAndSet(existingCountercount);
    } else {
      existingCounter = .get(element);
      oldCount = getAndSet(existingCountercount);
      if (existingCounter == null) {
        .put(elementnew Count(count));
      }
    }
     += (count - oldCount);
    return oldCount;
  }
  private static int getAndSet(Count iint count) {
    if (i == null) {
      return 0;
    }
    return i.getAndSet(count);
  }
  // Don't allow default serialization.
  @GwtIncompatible("java.io.ObjectStreamException")
  @SuppressWarnings("unused"// actually used during deserialization
  private void readObjectNoData() throws ObjectStreamException {
    throw new InvalidObjectException("Stream data required");
  }
  @GwtIncompatible("not needed in emulated source.")
  private static final long serialVersionUID = -2250766705698539974L;
New to GrepCode? Check out our FAQ X