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

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 35.0.0.Beta1
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.Map;
import java.util.Set;
import java.util.function.ObjIntConsumer;
import org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

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

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 { // TODO(lowasser): consider overhauling this back to Map private transient Map 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(Map backingMap) { checkArgument(backingMap.isEmpty()); this.backingMap = backingMap; } /** Used during deserialization only. The backing map must be empty. */ void setBackingMap(Map backingMap) { this.backingMap = backingMap; } // Required Implementations /** * {@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> entrySet() { return super.entrySet(); } @Override Iterator elementIterator() { final Iterator> backingEntries = backingMap.entrySet().iterator(); return new Iterator() { @NullableDecl Map.Entry toRemove; @Override public boolean hasNext() { return backingEntries.hasNext(); } @Override public E next() { final Map.Entry mapEntry = backingEntries.next(); toRemove = mapEntry; return mapEntry.getKey(); } @Override public void remove() { checkRemove(toRemove != null); size -= toRemove.getValue().getAndSet(0); backingEntries.remove(); toRemove = null; } }; } @Override Iterator> entryIterator() { final Iterator> backingEntries = backingMap.entrySet().iterator(); return new Iterator>() { @NullableDecl Map.Entry toRemove; @Override public boolean hasNext() { return backingEntries.hasNext(); } @Override public Multiset.Entry next() { final Map.Entry mapEntry = backingEntries.next(); toRemove = mapEntry; return new Multisets.AbstractEntry() { @Override public E getElement() { return mapEntry.getKey(); } @Override public int getCount() { Count count = mapEntry.getValue(); if (count == null || count.get() == 0) { Count frequency = backingMap.get(getElement()); if (frequency != null) { return frequency.get(); } } return (count == null) ? 0 : count.get(); } }; } @Override public void remove() { checkRemove(toRemove != null); size -= toRemove.getValue().getAndSet(0); backingEntries.remove(); toRemove = null; } }; } @Override public void forEachEntry(ObjIntConsumer action) { checkNotNull(action); backingMap.forEach((element, count) -> action.accept(element, count.get())); } @Override public void clear() { for (Count frequency : backingMap.values()) { frequency.set(0); } 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; @MonotonicNonNullDecl Map.Entry currentEntry; int occurrencesLeft; boolean canRemove; 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.getValue().get(); } occurrencesLeft--; canRemove = true; return currentEntry.getKey(); } @Override public void remove() { checkRemove(canRemove); int frequency = currentEntry.getValue().get(); if (frequency <= 0) { throw new ConcurrentModificationException(); } if (currentEntry.getValue().addAndGet(-1) == 0) { entryIterator.remove(); } size--; canRemove = false; } } @Override public int count(@NullableDecl Object element) { Count frequency = Maps.safeGet(backingMap, element); return (frequency == null) ? 0 : frequency.get(); } // 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); Count frequency = backingMap.get(element); int oldCount; if (frequency == null) { oldCount = 0; backingMap.put(element, new Count(occurrences)); } else { oldCount = frequency.get(); long newCount = (long) oldCount + (long) occurrences; checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); frequency.add(occurrences); } 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); Count frequency = backingMap.get(element); if (frequency == null) { return 0; } int oldCount = frequency.get(); int numberRemoved; if (oldCount > occurrences) { numberRemoved = occurrences; } else { numberRemoved = oldCount; backingMap.remove(element); } frequency.add(-numberRemoved); 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"); Count existingCounter; int oldCount; if (count == 0) { existingCounter = backingMap.remove(element); oldCount = getAndSet(existingCounter, count); } else { existingCounter = backingMap.get(element); oldCount = getAndSet(existingCounter, count); if (existingCounter == null) { backingMap.put(element, new Count(count)); } } size += (count - oldCount); return oldCount; } private static int getAndSet(@NullableDecl Count i, int count) { if (i == null) { return 0; } return i.getAndSet(count); } // 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 - 2025 Weber Informatics LLC | Privacy Policy