All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.SortedArraySet Maven / Gradle / Ivy

/*
 * Copyright (C) 2007 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.base.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;

/**
 * A sorted set that keeps its elements in a sorted {@code ArrayList}. Null
 * elements are allowed when the {@code SortedArraySet} is constructed with an
 * explicit comparator that supports nulls.
 *
 * 

This class is useful when you may have many sorted sets that only have * zero or one elements each. The performance of this implementation does not * scale to large numbers of elements as well as {@link java.util.TreeSet}, but * it is much more memory-efficient per entry. * *

Each {@code SortedArraySet} has a capacity, because it is backed by * an {@code ArrayList}. The capacity is the size of the array used to store the * elements in the set. It is always at least as large as the set size. As * elements are added to the set, its capacity grows automatically. The details * of the growth policy are not specified beyond the fact that adding an element * has O(lg n) amortized time cost. * *

An application can increase the capacity of the set before adding a large * number of elements using the {@code ensureCapacity} operation. This may * reduce the amount of incremental reallocation. * *

This implementation is not synchronized. As with {@code ArrayList}, * external synchronization is needed if multiple threads access a {@code * SortedArraySet} instance concurrently and at least one adds or deletes any * elements. * * @author Matthew Harris * @author Mike Bostock */ public final class SortedArraySet extends AbstractSet implements SortedSet, Serializable { private ArrayList contents; // initialized lazily private final Comparator comparator; /** * Constructs a new empty sorted set, sorted according to the element's * natural order, with an initial capacity of ten. All elements inserted into * the set must implement the {@code Comparable} interface. Furthermore, all * such elements must be mutally comparable: {@code e1.compareTo(e2)} * must not throw a {@code ClassCastException} for any elements {@code e1} and * {@code e2} in the set. If the user attempts to add an element to the set * that violates this constraint (for example, the user attempts to add a * string element to a set whose elements are integers), the {@code add} * method may throw a {@code ClassCastException}. * * @see Comparable * @see Comparators#naturalOrder */ public SortedArraySet() { this(10); } /** * Constructs a new empty sorted set, sorted according to the element's * natural order, with the specified initial capacity. All elements inserted * into the set must implement the {@code Comparable} interface. Furthermore, * all such elements must be mutually comparable: {@code * e1.compareTo(e2)} must not throw a {@code ClassCastException} for any * elements {@code e1} and {@code e2} in the set. If the user attempts to add * an element to the set that violates this constraint (for example, the user * attempts to add a string element to a set whose elements are integers), the * {@code add} method may throw a {@code ClassCastException}. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if {@code initialCapacity} is negative * @see Comparators#naturalOrder */ public SortedArraySet(int initialCapacity) { checkArgument(initialCapacity >= 0); comparator = orNaturalOrder(null); if (initialCapacity > 0) { contents = new ArrayList(initialCapacity); } } /** * Creates a new empty sorted set, sorted according to the specified * comparator, with the initial capacity of ten. All elements inserted into * the set must be mutually comparable 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 set. * If the user attempts to add an element to the set that violates this * constraint, the {@code add} method may throw a {@code ClassCastException}. * * @param comparator the comparator used to sort elements in this set */ public SortedArraySet(Comparator comparator) { this(comparator, 10); } /** * Creates a new empty sorted set, sorted according to the specified * comparator, with the specified initial capacity. All elements inserted into * the set must be mutually comparable 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 set. * If the user attempts to add an element to the set that violates this * constraint, the {@code add} method may throw a {@code ClassCastException}. * * @param comparator the comparator used to sort elements in this set */ public SortedArraySet(Comparator comparator, int initialCapacity) { checkNotNull(comparator); this.comparator = comparator; if (initialCapacity > 0) { contents = new ArrayList(initialCapacity); } } /** * Creates a new sorted set with the same elements as the specified * collection. If the specified collection is a {@code SortedSet} instance, * this constructor behaves identically to {@link #SortedArraySet(SortedSet)}. * Otherwise, the elements are sorted according to the elements' natural * order; see {@link #SortedArraySet()}. * * @param collection the elements that will comprise the new set * @throws ClassCastException if the elements in the specified collection are * not mutually comparable */ @SuppressWarnings("unchecked") public SortedArraySet(Collection collection) { if (collection instanceof SortedSet) { comparator = orNaturalOrder(((SortedSet) collection).comparator()); } else { comparator = orNaturalOrder(null); } addAll(collection); // careful if we make this class non-final } /** * Creates a new sorted set with the same elements and the same ordering as * the specified sorted set. * * @param set the set whose elements will comprise the new set */ public SortedArraySet(SortedSet set) { comparator = orNaturalOrder(set.comparator()); addAll(set); // careful if we make this class non-final } /** * Increases the capacity of this sorted set, if necessary, to ensure that it * can hold at least the number of elements specified by the minimum capacity * argument. * * @param minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { if (contents == null) { if (minCapacity > 0) { contents = new ArrayList(minCapacity); } } else { contents.ensureCapacity(minCapacity); } } /** * Trims the capacity of this sorted set to be the set's current size. An * application can use this operation to minimize the storage of a sorted * set. */ public void trimToSize() { if (size() == 0) { contents = null; } else { contents.trimToSize(); } } @Override public boolean add(E o) { if (contents == null) { contents = new ArrayList(1); contents.add(o); return true; } int pos = binarySearch(o); if (pos < 0) { contents.add(-pos - 1, o); return true; } return false; } @Override public boolean addAll(Collection c) { // optimize the case where c is sorted and we're empty if (((contents == null) || contents.isEmpty()) && !c.isEmpty() && (c instanceof SortedSet)) { Comparator comparator2 = ((SortedSet) c).comparator(); if (((comparator == Comparators.naturalOrder()) && (comparator2 == null)) || (comparator == comparator2)) { if (contents == null) { contents = new ArrayList(c); } else { contents.addAll(c); } return true; } } return super.addAll(c); } @Override public void clear() { contents = null; } @Override public boolean contains(Object o) { return binarySearch(o) >= 0; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof SortedArraySet) { SortedArraySet set = (SortedArraySet) o; if (comparator == set.comparator) { int n = size(); return (n == set.size()) // beware of null contents && ((n == 0) || contents.equals(set.contents)); } } return super.equals(o); } @Override public Iterator iterator() { return (contents == null) ? Iterators.emptyIterator() : contents.iterator(); } @Override public boolean remove(Object o) { int pos = binarySearch(o); if (pos < 0) { return false; } contents.remove(pos); return true; } @Override public int size() { return (contents == null) ? 0 : contents.size(); } /** * Returns the comparator associated with this sorted set, or {@code * Comparators.naturalOrder} if it uses its elements' natural ordering. */ public Comparator comparator() { return comparator; } public SortedSet subSet(E fromElement, E toElement) { checkArgument(comparator.compare(toElement, fromElement) >= 0); return new SubSet(fromElement, toElement, true, true); } public SortedSet headSet(E toElement) { return new SubSet(null, toElement, false, true); } public SortedSet tailSet(E fromElement) { return new SubSet(fromElement, null, true, false); } public E first() { if (isEmpty()) { throw new NoSuchElementException(); } return get(0); } public E last() { if (isEmpty()) { throw new NoSuchElementException(); } return get(size() - 1); } /** * Searches the backing list of the specified object using the binary search * algorithm. * *

This method runs in O(lg n) time. * * @param o the object to be searched for * @return the index of the found object, if it is contained in the list; * otherwise, {@code (-(insertionpoint) - 1)}. The insertion point * is defined as the point at which the object would be inserted into the * list: the index of the first element greater than the key, or {@code * list.size()}, if all elements in the list are less than the specified * key. Note that this guarantees that the return value will be >= 0 if * and only if the key is found. * @throws ClassCastException if the list contains elements that are not * mutually comparable (for example, strings and integers), or the * specified object is not mutually comparable with the elements of the * list * @see Collections#binarySearch(java.util.List, Object, Comparator) */ private int binarySearch(Object o) { if (contents == null) { return -1; } @SuppressWarnings("unchecked") E e = (E) o; return Collections.binarySearch(contents, e, comparator); } /** * Returns the element at the specified position in the backing list. * * @param index the index of the element to return * @throws NoSuchElementException if the specified index is out of bounds */ private E get(int index) { if (contents == null) { throw new NoSuchElementException(); } try { return contents.get(index); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } /** * Returns the specified comparator if not null; otherwise returns {@code * Comparators.naturalOrder}. This method is an abomination of generics; the * only purpose of this method is to contain the ugly type-casting in one * place. */ @SuppressWarnings("unchecked") private Comparator orNaturalOrder( @Nullable Comparator comparator) { if (comparator != null) { // can't use ? : because of javac bug 5080917 return comparator; } return (Comparator) Comparators.naturalOrder(); } /** @see #subSet */ private class SubSet extends AbstractSet implements SortedSet { final E head; final E tail; final boolean hasHead; final boolean hasTail; /** * Constructs a subset view into the SortedArraySet. * * @param fromElement the low endpoint (inclusive) of the subset, or * {@code null} * @param toElement the high endpoint (exclusive) of the subset, or * {@code null} * @param hasHead whether this subset has a lower bound * @param hasTail whether this subset has an upper bound */ SubSet(@Nullable E fromElement, @Nullable E toElement, boolean hasHead, boolean hasTail) { this.head = fromElement; this.tail = toElement; this.hasHead = hasHead; this.hasTail = hasTail; } /** * Returns the index of the low endpoint (inclusive) of the subset, or zero * if the low endpoint is undefined. */ int headIndex() { if (!hasHead) { return 0; } int pos = binarySearch(head); return (pos < 0) ? (-pos - 1) : pos; } /** * Returns the position of the high endpoint (exclusive) of the subset, or * the size of the list if the high endpoint is undefined. */ int tailIndex() { if (contents == null) { return 0; } if (!hasTail) { return contents.size(); } int pos = binarySearch(tail); return (pos < 0) ? (-pos - 1) : pos; } /** * Throws an {@code IllegalArgumentException} if the head of this subset * does not precede or equal the specified element. * * @param fromElement the element to compare to the head */ void checkHead(E fromElement) { if (hasHead) { checkArgument(comparator.compare(fromElement, head) >= 0); } } /** * Throws an {@code IllegalArgumentException} if the specified element does * not precede the tail of this subset. * * @param toElement the element to compare to the tail */ void checkTail(E toElement) { if (hasTail) { checkArgument(comparator.compare(tail, toElement) > 0); } } @Override public int size() { return tailIndex() - headIndex(); } public Comparator comparator() { return comparator; } @Override public Iterator iterator() { return (contents == null) ? Iterators.emptyIterator() : contents.subList(headIndex(), tailIndex()).iterator(); } @Override public boolean contains(Object o) { @SuppressWarnings("unchecked") // throws ClassCastException E e = (E) o; if ((hasHead && (comparator.compare(e, head) < 0)) || (hasTail && (comparator.compare(tail, e) <= 0))) { return false; } return SortedArraySet.this.contains(o); } public SortedSet subSet(E fromElement, E toElement) { checkArgument(comparator.compare(toElement, fromElement) >= 0); checkHead(fromElement); checkTail(toElement); return new SubSet(fromElement, toElement, true, true); } public SortedSet headSet(E toElement) { checkHead(toElement); checkTail(toElement); return new SubSet(head, toElement, hasHead, true); } public SortedSet tailSet(E fromElement) { checkHead(fromElement); checkTail(fromElement); return new SubSet(fromElement, tail, true, hasTail); } public E first() { E o = get(headIndex()); if (hasTail && (comparator.compare(tail, o) <= 0)) { throw new NoSuchElementException(); } return o; } public E last() { E o = get(tailIndex() - 1); if (hasHead && (comparator.compare(o, head) < 0)) { throw new NoSuchElementException(); } return o; } } private static final long serialVersionUID = -296929484947694088L; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy