@Override public SortedMultiset<E> tailMultiset(@Nullable E lowerBound, BoundType boundType) { return new TreeMultiset<E>( rootReference, range.intersect(GeneralRange.downTo(comparator(), lowerBound, boundType)), header); }
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; }
@Override public boolean equals(@Nullable Object obj) { if (obj instanceof GeneralRange) { GeneralRange<?> r = (GeneralRange<?>) obj; return comparator.equals(r.comparator) && hasLowerBound == r.hasLowerBound && hasUpperBound == r.hasUpperBound && getLowerBoundType().equals(r.getLowerBoundType()) && getUpperBoundType().equals(r.getUpperBoundType()) && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint()) && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint()); } return false; }
@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 e) { return 0; } catch (NullPointerException e) { return 0; } rootReference.checkAndSet(root, newRoot); return result[0]; }
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>>(); }
/** Returns the same range relative to the reversed comparator. */ GeneralRange<T> reverse() { GeneralRange<T> result = reverse; if (result == null) { result = new GeneralRange<T>( Ordering.from(comparator).reverse(), hasUpperBound, getUpperEndpoint(), getUpperBoundType(), hasLowerBound, getLowerEndpoint(), getLowerBoundType()); result.reverse = this; return this.reverse = result; } return result; }
@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; }
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); } }
@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); }
@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; } }
@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 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 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; }
TreeMultiset(Reference<AvlNode<E>> rootReference, GeneralRange<E> range, AvlNode<E> endLink) { super(range.comparator()); this.rootReference = rootReference; this.range = range; this.header = endLink; }
/** * Returns the intersection of the two ranges, or an empty range if their intersection is empty. */ GeneralRange<T> intersect(GeneralRange<T> other) { checkNotNull(other); checkArgument(comparator.equals(other.comparator)); boolean hasLowBound = this.hasLowerBound; @Nullable T lowEnd = getLowerEndpoint(); BoundType lowType = getLowerBoundType(); if (!hasLowerBound()) { hasLowBound = other.hasLowerBound; lowEnd = other.getLowerEndpoint(); lowType = other.getLowerBoundType(); } else if (other.hasLowerBound()) { int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint()); if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) { lowEnd = other.getLowerEndpoint(); lowType = other.getLowerBoundType(); } } boolean hasUpBound = this.hasUpperBound; @Nullable T upEnd = getUpperEndpoint(); BoundType upType = getUpperBoundType(); if (!hasUpperBound()) { hasUpBound = other.hasUpperBound; upEnd = other.getUpperEndpoint(); upType = other.getUpperBoundType(); } else if (other.hasUpperBound()) { int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint()); if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) { upEnd = other.getUpperEndpoint(); upType = other.getUpperBoundType(); } } if (hasLowBound && hasUpBound) { int cmp = comparator.compare(lowEnd, upEnd); if (cmp > 0 || (cmp == 0 && lowType == OPEN && upType == OPEN)) { // force allowed empty range lowEnd = upEnd; lowType = OPEN; upType = CLOSED; } } return new GeneralRange<T>(comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType); }