io.prestosql.hadoop.$internal.com.google.common.collect.TreeMultiset Maven / Gradle / Ivy
Show all versions of hadoop-apache Show documentation
/*
* 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 io.prestosql.hadoop.$internal.com.google.common.collect;
import static io.prestosql.hadoop.$internal.com.google.common.base.Preconditions.checkArgument;
import static io.prestosql.hadoop.$internal.com.google.common.base.Preconditions.checkNotNull;
import static io.prestosql.hadoop.$internal.com.google.common.base.Preconditions.checkState;
import static io.prestosql.hadoop.$internal.com.google.common.collect.BstSide.LEFT;
import static io.prestosql.hadoop.$internal.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 io.prestosql.hadoop.$internal.com.google.common.annotations.GwtCompatible;
import io.prestosql.hadoop.$internal.com.google.common.annotations.GwtIncompatible;
import io.prestosql.hadoop.$internal.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 super E> 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 extends E> 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 super E> 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 super E> 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