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

shaded.com.google.common.collect.TreeMultiset Maven / Gradle / Ivy

There is a newer version: 4.1.4
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 shaded.shaded.com.google.common.collect;

import static shaded.shaded.com.google.common.base.Preconditions.checkArgument;
import static shaded.shaded.com.google.common.base.Preconditions.checkNotNull;
import static shaded.shaded.com.google.common.base.Preconditions.checkState;
import static shaded.shaded.com.google.common.collect.BstSide.LEFT;
import static shaded.shaded.com.google.common.collect.BstSide.RIGHT;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;

import javax.annotation.Nullable;

import shaded.shaded.com.google.common.annotations.GwtCompatible;
import shaded.shaded.com.google.common.annotations.GwtIncompatible;
import shaded.shaded.com.google.common.primitives.Ints;

/**
 * A multiset which maintains the ordering of its elements, according to either
 * their natural order or an explicit {@link Comparator}. In all cases, this
 * implementation uses {@link Comparable#compareTo} or {@link
 * Comparator#compare} instead of {@link Object#equals} to determine
 * equivalence of instances.
 *
 * 

Warning: The comparison must be consistent with equals as * explained by the {@link Comparable} class specification. Otherwise, the * resulting multiset will violate the {@link java.util.Collection} contract, * which is specified in terms of {@link Object#equals}. * * @author Louis Wasserman * @author Jared Levy * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(emulated = true) public final class TreeMultiset extends AbstractSortedMultiset implements Serializable { /** * Creates a new, empty multiset, sorted according to the elements' natural * order. All elements inserted into the multiset must implement the * {@code Comparable} interface. Furthermore, all such elements must be * mutually comparable: {@code e1.compareTo(e2)} must not throw a * {@code ClassCastException} for any elements {@code e1} and {@code e2} in * the multiset. If the user attempts to add an element to the multiset that * violates this constraint (for example, the user attempts to add a string * element to a set whose elements are integers), the {@code add(Object)} * call will throw a {@code ClassCastException}. * *

The type specification is {@code }, instead of the * more specific {@code >}, to support * classes defined without generics. */ public static TreeMultiset create() { return new TreeMultiset(Ordering.natural()); } /** * Creates a new, empty multiset, sorted according to the specified * comparator. All elements inserted into the multiset must be mutually * comparable by the specified comparator: {@code comparator.compare(e1, * e2)} must not throw a {@code ClassCastException} for any elements {@code * e1} and {@code e2} in the multiset. If the user attempts to add an element * to the multiset that violates this constraint, the {@code add(Object)} call * will throw a {@code ClassCastException}. * * @param comparator the comparator that will be used to sort this multiset. A * null value indicates that the elements' natural ordering should * be used. */ @SuppressWarnings("unchecked") public static TreeMultiset create( @Nullable Comparator comparator) { return (comparator == null) ? new TreeMultiset((Comparator) Ordering.natural()) : new TreeMultiset(comparator); } /** * Creates an empty multiset containing the given initial elements, sorted * according to the elements' natural order. * *

This implementation is highly efficient when {@code elements} is itself * a {@link Multiset}. * *

The type specification is {@code }, instead of the * more specific {@code >}, to support * classes defined without generics. */ public static TreeMultiset create( Iterable elements) { TreeMultiset multiset = create(); Iterables.addAll(multiset, elements); return multiset; } /** * Returns an iterator over the elements contained in this collection. */ @Override public Iterator iterator() { // Needed to avoid Javadoc bug. return super.iterator(); } private TreeMultiset(Comparator comparator) { super(comparator); this.range = GeneralRange.all(comparator); this.rootReference = new Reference>(); } private TreeMultiset(GeneralRange range, Reference> root) { super(range.comparator()); this.range = range; this.rootReference = root; } @SuppressWarnings("unchecked") E checkElement(Object o) { return (E) o; } private transient final GeneralRange range; private transient final Reference> rootReference; static final class Reference { T value; public Reference() {} public T get() { return value; } public boolean compareAndSet(T expected, T newValue) { if (value == expected) { value = newValue; return true; } return false; } } @Override int distinctElements() { Node root = rootReference.get(); return Ints.checkedCast(BstRangeOps.totalInRange(distinctAggregate(), range, root)); } @Override public int size() { Node root = rootReference.get(); return Ints.saturatedCast(BstRangeOps.totalInRange(sizeAggregate(), range, root)); } @Override public int count(@Nullable Object element) { try { E e = checkElement(element); if (range.contains(e)) { Node node = BstOperations.seek(comparator(), rootReference.get(), e); return countOrZero(node); } return 0; } catch (ClassCastException e) { return 0; } catch (NullPointerException e) { return 0; } } private int mutate(@Nullable E e, MultisetModifier modifier) { BstMutationRule> mutationRule = BstMutationRule.createRule( modifier, BstCountBasedBalancePolicies. >singleRebalancePolicy(distinctAggregate()), nodeFactory()); BstMutationResult> mutationResult = BstOperations.mutate(comparator(), mutationRule, rootReference.get(), e); if (!rootReference.compareAndSet( mutationResult.getOriginalRoot(), mutationResult.getChangedRoot())) { throw new ConcurrentModificationException(); } Node original = mutationResult.getOriginalTarget(); return countOrZero(original); } @Override public int add(E element, int occurrences) { checkElement(element); if (occurrences == 0) { return count(element); } checkArgument(range.contains(element)); return mutate(element, new AddModifier(occurrences)); } @Override public int remove(@Nullable Object element, int occurrences) { if (element == null) { return 0; } else if (occurrences == 0) { return count(element); } try { E e = checkElement(element); return range.contains(e) ? mutate(e, new RemoveModifier(occurrences)) : 0; } catch (ClassCastException e) { return 0; } } @Override public boolean setCount(E element, int oldCount, int newCount) { checkElement(element); checkArgument(range.contains(element)); return mutate(element, new ConditionalSetCountModifier(oldCount, newCount)) == oldCount; } @Override public int setCount(E element, int count) { checkElement(element); checkArgument(range.contains(element)); return mutate(element, new SetCountModifier(count)); } private BstPathFactory, BstInOrderPath>> pathFactory() { return BstInOrderPath.inOrderFactory(); } @Override Iterator> entryIterator() { Node root = rootReference.get(); final BstInOrderPath> startingPath = BstRangeOps.furthestPath(range, LEFT, pathFactory(), root); return iteratorInDirection(startingPath, RIGHT); } @Override Iterator> descendingEntryIterator() { Node root = rootReference.get(); final BstInOrderPath> startingPath = BstRangeOps.furthestPath(range, RIGHT, pathFactory(), root); return iteratorInDirection(startingPath, LEFT); } private Iterator> iteratorInDirection( @Nullable BstInOrderPath> start, final BstSide direction) { final Iterator>> pathIterator = new AbstractLinkedIterator>>(start) { @Override protected BstInOrderPath> computeNext(BstInOrderPath> previous) { if (!previous.hasNext(direction)) { return null; } BstInOrderPath> next = previous.next(direction); // TODO(user): only check against one side return range.contains(next.getTip().getKey()) ? next : null; } }; return new Iterator>() { E toRemove = null; @Override public boolean hasNext() { return pathIterator.hasNext(); } @Override public Entry next() { BstInOrderPath> path = pathIterator.next(); return new LiveEntry( toRemove = path.getTip().getKey(), path.getTip().elemCount()); } @Override public void remove() { checkState(toRemove != null); setCount(toRemove, 0); toRemove = null; } }; } class LiveEntry extends Multisets.AbstractEntry { private Node expectedRoot; private final E element; private int count; private LiveEntry(E element, int count) { this.expectedRoot = rootReference.get(); this.element = element; this.count = count; } @Override public E getElement() { return element; } @Override public int getCount() { if (rootReference.get() == expectedRoot) { return count; } else { // check for updates expectedRoot = rootReference.get(); return count = TreeMultiset.this.count(element); } } } @Override public void clear() { Node root = rootReference.get(); Node cleared = BstRangeOps.minusRange(range, BstCountBasedBalancePolicies.>fullRebalancePolicy(distinctAggregate()), nodeFactory(), root); if (!rootReference.compareAndSet(root, cleared)) { throw new ConcurrentModificationException(); } } @Override public SortedMultiset headMultiset(E upperBound, BoundType boundType) { checkNotNull(upperBound); return new TreeMultiset( range.intersect(GeneralRange.upTo(comparator, upperBound, boundType)), rootReference); } @Override public SortedMultiset tailMultiset(E lowerBound, BoundType boundType) { checkNotNull(lowerBound); return new TreeMultiset( range.intersect(GeneralRange.downTo(comparator, lowerBound, boundType)), rootReference); } /** * {@inheritDoc} * * @since 11.0 */ @Override public Comparator comparator() { return super.comparator(); } private static final class Node extends BstNode> implements Serializable { private final long size; private final int distinct; private Node(E key, int elemCount, @Nullable Node left, @Nullable Node right) { super(key, left, right); checkArgument(elemCount > 0); this.size = (long) elemCount + sizeOrZero(left) + sizeOrZero(right); this.distinct = 1 + distinctOrZero(left) + distinctOrZero(right); } int elemCount() { long result = size - sizeOrZero(childOrNull(LEFT)) - sizeOrZero(childOrNull(RIGHT)); return Ints.checkedCast(result); } private Node(E key, int elemCount) { this(key, elemCount, null, null); } private static final long serialVersionUID = 0; } private static long sizeOrZero(@Nullable Node node) { return (node == null) ? 0 : node.size; } private static int distinctOrZero(@Nullable Node node) { return (node == null) ? 0 : node.distinct; } private static int countOrZero(@Nullable Node entry) { return (entry == null) ? 0 : entry.elemCount(); } @SuppressWarnings("unchecked") private BstAggregate> distinctAggregate() { return (BstAggregate) DISTINCT_AGGREGATE; } private static final BstAggregate> DISTINCT_AGGREGATE = new BstAggregate>() { @Override public int entryValue(Node entry) { return 1; } @Override public long treeValue(@Nullable Node tree) { return distinctOrZero(tree); } }; @SuppressWarnings("unchecked") private BstAggregate> sizeAggregate() { return (BstAggregate) SIZE_AGGREGATE; } private static final BstAggregate> SIZE_AGGREGATE = new BstAggregate>() { @Override public int entryValue(Node entry) { return entry.elemCount(); } @Override public long treeValue(@Nullable Node tree) { return sizeOrZero(tree); } }; @SuppressWarnings("unchecked") private BstNodeFactory> nodeFactory() { return (BstNodeFactory) NODE_FACTORY; } private static final BstNodeFactory> NODE_FACTORY = new BstNodeFactory>() { @Override public Node createNode(Node source, @Nullable Node left, @Nullable Node right) { return new Node(source.getKey(), source.elemCount(), left, right); } }; private abstract class MultisetModifier implements BstModifier> { abstract int newCount(int oldCount); @Nullable @Override public BstModificationResult> modify(E key, @Nullable Node originalEntry) { int oldCount = countOrZero(originalEntry); int newCount = newCount(oldCount); if (oldCount == newCount) { return BstModificationResult.identity(originalEntry); } else if (newCount == 0) { return BstModificationResult.rebalancingChange(originalEntry, null); } else if (oldCount == 0) { return BstModificationResult.rebalancingChange(null, new Node(key, newCount)); } else { return BstModificationResult.rebuildingChange(originalEntry, new Node(originalEntry.getKey(), newCount)); } } } private final class AddModifier extends MultisetModifier { private final int countToAdd; private AddModifier(int countToAdd) { checkArgument(countToAdd > 0); this.countToAdd = countToAdd; } @Override int newCount(int oldCount) { checkArgument(countToAdd <= Integer.MAX_VALUE - oldCount, "Cannot add this many elements"); return oldCount + countToAdd; } } private final class RemoveModifier extends MultisetModifier { private final int countToRemove; private RemoveModifier(int countToRemove) { checkArgument(countToRemove > 0); this.countToRemove = countToRemove; } @Override int newCount(int oldCount) { return Math.max(0, oldCount - countToRemove); } } private final class SetCountModifier extends MultisetModifier { private final int countToSet; private SetCountModifier(int countToSet) { checkArgument(countToSet >= 0); this.countToSet = countToSet; } @Override int newCount(int oldCount) { return countToSet; } } private final class ConditionalSetCountModifier extends MultisetModifier { private final int expectedCount; private final int setCount; private ConditionalSetCountModifier(int expectedCount, int setCount) { checkArgument(setCount >= 0 & expectedCount >= 0); this.expectedCount = expectedCount; this.setCount = setCount; } @Override int newCount(int oldCount) { return (oldCount == expectedCount) ? setCount : oldCount; } } /* * TODO(jlevy): Decide whether entrySet() should return entries with an * equals() method that calls the comparator to compare the two keys. If that * change is made, AbstractMultiset.equals() can simply check whether two * multisets have equal entry sets. */ /** * @serialData the comparator, the number of distinct elements, the first * element, its count, the second element, its count, and so on */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(elementSet().comparator()); Serialization.writeMultiset(this, stream); } @GwtIncompatible("java.io.ObjectInputStream") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); @SuppressWarnings("unchecked") // reading data stored by writeObject Comparator comparator = (Comparator) stream.readObject(); Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); Serialization.getFieldSetter(TreeMultiset.class, "range").set(this, GeneralRange.all(comparator)); Serialization.getFieldSetter(TreeMultiset.class, "rootReference").set(this, new Reference>()); Serialization.populateMultiset(this, stream); } @GwtIncompatible("not needed in emulated source") private static final long serialVersionUID = 1; }