blob: 9445379a5835c4567af4655a54fa4716c2f2267f [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc.
*
* 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 com.google.common.annotations.GwtCompatible;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Nullable;
/**
* An immutable sorted set with one or more elements.
* TODO: Consider creating a separate class for a single-element sorted set.
*
* @author Jared Levy
*/
@GwtCompatible(serializable = true)
@SuppressWarnings("serial")
final class RegularImmutableSortedSet<E>
extends ImmutableSortedSet<E> {
private final Object[] elements;
/**
* The index of the first element that's in the sorted set (inclusive
* index).
*/
private final int fromIndex;
/**
* The index after the last element that's in the sorted set (exclusive
* index).
*/
private final int toIndex;
RegularImmutableSortedSet(Object[] elements,
Comparator<? super E> comparator) {
super(comparator);
this.elements = elements;
this.fromIndex = 0;
this.toIndex = elements.length;
}
RegularImmutableSortedSet(Object[] elements,
Comparator<? super E> comparator, int fromIndex, int toIndex) {
super(comparator);
this.elements = elements;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
// The factory methods ensure that every element is an E.
@SuppressWarnings("unchecked")
@Override public UnmodifiableIterator<E> iterator() {
return (UnmodifiableIterator<E>)
Iterators.forArray(elements, fromIndex, size());
}
@Override public boolean isEmpty() {
return false;
}
public int size() {
return toIndex - fromIndex;
}
@Override public boolean contains(Object o) {
if (o == null) {
return false;
}
try {
return binarySearch(o) >= 0;
} catch (ClassCastException e) {
return false;
}
}
@Override public boolean containsAll(Collection<?> targets) {
// TODO: For optimal performance, use a binary search when
// targets.size() < size() / log(size())
if (!hasSameComparator(targets, comparator()) || (targets.size() <= 1)) {
return super.containsAll(targets);
}
/*
* If targets is a sorted set with the same comparator, containsAll can
* run in O(n) time stepping through the two collections.
*/
int i = fromIndex;
Iterator<?> iterator = targets.iterator();
Object target = iterator.next();
while (true) {
if (i >= toIndex) {
return false;
}
int cmp = unsafeCompare(elements[i], target);
if (cmp < 0) {
i++;
} else if (cmp == 0) {
if (!iterator.hasNext()) {
return true;
}
target = iterator.next();
i++;
} else if (cmp > 0) {
return false;
}
}
}
private int binarySearch(Object key) {
int lower = fromIndex;
int upper = toIndex - 1;
while (lower <= upper) {
int middle = lower + (upper - lower) / 2;
int c = unsafeCompare(key, elements[middle]);
if (c < 0) {
upper = middle - 1;
} else if (c > 0) {
lower = middle + 1;
} else {
return middle;
}
}
return -lower - 1;
}
@Override public Object[] toArray() {
Object[] array = new Object[size()];
Platform.unsafeArrayCopy(elements, fromIndex, array, 0, size());
return array;
}
// TODO: Move to ObjectArrays (same code in ImmutableList).
@Override public <T> T[] toArray(T[] array) {
int size = size();
if (array.length < size) {
array = ObjectArrays.newArray(array, size);
} else if (array.length > size) {
array[size] = null;
}
Platform.unsafeArrayCopy(elements, fromIndex, array, 0, size);
return array;
}
@Override public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
}
if (!(object instanceof Set)) {
return false;
}
Set<?> that = (Set<?>) object;
if (size() != that.size()) {
return false;
}
if (hasSameComparator(that, comparator)) {
Iterator<?> iterator = that.iterator();
try {
for (int i = fromIndex; i < toIndex; i++) {
Object otherElement = iterator.next();
if (otherElement == null
|| unsafeCompare(elements[i], otherElement) != 0) {
return false;
}
}
return true;
} catch (ClassCastException e) {
return false;
} catch (NoSuchElementException e) {
return false; // concurrent change to other set
}
}
return this.containsAll(that);
}
@Override public int hashCode() {
// not caching hash code since it could change if the elements are mutable
// in a way that modifies their hash codes
int hash = 0;
for (int i = fromIndex; i < toIndex; i++) {
hash += elements[i].hashCode();
}
return hash;
}
// The factory methods ensure that every element is an E.
@SuppressWarnings("unchecked")
public E first() {
return (E) elements[fromIndex];
}
// The factory methods ensure that every element is an E.
@SuppressWarnings("unchecked")
public E last() {
return (E) elements[toIndex - 1];
}
@Override ImmutableSortedSet<E> headSetImpl(E toElement) {
return createSubset(fromIndex, findSubsetIndex(toElement));
}
@Override ImmutableSortedSet<E> subSetImpl(E fromElement, E toElement) {
return createSubset(
findSubsetIndex(fromElement), findSubsetIndex(toElement));
}
@Override ImmutableSortedSet<E> tailSetImpl(E fromElement) {
return createSubset(findSubsetIndex(fromElement), toIndex);
}
private int findSubsetIndex(E element) {
int index = binarySearch(element);
return (index >= 0) ? index : (-index - 1);
}
private ImmutableSortedSet<E> createSubset(
int newFromIndex, int newToIndex) {
if (newFromIndex < newToIndex) {
return new RegularImmutableSortedSet<E>(elements, comparator,
newFromIndex, newToIndex);
} else {
return emptySet(comparator);
}
}
@Override boolean hasPartialArray() {
return (fromIndex != 0) || (toIndex != elements.length);
}
@Override int indexOf(Object target) {
if (target == null) {
return -1;
}
int position;
try {
position = binarySearch(target);
} catch (ClassCastException e) {
return -1;
}
// The equals() check is needed when the comparator isn't compatible with
// equals().
return (position >= 0 && elements[position].equals(target))
? position - fromIndex : -1;
}
@Override ImmutableList<E> createAsList() {
return new ImmutableSortedAsList<E>(elements, fromIndex, size(), this);
}
}