All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.AbstractMapBasedMultiset Maven / Gradle / Ivy

Go to download

Guava is a suite of core and expanded libraries that include utility classes, google's collections, io classes, and much much more.

There is a newer version: 33.1.0-jre
Show newest version
/*
 * 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 com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * Basic implementation of {@code Multiset} backed by an instance of {@code
 * AbstractObjectCountMap}.
 *
 * 

For serialization to work, the subclass must specify explicit {@code readObject} and {@code * writeObject} methods. * * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) abstract class AbstractMapBasedMultiset extends AbstractMultiset implements Serializable { transient AbstractObjectCountMap backingMap; /* * 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(AbstractObjectCountMap backingMap) { this.backingMap = checkNotNull(backingMap); this.size = super.size(); } /** Used during deserialization only. The backing map must be empty. */ void setBackingMap(AbstractObjectCountMap backingMap) { this.backingMap = backingMap; } // Required Implementations @Override Set createElementSet() { return backingMap.keySet(); } /** * {@inheritDoc} * *

Invoking {@link 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> createEntrySet() { return new EntrySet(); } @Override Iterator> entryIterator() { final Iterator> backingEntries = backingMap.entrySet().iterator(); return new Iterator>() { Entry toRemove; boolean canRemove; @Override public boolean hasNext() { return backingEntries.hasNext(); } @Override public Multiset.Entry next() { final Entry mapEntry = backingEntries.next(); toRemove = mapEntry; canRemove = true; return mapEntry; } @Override public void remove() { checkRemove(canRemove); size -= toRemove.getCount(); backingEntries.remove(); canRemove = false; toRemove = null; } }; } @Override public void clear() { backingMap.clear(); size = 0L; } @Override int distinctElements() { return backingMap.size(); } // Optimizations - Query Operations @Override public int size() { return Ints.saturatedCast(size); } @Override public Iterator iterator() { return new MapBasedMultisetIterator(); } /* * Not subclassing AbstractMultiset$MultisetIterator because next() needs to * retrieve the Map.Entry entry, which can then be used for * a more efficient remove() call. */ private class MapBasedMultisetIterator implements Iterator { final Iterator> entryIterator; Entry currentEntry; int occurrencesLeft = 0; boolean canRemove = false; MapBasedMultisetIterator() { this.entryIterator = backingMap.entrySet().iterator(); } @Override public boolean hasNext() { return occurrencesLeft > 0 || entryIterator.hasNext(); } @Override public E next() { if (occurrencesLeft == 0) { currentEntry = entryIterator.next(); occurrencesLeft = currentEntry.getCount(); } occurrencesLeft--; canRemove = true; return currentEntry.getElement(); } @Override public void remove() { checkRemove(canRemove); int frequency = currentEntry.getCount(); if (frequency <= 0) { throw new ConcurrentModificationException(); } if (frequency == 1) { entryIterator.remove(); } else { ((ObjectCountHashMap.MapEntry) currentEntry).setCount(frequency - 1); } size--; canRemove = false; } } @Override public int count(@NullableDecl Object element) { return backingMap.get(element); } // Optional Operations - Modification Operations /** * {@inheritDoc} * * @throws IllegalArgumentException if the call would result in more than {@link * Integer#MAX_VALUE} occurrences of {@code element} in this multiset. */ @CanIgnoreReturnValue @Override public int add(@NullableDecl E element, int occurrences) { if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); int oldCount = backingMap.get(element); long newCount = (long) oldCount + (long) occurrences; checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); backingMap.put(element, (int) newCount); size += occurrences; return oldCount; } @CanIgnoreReturnValue @Override public int remove(@NullableDecl Object element, int occurrences) { if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); int oldCount = backingMap.get(element); int numberRemoved; if (oldCount > occurrences) { numberRemoved = occurrences; backingMap.put((E) element, oldCount - occurrences); } else { numberRemoved = oldCount; backingMap.remove(element); } size -= numberRemoved; return oldCount; } // Roughly a 33% performance improvement over AbstractMultiset.setCount(). @CanIgnoreReturnValue @Override public int setCount(@NullableDecl E element, int count) { checkNonnegative(count, "count"); int oldCount = (count == 0) ? backingMap.remove(element) : backingMap.put(element, count); size += (count - oldCount); return oldCount; } // Don't allow default serialization. @GwtIncompatible // java.io.ObjectStreamException private void readObjectNoData() throws ObjectStreamException { throw new InvalidObjectException("Stream data required"); } @GwtIncompatible // not needed in emulated source. private static final long serialVersionUID = -2250766705698539974L; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy