mirror of
https://github.com/Eaglercraft-Archive/Eaglercraftx-1.8.8-src.git
synced 2025-06-28 02:48:14 -05:00
Update #0 - First Release
This commit is contained in:
989
sources/main/java/com/google/common/collect/TreeMultiset.java
Normal file
989
sources/main/java/com/google/common/collect/TreeMultiset.java
Normal file
@ -0,0 +1,989 @@
|
||||
/*
|
||||
* 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.checkState;
|
||||
import static com.google.common.collect.CollectPreconditions.checkNonnegative;
|
||||
import static com.google.common.collect.CollectPreconditions.checkRemove;
|
||||
|
||||
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 javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.annotations.GwtCompatible;
|
||||
import com.google.common.annotations.GwtIncompatible;
|
||||
import com.google.common.base.Objects;
|
||||
import 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.
|
||||
*
|
||||
* <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=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset">
|
||||
* {@code Multiset}</a>.
|
||||
*
|
||||
* @author Louis Wasserman
|
||||
* @author Jared Levy
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@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
|
||||
* {@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 <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.
|
||||
*
|
||||
* <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<AvlNode<E>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
@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 e) {
|
||||
return 0;
|
||||
} catch (NullPointerException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
|
||||
@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 e) {
|
||||
return 0;
|
||||
} catch (NullPointerException e) {
|
||||
return 0;
|
||||
}
|
||||
rootReference.checkAndSet(root, newRoot);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
@Nullable
|
||||
private 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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private 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<Entry<E>> entryIterator() {
|
||||
return new Iterator<Entry<E>>() {
|
||||
AvlNode<E> current = firstNode();
|
||||
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 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);
|
||||
}
|
||||
|
||||
static int distinctElements(@Nullable AvlNode<?> node) {
|
||||
return (node == null) ? 0 : node.distinctElements;
|
||||
}
|
||||
|
||||
private static final class Reference<T> {
|
||||
@Nullable
|
||||
private T value;
|
||||
|
||||
@Nullable
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void checkAndSet(@Nullable T expected, T newValue) {
|
||||
if (value != expected) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AvlNode<E> extends Multisets.AbstractEntry<E> {
|
||||
@Nullable
|
||||
private final E elem;
|
||||
|
||||
// elemCount is 0 iff this node has been deleted.
|
||||
private int elemCount;
|
||||
|
||||
private int distinctElements;
|
||||
private long totalCount;
|
||||
private int height;
|
||||
private AvlNode<E> left;
|
||||
private AvlNode<E> right;
|
||||
private AvlNode<E> pred;
|
||||
private 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;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AvlNode<E> ceiling(Comparator<? super E> comparator, E e) {
|
||||
int cmp = comparator.compare(e, elem);
|
||||
if (cmp < 0) {
|
||||
return (left == null) ? this : Objects.firstNonNull(left.ceiling(comparator, e), this);
|
||||
} else if (cmp == 0) {
|
||||
return this;
|
||||
} else {
|
||||
return (right == null) ? null : right.ceiling(comparator, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AvlNode<E> floor(Comparator<? super E> comparator, E e) {
|
||||
int cmp = comparator.compare(e, elem);
|
||||
if (cmp > 0) {
|
||||
return (right == null) ? this : Objects.firstNonNull(right.floor(comparator, e), this);
|
||||
} else if (cmp == 0) {
|
||||
return this;
|
||||
} else {
|
||||
return (left == null) ? null : left.floor(comparator, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getElement() {
|
||||
return elem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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
|
||||
*/
|
||||
@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;
|
||||
}
|
Reference in New Issue
Block a user