/*
 * 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. */
@serialDatathe 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; }