/* * Copyright (C) 2011 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.checkNotNull; import static com.google.common.collect.SortedLists.KeyAbsentBehavior.INVERTED_INSERTION_INDEX; import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; import com.google.common.primitives.Ints; import java.util.Comparator; import java.util.List; import javax.annotation.Nullable; /** * An immutable sorted multiset with one or more distinct elements. * * @author Louis Wasserman */ final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset { private static final class CumulativeCountEntry extends Multisets.AbstractEntry { final E element; final int count; final long cumulativeCount; CumulativeCountEntry(E element, int count, @Nullable CumulativeCountEntry previous) { this.element = element; this.count = count; this.cumulativeCount = count + ((previous == null) ? 0 : previous.cumulativeCount); } @Override public E getElement() { return element; } @Override public int getCount() { return count; } } static RegularImmutableSortedMultiset createFromSorted(Comparator comparator, List> entries) { List> newEntries = Lists.newArrayListWithCapacity(entries.size()); CumulativeCountEntry previous = null; for (Multiset.Entry entry : entries) { newEntries.add( previous = new CumulativeCountEntry(entry.getElement(), entry.getCount(), previous)); } return new RegularImmutableSortedMultiset(comparator, ImmutableList.copyOf(newEntries)); } final transient ImmutableList> entries; RegularImmutableSortedMultiset( Comparator comparator, ImmutableList> entries) { super(comparator); this.entries = entries; assert !entries.isEmpty(); } ImmutableList elementList() { return new TransformedImmutableList, E>(entries) { @Override E transform(CumulativeCountEntry entry) { return entry.getElement(); } }; } @Override ImmutableSortedSet createElementSet() { return new RegularImmutableSortedSet(elementList(), comparator()); } @Override ImmutableSortedSet createDescendingElementSet() { return new RegularImmutableSortedSet(elementList().reverse(), reverseComparator()); } @SuppressWarnings("unchecked") @Override UnmodifiableIterator> entryIterator() { return (UnmodifiableIterator) entries.iterator(); } @SuppressWarnings("unchecked") @Override UnmodifiableIterator> descendingEntryIterator() { return (UnmodifiableIterator) entries.reverse().iterator(); } @Override public CumulativeCountEntry firstEntry() { return entries.get(0); } @Override public CumulativeCountEntry lastEntry() { return entries.get(entries.size() - 1); } @Override public int size() { CumulativeCountEntry firstEntry = firstEntry(); CumulativeCountEntry lastEntry = lastEntry(); return Ints.saturatedCast( lastEntry.cumulativeCount - firstEntry.cumulativeCount + firstEntry.count); } @Override int distinctElements() { return entries.size(); } @Override boolean isPartialView() { return entries.isPartialView(); } @SuppressWarnings("unchecked") @Override public int count(@Nullable Object element) { if (element == null) { return 0; } try { int index = SortedLists.binarySearch( elementList(), (E) element, comparator(), ANY_PRESENT, INVERTED_INSERTION_INDEX); return (index >= 0) ? entries.get(index).getCount() : 0; } catch (ClassCastException e) { return 0; } } @Override public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { int index; switch (boundType) { case OPEN: index = SortedLists.binarySearch( elementList(), checkNotNull(upperBound), comparator(), ANY_PRESENT, NEXT_HIGHER); break; case CLOSED: index = SortedLists.binarySearch( elementList(), checkNotNull(upperBound), comparator(), ANY_PRESENT, NEXT_LOWER) + 1; break; default: throw new AssertionError(); } return createSubMultiset(0, index); } @Override public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { int index; switch (boundType) { case OPEN: index = SortedLists.binarySearch( elementList(), checkNotNull(lowerBound), comparator(), ANY_PRESENT, NEXT_LOWER) + 1; break; case CLOSED: index = SortedLists.binarySearch( elementList(), checkNotNull(lowerBound), comparator(), ANY_PRESENT, NEXT_HIGHER); break; default: throw new AssertionError(); } return createSubMultiset(index, distinctElements()); } private ImmutableSortedMultiset createSubMultiset(int newFromIndex, int newToIndex) { if (newFromIndex == 0 && newToIndex == entries.size()) { return this; } else if (newFromIndex >= newToIndex) { return emptyMultiset(comparator()); } else { return new RegularImmutableSortedMultiset( comparator(), entries.subList(newFromIndex, newToIndex)); } } }