/*
* 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.base.Preconditions.checkState;
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.base.MoreObjects;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
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 java.util.NoSuchElementException;
import java.util.function.ObjIntConsumer;
import org.checkerframework.checker.nullness.qual.Nullable;
A multiset which maintains the ordering of its elements, according to either their natural order or an explicit Comparator
. In all cases, this implementation uses Comparable.compareTo
or Comparator.compare
instead of Object.equals
to determine equivalence of instances. Warning: The comparison must be consistent with equals as explained by the Comparable
class specification. Otherwise, the resulting multiset will violate the Collection
contract, which is specified in terms of Object.equals
.
See the Guava User Guide article on
Multiset
.
Author: Louis Wasserman, Jared Levy Since: 2.0
/**
* 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.
*
* <p><b>Warning:</b> The comparison must be <i>consistent with equals</i> 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}.
*
* <p>See the Guava User Guide article on <a href=
* "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset"> {@code
* Multiset}</a>.
*
* @author Louis Wasserman
* @author Jared Levy
* @since 2.0
*/
@GwtCompatible(emulated = true)
public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements Serializable {
Creates a new, empty multiset, sorted according to the elements' natural order. All elements inserted into the multiset must implement the Comparable
interface. Furthermore, all such elements must be mutually comparable: e1.compareTo(e2)
must not throw a ClassCastException
for any elements e1
and 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
add(Object)
call will throw a ClassCastException
. The type specification is <E extends Comparable>
, instead of the more specific <E extends Comparable<? super E>>
, to support classes defined without generics.
/**
* 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 <i>mutually comparable</i>: {@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}.
*
* <p>The type specification is {@code <E extends Comparable>}, instead of the more specific
* {@code <E extends Comparable<? super E>>}, to support classes defined without generics.
*/
public static <E extends Comparable> TreeMultiset<E> create() {
return new TreeMultiset<E>(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: comparator.compare(e1, e2)
must not throw a ClassCastException
for any elements e1
and e2
in the multiset. If the user attempts to add an element to the multiset that violates this constraint, the add(Object)
call will throw a
ClassCastException
. Params: - comparator – the comparator that will be used to sort this multiset. A null value
indicates that the elements' natural ordering should be used.
/**
* Creates a new, empty multiset, sorted according to the specified comparator. All elements
* inserted into the multiset must be <i>mutually comparable</i> 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' <i>natural ordering</i> should be used.
*/
@SuppressWarnings("unchecked")
public static <E> TreeMultiset<E> create(@Nullable Comparator<? super E> comparator) {
return (comparator == null)
? new TreeMultiset<E>((Comparator) Ordering.natural())
: new TreeMultiset<E>(comparator);
}
Creates an empty multiset containing the given initial elements, sorted according to the
elements' natural order.
This implementation is highly efficient when elements
is itself a Multiset
.
The type specification is <E extends Comparable>
, instead of the more specific <E extends Comparable<? super E>>
, to support classes defined without generics.
/**
* Creates an empty multiset containing the given initial elements, sorted according to the
* elements' natural order.
*
* <p>This implementation is highly efficient when {@code elements} is itself a {@link Multiset}.
*
* <p>The type specification is {@code <E extends Comparable>}, instead of the more specific
* {@code <E extends Comparable<? super E>>}, to support classes defined without generics.
*/
public static <E extends Comparable> TreeMultiset<E> create(Iterable<? extends E> elements) {
TreeMultiset<E> multiset = create();
Iterables.addAll(multiset, elements);
return multiset;
}
private final transient Reference<AvlNode<E>> rootReference;
private final transient GeneralRange<E> range;
private final transient AvlNode<E> header;
TreeMultiset(Reference<AvlNode<E>> rootReference, GeneralRange<E> range, AvlNode<E> endLink) {
super(range.comparator());
this.rootReference = rootReference;
this.range = range;
this.header = endLink;
}
TreeMultiset(Comparator<? super E> comparator) {
super(comparator);
this.range = GeneralRange.all(comparator);
this.header = new AvlNode<E>(null, 1);
successor(header, header);
this.rootReference = new Reference<>();
}
A function which can be summed across a subtree. /** A function which can be summed across a subtree. */
private enum Aggregate {
SIZE {
@Override
int nodeAggregate(AvlNode<?> node) {
return node.elemCount;
}
@Override
long treeAggregate(@Nullable AvlNode<?> root) {
return (root == null) ? 0 : root.totalCount;
}
},
DISTINCT {
@Override
int nodeAggregate(AvlNode<?> node) {
return 1;
}
@Override
long treeAggregate(@Nullable AvlNode<?> root) {
return (root == null) ? 0 : root.distinctElements;
}
};
abstract int nodeAggregate(AvlNode<?> node);
abstract long treeAggregate(@Nullable AvlNode<?> root);
}
private long aggregateForEntries(Aggregate aggr) {
AvlNode<E> root = rootReference.get();
long total = aggr.treeAggregate(root);
if (range.hasLowerBound()) {
total -= aggregateBelowRange(aggr, root);
}
if (range.hasUpperBound()) {
total -= aggregateAboveRange(aggr, root);
}
return total;
}
private long aggregateBelowRange(Aggregate aggr, @Nullable AvlNode<E> node) {
if (node == null) {
return 0;
}
int cmp = comparator().compare(range.getLowerEndpoint(), node.elem);
if (cmp < 0) {
return aggregateBelowRange(aggr, node.left);
} else if (cmp == 0) {
switch (range.getLowerBoundType()) {
case OPEN:
return aggr.nodeAggregate(node) + aggr.treeAggregate(node.left);
case CLOSED:
return aggr.treeAggregate(node.left);
default:
throw new AssertionError();
}
} else {
return aggr.treeAggregate(node.left)
+ aggr.nodeAggregate(node)
+ aggregateBelowRange(aggr, node.right);
}
}
private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode<E> node) {
if (node == null) {
return 0;
}
int cmp = comparator().compare(range.getUpperEndpoint(), node.elem);
if (cmp > 0) {
return aggregateAboveRange(aggr, node.right);
} else if (cmp == 0) {
switch (range.getUpperBoundType()) {
case OPEN:
return aggr.nodeAggregate(node) + aggr.treeAggregate(node.right);
case CLOSED:
return aggr.treeAggregate(node.right);
default:
throw new AssertionError();
}
} else {
return aggr.treeAggregate(node.right)
+ aggr.nodeAggregate(node)
+ aggregateAboveRange(aggr, node.left);
}
}
@Override
public int size() {
return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE));
}
@Override
int distinctElements() {
return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT));
}
static int distinctElements(@Nullable AvlNode<?> node) {
return (node == null) ? 0 : node.distinctElements;
}
@Override
public int count(@Nullable Object element) {
try {
@SuppressWarnings("unchecked")
E e = (E) element;
AvlNode<E> root = rootReference.get();
if (!range.contains(e) || root == null) {
return 0;
}
return root.count(comparator(), e);
} catch (ClassCastException | NullPointerException e) {
return 0;
}
}
@CanIgnoreReturnValue
@Override
public int add(@Nullable E element, int occurrences) {
checkNonnegative(occurrences, "occurrences");
if (occurrences == 0) {
return count(element);
}
checkArgument(range.contains(element));
AvlNode<E> root = rootReference.get();
if (root == null) {
comparator().compare(element, element);
AvlNode<E> newRoot = new AvlNode<E>(element, occurrences);
successor(header, newRoot, header);
rootReference.checkAndSet(root, newRoot);
return 0;
}
int[] result = new int[1]; // used as a mutable int reference to hold result
AvlNode<E> newRoot = root.add(comparator(), element, occurrences, result);
rootReference.checkAndSet(root, newRoot);
return result[0];
}
@CanIgnoreReturnValue
@Override
public int remove(@Nullable Object element, int occurrences) {
checkNonnegative(occurrences, "occurrences");
if (occurrences == 0) {
return count(element);
}
AvlNode<E> root = rootReference.get();
int[] result = new int[1]; // used as a mutable int reference to hold result
AvlNode<E> newRoot;
try {
@SuppressWarnings("unchecked")
E e = (E) element;
if (!range.contains(e) || root == null) {
return 0;
}
newRoot = root.remove(comparator(), e, occurrences, result);
} catch (ClassCastException | NullPointerException e) {
return 0;
}
rootReference.checkAndSet(root, newRoot);
return result[0];
}
@CanIgnoreReturnValue
@Override
public int setCount(@Nullable E element, int count) {
checkNonnegative(count, "count");
if (!range.contains(element)) {
checkArgument(count == 0);
return 0;
}
AvlNode<E> root = rootReference.get();
if (root == null) {
if (count > 0) {
add(element, count);
}
return 0;
}
int[] result = new int[1]; // used as a mutable int reference to hold result
AvlNode<E> newRoot = root.setCount(comparator(), element, count, result);
rootReference.checkAndSet(root, newRoot);
return result[0];
}
@CanIgnoreReturnValue
@Override
public boolean setCount(@Nullable E element, int oldCount, int newCount) {
checkNonnegative(newCount, "newCount");
checkNonnegative(oldCount, "oldCount");
checkArgument(range.contains(element));
AvlNode<E> root = rootReference.get();
if (root == null) {
if (oldCount == 0) {
if (newCount > 0) {
add(element, newCount);
}
return true;
} else {
return false;
}
}
int[] result = new int[1]; // used as a mutable int reference to hold result
AvlNode<E> newRoot = root.setCount(comparator(), element, oldCount, newCount, result);
rootReference.checkAndSet(root, newRoot);
return result[0] == oldCount;
}
@Override
public void clear() {
if (!range.hasLowerBound() && !range.hasUpperBound()) {
// We can do this in O(n) rather than removing one by one, which could force rebalancing.
for (AvlNode<E> current = header.succ; current != header; ) {
AvlNode<E> next = current.succ;
current.elemCount = 0;
// Also clear these fields so that one deleted Entry doesn't retain all elements.
current.left = null;
current.right = null;
current.pred = null;
current.succ = null;
current = next;
}
successor(header, header);
rootReference.clear();
} else {
// TODO(cpovirk): Perhaps we can optimize in this case, too?
Iterators.clear(entryIterator());
}
}
private Entry<E> wrapEntry(final AvlNode<E> baseEntry) {
return new Multisets.AbstractEntry<E>() {
@Override
public E getElement() {
return baseEntry.getElement();
}
@Override
public int getCount() {
int result = baseEntry.getCount();
if (result == 0) {
return count(getElement());
} else {
return result;
}
}
};
}
Returns the first node in the tree that is in range. /** Returns the first node in the tree that is in range. */
private @Nullable AvlNode<E> firstNode() {
AvlNode<E> root = rootReference.get();
if (root == null) {
return null;
}
AvlNode<E> node;
if (range.hasLowerBound()) {
E endpoint = range.getLowerEndpoint();
node = rootReference.get().ceiling(comparator(), endpoint);
if (node == null) {
return null;
}
if (range.getLowerBoundType() == BoundType.OPEN
&& comparator().compare(endpoint, node.getElement()) == 0) {
node = node.succ;
}
} else {
node = header.succ;
}
return (node == header || !range.contains(node.getElement())) ? null : node;
}
private @Nullable AvlNode<E> lastNode() {
AvlNode<E> root = rootReference.get();
if (root == null) {
return null;
}
AvlNode<E> node;
if (range.hasUpperBound()) {
E endpoint = range.getUpperEndpoint();
node = rootReference.get().floor(comparator(), endpoint);
if (node == null) {
return null;
}
if (range.getUpperBoundType() == BoundType.OPEN
&& comparator().compare(endpoint, node.getElement()) == 0) {
node = node.pred;
}
} else {
node = header.pred;
}
return (node == header || !range.contains(node.getElement())) ? null : node;
}
@Override
Iterator<E> elementIterator() {
return Multisets.elementIterator(entryIterator());
}
@Override
Iterator<Entry<E>> entryIterator() {
return new Iterator<Entry<E>>() {
AvlNode<E> current = firstNode();
@Nullable Entry<E> prevEntry;
@Override
public boolean hasNext() {
if (current == null) {
return false;
} else if (range.tooHigh(current.getElement())) {
current = null;
return false;
} else {
return true;
}
}
@Override
public Entry<E> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Entry<E> result = wrapEntry(current);
prevEntry = result;
if (current.succ == header) {
current = null;
} else {
current = current.succ;
}
return result;
}
@Override
public void remove() {
checkRemove(prevEntry != null);
setCount(prevEntry.getElement(), 0);
prevEntry = null;
}
};
}
@Override
Iterator<Entry<E>> descendingEntryIterator() {
return new Iterator<Entry<E>>() {
AvlNode<E> current = lastNode();
Entry<E> prevEntry = null;
@Override
public boolean hasNext() {
if (current == null) {
return false;
} else if (range.tooLow(current.getElement())) {
current = null;
return false;
} else {
return true;
}
}
@Override
public Entry<E> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Entry<E> result = wrapEntry(current);
prevEntry = result;
if (current.pred == header) {
current = null;
} else {
current = current.pred;
}
return result;
}
@Override
public void remove() {
checkRemove(prevEntry != null);
setCount(prevEntry.getElement(), 0);
prevEntry = null;
}
};
}
@Override
public void forEachEntry(ObjIntConsumer<? super E> action) {
checkNotNull(action);
for (AvlNode<E> node = firstNode();
node != header && node != null && !range.tooHigh(node.getElement());
node = node.succ) {
action.accept(node.getElement(), node.getCount());
}
}
@Override
public Iterator<E> iterator() {
return Multisets.iteratorImpl(this);
}
@Override
public SortedMultiset<E> headMultiset(@Nullable E upperBound, BoundType boundType) {
return new TreeMultiset<E>(
rootReference,
range.intersect(GeneralRange.upTo(comparator(), upperBound, boundType)),
header);
}
@Override
public SortedMultiset<E> tailMultiset(@Nullable E lowerBound, BoundType boundType) {
return new TreeMultiset<E>(
rootReference,
range.intersect(GeneralRange.downTo(comparator(), lowerBound, boundType)),
header);
}
private static final class Reference<T> {
private @Nullable T value;
public @Nullable T get() {
return value;
}
public void checkAndSet(@Nullable T expected, T newValue) {
if (value != expected) {
throw new ConcurrentModificationException();
}
value = newValue;
}
void clear() {
value = null;
}
}
private static final class AvlNode<E> {
private final @Nullable E elem;
// elemCount is 0 iff this node has been deleted.
private int elemCount;
private int distinctElements;
private long totalCount;
private int height;
private @Nullable AvlNode<E> left;
private @Nullable AvlNode<E> right;
private @Nullable AvlNode<E> pred;
private @Nullable AvlNode<E> succ;
AvlNode(@Nullable E elem, int elemCount) {
checkArgument(elemCount > 0);
this.elem = elem;
this.elemCount = elemCount;
this.totalCount = elemCount;
this.distinctElements = 1;
this.height = 1;
this.left = null;
this.right = null;
}
public int count(Comparator<? super E> comparator, E e) {
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
return (left == null) ? 0 : left.count(comparator, e);
} else if (cmp > 0) {
return (right == null) ? 0 : right.count(comparator, e);
} else {
return elemCount;
}
}
private AvlNode<E> addRightChild(E e, int count) {
right = new AvlNode<E>(e, count);
successor(this, right, succ);
height = Math.max(2, height);
distinctElements++;
totalCount += count;
return this;
}
private AvlNode<E> addLeftChild(E e, int count) {
left = new AvlNode<E>(e, count);
successor(pred, left, this);
height = Math.max(2, height);
distinctElements++;
totalCount += count;
return this;
}
AvlNode<E> add(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
/*
* It speeds things up considerably to unconditionally add count to totalCount here,
* but that destroys failure atomicity in the case of count overflow. =(
*/
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
AvlNode<E> initLeft = left;
if (initLeft == null) {
result[0] = 0;
return addLeftChild(e, count);
}
int initHeight = initLeft.height;
left = initLeft.add(comparator, e, count, result);
if (result[0] == 0) {
distinctElements++;
}
this.totalCount += count;
return (left.height == initHeight) ? this : rebalance();
} else if (cmp > 0) {
AvlNode<E> initRight = right;
if (initRight == null) {
result[0] = 0;
return addRightChild(e, count);
}
int initHeight = initRight.height;
right = initRight.add(comparator, e, count, result);
if (result[0] == 0) {
distinctElements++;
}
this.totalCount += count;
return (right.height == initHeight) ? this : rebalance();
}
// adding count to me! No rebalance possible.
result[0] = elemCount;
long resultCount = (long) elemCount + count;
checkArgument(resultCount <= Integer.MAX_VALUE);
this.elemCount += count;
this.totalCount += count;
return this;
}
AvlNode<E> remove(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
AvlNode<E> initLeft = left;
if (initLeft == null) {
result[0] = 0;
return this;
}
left = initLeft.remove(comparator, e, count, result);
if (result[0] > 0) {
if (count >= result[0]) {
this.distinctElements--;
this.totalCount -= result[0];
} else {
this.totalCount -= count;
}
}
return (result[0] == 0) ? this : rebalance();
} else if (cmp > 0) {
AvlNode<E> initRight = right;
if (initRight == null) {
result[0] = 0;
return this;
}
right = initRight.remove(comparator, e, count, result);
if (result[0] > 0) {
if (count >= result[0]) {
this.distinctElements--;
this.totalCount -= result[0];
} else {
this.totalCount -= count;
}
}
return rebalance();
}
// removing count from me!
result[0] = elemCount;
if (count >= elemCount) {
return deleteMe();
} else {
this.elemCount -= count;
this.totalCount -= count;
return this;
}
}
AvlNode<E> setCount(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
AvlNode<E> initLeft = left;
if (initLeft == null) {
result[0] = 0;
return (count > 0) ? addLeftChild(e, count) : this;
}
left = initLeft.setCount(comparator, e, count, result);
if (count == 0 && result[0] != 0) {
this.distinctElements--;
} else if (count > 0 && result[0] == 0) {
this.distinctElements++;
}
this.totalCount += count - result[0];
return rebalance();
} else if (cmp > 0) {
AvlNode<E> initRight = right;
if (initRight == null) {
result[0] = 0;
return (count > 0) ? addRightChild(e, count) : this;
}
right = initRight.setCount(comparator, e, count, result);
if (count == 0 && result[0] != 0) {
this.distinctElements--;
} else if (count > 0 && result[0] == 0) {
this.distinctElements++;
}
this.totalCount += count - result[0];
return rebalance();
}
// setting my count
result[0] = elemCount;
if (count == 0) {
return deleteMe();
}
this.totalCount += count - elemCount;
this.elemCount = count;
return this;
}
AvlNode<E> setCount(
Comparator<? super E> comparator,
@Nullable E e,
int expectedCount,
int newCount,
int[] result) {
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
AvlNode<E> initLeft = left;
if (initLeft == null) {
result[0] = 0;
if (expectedCount == 0 && newCount > 0) {
return addLeftChild(e, newCount);
}
return this;
}
left = initLeft.setCount(comparator, e, expectedCount, newCount, result);
if (result[0] == expectedCount) {
if (newCount == 0 && result[0] != 0) {
this.distinctElements--;
} else if (newCount > 0 && result[0] == 0) {
this.distinctElements++;
}
this.totalCount += newCount - result[0];
}
return rebalance();
} else if (cmp > 0) {
AvlNode<E> initRight = right;
if (initRight == null) {
result[0] = 0;
if (expectedCount == 0 && newCount > 0) {
return addRightChild(e, newCount);
}
return this;
}
right = initRight.setCount(comparator, e, expectedCount, newCount, result);
if (result[0] == expectedCount) {
if (newCount == 0 && result[0] != 0) {
this.distinctElements--;
} else if (newCount > 0 && result[0] == 0) {
this.distinctElements++;
}
this.totalCount += newCount - result[0];
}
return rebalance();
}
// setting my count
result[0] = elemCount;
if (expectedCount == elemCount) {
if (newCount == 0) {
return deleteMe();
}
this.totalCount += newCount - elemCount;
this.elemCount = newCount;
}
return this;
}
private AvlNode<E> deleteMe() {
int oldElemCount = this.elemCount;
this.elemCount = 0;
successor(pred, succ);
if (left == null) {
return right;
} else if (right == null) {
return left;
} else if (left.height >= right.height) {
AvlNode<E> newTop = pred;
// newTop is the maximum node in my left subtree
newTop.left = left.removeMax(newTop);
newTop.right = right;
newTop.distinctElements = distinctElements - 1;
newTop.totalCount = totalCount - oldElemCount;
return newTop.rebalance();
} else {
AvlNode<E> newTop = succ;
newTop.right = right.removeMin(newTop);
newTop.left = left;
newTop.distinctElements = distinctElements - 1;
newTop.totalCount = totalCount - oldElemCount;
return newTop.rebalance();
}
}
// Removes the minimum node from this subtree to be reused elsewhere
private AvlNode<E> removeMin(AvlNode<E> node) {
if (left == null) {
return right;
} else {
left = left.removeMin(node);
distinctElements--;
totalCount -= node.elemCount;
return rebalance();
}
}
// Removes the maximum node from this subtree to be reused elsewhere
private AvlNode<E> removeMax(AvlNode<E> node) {
if (right == null) {
return left;
} else {
right = right.removeMax(node);
distinctElements--;
totalCount -= node.elemCount;
return rebalance();
}
}
private void recomputeMultiset() {
this.distinctElements =
1 + TreeMultiset.distinctElements(left) + TreeMultiset.distinctElements(right);
this.totalCount = elemCount + totalCount(left) + totalCount(right);
}
private void recomputeHeight() {
this.height = 1 + Math.max(height(left), height(right));
}
private void recompute() {
recomputeMultiset();
recomputeHeight();
}
private AvlNode<E> rebalance() {
switch (balanceFactor()) {
case -2:
if (right.balanceFactor() > 0) {
right = right.rotateRight();
}
return rotateLeft();
case 2:
if (left.balanceFactor() < 0) {
left = left.rotateLeft();
}
return rotateRight();
default:
recomputeHeight();
return this;
}
}
private int balanceFactor() {
return height(left) - height(right);
}
private AvlNode<E> rotateLeft() {
checkState(right != null);
AvlNode<E> newTop = right;
this.right = newTop.left;
newTop.left = this;
newTop.totalCount = this.totalCount;
newTop.distinctElements = this.distinctElements;
this.recompute();
newTop.recomputeHeight();
return newTop;
}
private AvlNode<E> rotateRight() {
checkState(left != null);
AvlNode<E> newTop = left;
this.left = newTop.right;
newTop.right = this;
newTop.totalCount = this.totalCount;
newTop.distinctElements = this.distinctElements;
this.recompute();
newTop.recomputeHeight();
return newTop;
}
private static long totalCount(@Nullable AvlNode<?> node) {
return (node == null) ? 0 : node.totalCount;
}
private static int height(@Nullable AvlNode<?> node) {
return (node == null) ? 0 : node.height;
}
private @Nullable AvlNode<E> ceiling(Comparator<? super E> comparator, E e) {
int cmp = comparator.compare(e, elem);
if (cmp < 0) {
return (left == null) ? this : MoreObjects.firstNonNull(left.ceiling(comparator, e), this);
} else if (cmp == 0) {
return this;
} else {
return (right == null) ? null : right.ceiling(comparator, e);
}
}
private @Nullable AvlNode<E> floor(Comparator<? super E> comparator, E e) {
int cmp = comparator.compare(e, elem);
if (cmp > 0) {
return (right == null) ? this : MoreObjects.firstNonNull(right.floor(comparator, e), this);
} else if (cmp == 0) {
return this;
} else {
return (left == null) ? null : left.floor(comparator, e);
}
}
E getElement() {
return elem;
}
int getCount() {
return elemCount;
}
@Override
public String toString() {
return Multisets.immutableEntry(getElement(), getCount()).toString();
}
}
private static <T> void successor(AvlNode<T> a, AvlNode<T> b) {
a.succ = b;
b.pred = a;
}
private static <T> void successor(AvlNode<T> a, AvlNode<T> b, AvlNode<T> c) {
successor(a, b);
successor(b, c);
}
/*
* 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
/**
* @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<? super E> comparator = (Comparator<? super E>) 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<AvlNode<E>>());
AvlNode<E> header = new AvlNode<E>(null, 1);
Serialization.getFieldSetter(TreeMultiset.class, "header").set(this, header);
successor(header, header);
Serialization.populateMultiset(this, stream);
}
@GwtIncompatible // not needed in emulated source
private static final long serialVersionUID = 1;
}