diff options
Diffstat (limited to 'guava/src/com/google/common/collect')
182 files changed, 9881 insertions, 19724 deletions
diff --git a/guava/src/com/google/common/collect/AbstractBiMap.java b/guava/src/com/google/common/collect/AbstractBiMap.java index 44ab8c7..2a94f88 100644 --- a/guava/src/com/google/common/collect/AbstractBiMap.java +++ b/guava/src/com/google/common/collect/AbstractBiMap.java @@ -49,7 +49,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable { private transient Map<K, V> delegate; - transient AbstractBiMap<V, K> inverse; + private transient AbstractBiMap<V, K> inverse; /** Package-private constructor for creating a map-backed bimap. */ AbstractBiMap(Map<K, V> forward, Map<V, K> backward) { @@ -67,20 +67,6 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> } /** - * Returns its input, or throws an exception if this is not a valid key. - */ - K checkKey(@Nullable K key) { - return key; - } - - /** - * Returns its input, or throws an exception if this is not a valid value. - */ - V checkValue(@Nullable V value) { - return value; - } - - /** * Specifies the delegate maps going in each direction. Called by the * constructor and by subclasses during deserialization. */ @@ -100,24 +86,22 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> // Query Operations (optimizations) - @Override public boolean containsValue(@Nullable Object value) { + @Override public boolean containsValue(Object value) { return inverse.containsKey(value); } // Modification Operations - @Override public V put(@Nullable K key, @Nullable V value) { + @Override public V put(K key, V value) { return putInBothMaps(key, value, false); } @Override - public V forcePut(@Nullable K key, @Nullable V value) { + public V forcePut(K key, V value) { return putInBothMaps(key, value, true); } private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) { - checkKey(key); - checkValue(value); boolean containedKey = containsKey(key); if (containedKey && Objects.equal(value, get(key))) { return value; @@ -140,7 +124,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> inverse.delegate.put(newValue, key); } - @Override public V remove(@Nullable Object key) { + @Override public V remove(Object key) { return containsKey(key) ? removeFromBothMaps(key) : null; } @@ -207,7 +191,27 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> } @Override public Iterator<K> iterator() { - return Maps.keyIterator(entrySet().iterator()); + final Iterator<Entry<K, V>> iterator = delegate.entrySet().iterator(); + return new Iterator<K>() { + Entry<K, V> entry; + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + @Override + public K next() { + entry = iterator.next(); + return entry.getKey(); + } + @Override + public void remove() { + checkState(entry != null); + V value = entry.getValue(); + iterator.remove(); + removeFromInverseMap(value); + } + }; } } @@ -230,7 +234,23 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> } @Override public Iterator<V> iterator() { - return Maps.valueIterator(entrySet().iterator()); + final Iterator<V> iterator = delegate.values().iterator(); + return new Iterator<V>() { + V valueToRemove; + + @Override public boolean hasNext() { + return iterator.hasNext(); + } + + @Override public V next() { + return valueToRemove = iterator.next(); + } + + @Override public void remove() { + iterator.remove(); + removeFromInverseMap(valueToRemove); + } + }; } @Override public Object[] toArray() { @@ -363,16 +383,6 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> * instances have inverse() methods that return the other. */ - @Override - K checkKey(K key) { - return inverse.checkValue(key); - } - - @Override - V checkValue(V value) { - return inverse.checkKey(value); - } - /** * @serialData the forward bimap */ diff --git a/guava/src/com/google/common/collect/AbstractSequentialIterator.java b/guava/src/com/google/common/collect/AbstractLinkedIterator.java index c6567f5..e796b9b 100644 --- a/guava/src/com/google/common/collect/AbstractSequentialIterator.java +++ b/guava/src/com/google/common/collect/AbstractLinkedIterator.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.NoSuchElementException; @@ -30,18 +31,18 @@ import javax.annotation.Nullable; * * <p>Example: <pre> {@code * - * Iterator<Integer> powersOfTwo = - * new AbstractSequentialIterator<Integer>(1) { - * protected Integer computeNext(Integer previous) { - * return (previous == 1 << 30) ? null : previous * 2; - * } - * };}</pre> + * Iterator<Integer> powersOfTwo = new AbstractLinkedIterator<Integer>(1) { + * protected Integer computeNext(Integer previous) { + * return (previous == 1 << 30) ? null : previous * 2; + * } + * };}</pre> * * @author Chris Povirk - * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) + * @since 8.0 */ +@Beta @GwtCompatible -public abstract class AbstractSequentialIterator<T> +public abstract class AbstractLinkedIterator<T> extends UnmodifiableIterator<T> { private T nextOrNull; @@ -49,7 +50,7 @@ public abstract class AbstractSequentialIterator<T> * Creates a new iterator with the given first element, or, if {@code * firstOrNull} is null, creates a new empty iterator. */ - protected AbstractSequentialIterator(@Nullable T firstOrNull) { + protected AbstractLinkedIterator(@Nullable T firstOrNull) { this.nextOrNull = firstOrNull; } diff --git a/guava/src/com/google/common/collect/AbstractListMultimap.java b/guava/src/com/google/common/collect/AbstractListMultimap.java index 3759c93..ad24011 100644 --- a/guava/src/com/google/common/collect/AbstractListMultimap.java +++ b/guava/src/com/google/common/collect/AbstractListMultimap.java @@ -26,7 +26,7 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link ListMultimap} interface. It's a wrapper - * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * around {@link AbstractMultimap} that converts the returned collections into * {@code Lists}. The {@link #createCollection} method must return a {@code * List}. * @@ -35,7 +35,7 @@ import javax.annotation.Nullable; */ @GwtCompatible abstract class AbstractListMultimap<K, V> - extends AbstractMapBasedMultimap<K, V> implements ListMultimap<K, V> { + extends AbstractMultimap<K, V> implements ListMultimap<K, V> { /** * Creates a new multimap that uses the provided map. * @@ -48,11 +48,6 @@ abstract class AbstractListMultimap<K, V> @Override abstract List<V> createCollection(); - @Override - List<V> createUnmodifiableEmptyCollection() { - return ImmutableList.of(); - } - // Following Javadoc copied from ListMultimap. /** diff --git a/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java b/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java deleted file mode 100644 index 0a1edf3..0000000 --- a/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java +++ /dev/null @@ -1,1567 +0,0 @@ -/* - * 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 com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -import java.util.AbstractCollection; -import java.util.AbstractMap; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.RandomAccess; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; - -import javax.annotation.Nullable; - -/** - * Basic implementation of the {@link Multimap} interface. This class represents - * a multimap as a map that associates each key with a collection of values. All - * methods of {@link Multimap} are supported, including those specified as - * optional in the interface. - * - * <p>To implement a multimap, a subclass must define the method {@link - * #createCollection()}, which creates an empty collection of values for a key. - * - * <p>The multimap constructor takes a map that has a single entry for each - * distinct key. When you insert a key-value pair with a key that isn't already - * in the multimap, {@code AbstractMapBasedMultimap} calls {@link #createCollection()} - * to create the collection of values for that key. The subclass should not call - * {@link #createCollection()} directly, and a new instance should be created - * every time the method is called. - * - * <p>For example, the subclass could pass a {@link java.util.TreeMap} during - * construction, and {@link #createCollection()} could return a {@link - * java.util.TreeSet}, in which case the multimap's iterators would propagate - * through the keys and values in sorted order. - * - * <p>Keys and values may be null, as long as the underlying collection classes - * support null elements. - * - * <p>The collections created by {@link #createCollection()} may or may not - * allow duplicates. If the collection, such as a {@link Set}, does not support - * duplicates, an added key-value pair will replace an existing pair with the - * same key and value, if such a pair is present. With collections like {@link - * List} that allow duplicates, the collection will keep the existing key-value - * pairs while adding a new pair. - * - * <p>This class is not threadsafe when any concurrent operations update the - * multimap, even if the underlying map and {@link #createCollection()} method - * return threadsafe classes. Concurrent read operations will work correctly. To - * allow concurrent update operations, wrap your multimap with a call to {@link - * Multimaps#synchronizedMultimap}. - * - * <p>For serialization to work, the subclass must specify explicit - * {@code readObject} and {@code writeObject} methods. - * - * @author Jared Levy - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V> - implements Serializable { - /* - * Here's an outline of the overall design. - * - * The map variable contains the collection of values associated with each - * key. When a key-value pair is added to a multimap that didn't previously - * contain any values for that key, a new collection generated by - * createCollection is added to the map. That same collection instance - * remains in the map as long as the multimap has any values for the key. If - * all values for the key are removed, the key and collection are removed - * from the map. - * - * The get method returns a WrappedCollection, which decorates the collection - * in the map (if the key is present) or an empty collection (if the key is - * not present). When the collection delegate in the WrappedCollection is - * empty, the multimap may contain subsequently added values for that key. To - * handle that situation, the WrappedCollection checks whether map contains - * an entry for the provided key, and if so replaces the delegate. - */ - - private transient Map<K, Collection<V>> map; - private transient int totalSize; - - /** - * Creates a new multimap that uses the provided map. - * - * @param map place to store the mapping from each key to its corresponding - * values - * @throws IllegalArgumentException if {@code map} is not empty - */ - protected AbstractMapBasedMultimap(Map<K, Collection<V>> map) { - checkArgument(map.isEmpty()); - this.map = map; - } - - /** Used during deserialization only. */ - final void setMap(Map<K, Collection<V>> map) { - this.map = map; - totalSize = 0; - for (Collection<V> values : map.values()) { - checkArgument(!values.isEmpty()); - totalSize += values.size(); - } - } - - /** - * Creates an unmodifiable, empty collection of values. - * - * <p>This is used in {@link #removeAll} on an empty key. - */ - Collection<V> createUnmodifiableEmptyCollection() { - return unmodifiableCollectionSubclass(createCollection()); - } - - /** - * Creates the collection of values for a single key. - * - * <p>Collections with weak, soft, or phantom references are not supported. - * Each call to {@code createCollection} should create a new instance. - * - * <p>The returned collection class determines whether duplicate key-value - * pairs are allowed. - * - * @return an empty collection of values - */ - abstract Collection<V> createCollection(); - - /** - * Creates the collection of values for an explicitly provided key. By - * default, it simply calls {@link #createCollection()}, which is the correct - * behavior for most implementations. The {@link LinkedHashMultimap} class - * overrides it. - * - * @param key key to associate with values in the collection - * @return an empty collection of values - */ - Collection<V> createCollection(@Nullable K key) { - return createCollection(); - } - - Map<K, Collection<V>> backingMap() { - return map; - } - - // Query Operations - - @Override - public int size() { - return totalSize; - } - - @Override - public boolean containsKey(@Nullable Object key) { - return map.containsKey(key); - } - - // Modification Operations - - @Override - public boolean put(@Nullable K key, @Nullable V value) { - Collection<V> collection = map.get(key); - if (collection == null) { - collection = createCollection(key); - if (collection.add(value)) { - totalSize++; - map.put(key, collection); - return true; - } else { - throw new AssertionError("New Collection violated the Collection spec"); - } - } else if (collection.add(value)) { - totalSize++; - return true; - } else { - return false; - } - } - - private Collection<V> getOrCreateCollection(@Nullable K key) { - Collection<V> collection = map.get(key); - if (collection == null) { - collection = createCollection(key); - map.put(key, collection); - } - return collection; - } - - // Bulk Operations - - /** - * {@inheritDoc} - * - * <p>The returned collection is immutable. - */ - @Override - public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { - Iterator<? extends V> iterator = values.iterator(); - if (!iterator.hasNext()) { - return removeAll(key); - } - - // TODO(user): investigate atomic failure? - Collection<V> collection = getOrCreateCollection(key); - Collection<V> oldValues = createCollection(); - oldValues.addAll(collection); - - totalSize -= collection.size(); - collection.clear(); - - while (iterator.hasNext()) { - if (collection.add(iterator.next())) { - totalSize++; - } - } - - return unmodifiableCollectionSubclass(oldValues); - } - - /** - * {@inheritDoc} - * - * <p>The returned collection is immutable. - */ - @Override - public Collection<V> removeAll(@Nullable Object key) { - Collection<V> collection = map.remove(key); - - if (collection == null) { - return createUnmodifiableEmptyCollection(); - } - - Collection<V> output = createCollection(); - output.addAll(collection); - totalSize -= collection.size(); - collection.clear(); - - return unmodifiableCollectionSubclass(output); - } - - Collection<V> unmodifiableCollectionSubclass(Collection<V> collection) { - // We don't deal with NavigableSet here yet for GWT reasons -- instead, - // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. - if (collection instanceof SortedSet) { - return Collections.unmodifiableSortedSet((SortedSet<V>) collection); - } else if (collection instanceof Set) { - return Collections.unmodifiableSet((Set<V>) collection); - } else if (collection instanceof List) { - return Collections.unmodifiableList((List<V>) collection); - } else { - return Collections.unmodifiableCollection(collection); - } - } - - @Override - public void clear() { - // Clear each collection, to make previously returned collections empty. - for (Collection<V> collection : map.values()) { - collection.clear(); - } - map.clear(); - totalSize = 0; - } - - // Views - - /** - * {@inheritDoc} - * - * <p>The returned collection is not serializable. - */ - @Override - public Collection<V> get(@Nullable K key) { - Collection<V> collection = map.get(key); - if (collection == null) { - collection = createCollection(key); - } - return wrapCollection(key, collection); - } - - /** - * Generates a decorated collection that remains consistent with the values in - * the multimap for the provided key. Changes to the multimap may alter the - * returned collection, and vice versa. - */ - Collection<V> wrapCollection(@Nullable K key, Collection<V> collection) { - // We don't deal with NavigableSet here yet for GWT reasons -- instead, - // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. - if (collection instanceof SortedSet) { - return new WrappedSortedSet(key, (SortedSet<V>) collection, null); - } else if (collection instanceof Set) { - return new WrappedSet(key, (Set<V>) collection); - } else if (collection instanceof List) { - return wrapList(key, (List<V>) collection, null); - } else { - return new WrappedCollection(key, collection, null); - } - } - - private List<V> wrapList( - @Nullable K key, List<V> list, @Nullable WrappedCollection ancestor) { - return (list instanceof RandomAccess) - ? new RandomAccessWrappedList(key, list, ancestor) - : new WrappedList(key, list, ancestor); - } - - /** - * Collection decorator that stays in sync with the multimap values for a key. - * There are two kinds of wrapped collections: full and subcollections. Both - * have a delegate pointing to the underlying collection class. - * - * <p>Full collections, identified by a null ancestor field, contain all - * multimap values for a given key. Its delegate is a value in {@link - * AbstractMapBasedMultimap#map} whenever the delegate is non-empty. The {@code - * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure - * that the {@code WrappedCollection} and map remain consistent. - * - * <p>A subcollection, such as a sublist, contains some of the values for a - * given key. Its ancestor field points to the full wrapped collection with - * all values for the key. The subcollection {@code refreshIfEmpty}, {@code - * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods - * of the full wrapped collection. - */ - private class WrappedCollection extends AbstractCollection<V> { - final K key; - Collection<V> delegate; - final WrappedCollection ancestor; - final Collection<V> ancestorDelegate; - - WrappedCollection(@Nullable K key, Collection<V> delegate, - @Nullable WrappedCollection ancestor) { - this.key = key; - this.delegate = delegate; - this.ancestor = ancestor; - this.ancestorDelegate - = (ancestor == null) ? null : ancestor.getDelegate(); - } - - /** - * If the delegate collection is empty, but the multimap has values for the - * key, replace the delegate with the new collection for the key. - * - * <p>For a subcollection, refresh its ancestor and validate that the - * ancestor delegate hasn't changed. - */ - void refreshIfEmpty() { - if (ancestor != null) { - ancestor.refreshIfEmpty(); - if (ancestor.getDelegate() != ancestorDelegate) { - throw new ConcurrentModificationException(); - } - } else if (delegate.isEmpty()) { - Collection<V> newDelegate = map.get(key); - if (newDelegate != null) { - delegate = newDelegate; - } - } - } - - /** - * If collection is empty, remove it from {@code AbstractMapBasedMultimap.this.map}. - * For subcollections, check whether the ancestor collection is empty. - */ - void removeIfEmpty() { - if (ancestor != null) { - ancestor.removeIfEmpty(); - } else if (delegate.isEmpty()) { - map.remove(key); - } - } - - K getKey() { - return key; - } - - /** - * Add the delegate to the map. Other {@code WrappedCollection} methods - * should call this method after adding elements to a previously empty - * collection. - * - * <p>Subcollection add the ancestor's delegate instead. - */ - void addToMap() { - if (ancestor != null) { - ancestor.addToMap(); - } else { - map.put(key, delegate); - } - } - - @Override public int size() { - refreshIfEmpty(); - return delegate.size(); - } - - @Override public boolean equals(@Nullable Object object) { - if (object == this) { - return true; - } - refreshIfEmpty(); - return delegate.equals(object); - } - - @Override public int hashCode() { - refreshIfEmpty(); - return delegate.hashCode(); - } - - @Override public String toString() { - refreshIfEmpty(); - return delegate.toString(); - } - - Collection<V> getDelegate() { - return delegate; - } - - @Override public Iterator<V> iterator() { - refreshIfEmpty(); - return new WrappedIterator(); - } - - /** Collection iterator for {@code WrappedCollection}. */ - class WrappedIterator implements Iterator<V> { - final Iterator<V> delegateIterator; - final Collection<V> originalDelegate = delegate; - - WrappedIterator() { - delegateIterator = iteratorOrListIterator(delegate); - } - - WrappedIterator(Iterator<V> delegateIterator) { - this.delegateIterator = delegateIterator; - } - - /** - * If the delegate changed since the iterator was created, the iterator is - * no longer valid. - */ - void validateIterator() { - refreshIfEmpty(); - if (delegate != originalDelegate) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - validateIterator(); - return delegateIterator.hasNext(); - } - - @Override - public V next() { - validateIterator(); - return delegateIterator.next(); - } - - @Override - public void remove() { - delegateIterator.remove(); - totalSize--; - removeIfEmpty(); - } - - Iterator<V> getDelegateIterator() { - validateIterator(); - return delegateIterator; - } - } - - @Override public boolean add(V value) { - refreshIfEmpty(); - boolean wasEmpty = delegate.isEmpty(); - boolean changed = delegate.add(value); - if (changed) { - totalSize++; - if (wasEmpty) { - addToMap(); - } - } - return changed; - } - - WrappedCollection getAncestor() { - return ancestor; - } - - // The following methods are provided for better performance. - - @Override public boolean addAll(Collection<? extends V> collection) { - if (collection.isEmpty()) { - return false; - } - int oldSize = size(); // calls refreshIfEmpty - boolean changed = delegate.addAll(collection); - if (changed) { - int newSize = delegate.size(); - totalSize += (newSize - oldSize); - if (oldSize == 0) { - addToMap(); - } - } - return changed; - } - - @Override public boolean contains(Object o) { - refreshIfEmpty(); - return delegate.contains(o); - } - - @Override public boolean containsAll(Collection<?> c) { - refreshIfEmpty(); - return delegate.containsAll(c); - } - - @Override public void clear() { - int oldSize = size(); // calls refreshIfEmpty - if (oldSize == 0) { - return; - } - delegate.clear(); - totalSize -= oldSize; - removeIfEmpty(); // maybe shouldn't be removed if this is a sublist - } - - @Override public boolean remove(Object o) { - refreshIfEmpty(); - boolean changed = delegate.remove(o); - if (changed) { - totalSize--; - removeIfEmpty(); - } - return changed; - } - - @Override public boolean removeAll(Collection<?> c) { - if (c.isEmpty()) { - return false; - } - int oldSize = size(); // calls refreshIfEmpty - boolean changed = delegate.removeAll(c); - if (changed) { - int newSize = delegate.size(); - totalSize += (newSize - oldSize); - removeIfEmpty(); - } - return changed; - } - - @Override public boolean retainAll(Collection<?> c) { - checkNotNull(c); - int oldSize = size(); // calls refreshIfEmpty - boolean changed = delegate.retainAll(c); - if (changed) { - int newSize = delegate.size(); - totalSize += (newSize - oldSize); - removeIfEmpty(); - } - return changed; - } - } - - private Iterator<V> iteratorOrListIterator(Collection<V> collection) { - return (collection instanceof List) - ? ((List<V>) collection).listIterator() - : collection.iterator(); - } - - /** Set decorator that stays in sync with the multimap values for a key. */ - private class WrappedSet extends WrappedCollection implements Set<V> { - WrappedSet(@Nullable K key, Set<V> delegate) { - super(key, delegate, null); - } - - @Override - public boolean removeAll(Collection<?> c) { - if (c.isEmpty()) { - return false; - } - int oldSize = size(); // calls refreshIfEmpty - - // Guava issue 1013: AbstractSet and most JDK set implementations are - // susceptible to quadratic removeAll performance on lists; - // use a slightly smarter implementation here - boolean changed = Sets.removeAllImpl((Set<V>) delegate, c); - if (changed) { - int newSize = delegate.size(); - totalSize += (newSize - oldSize); - removeIfEmpty(); - } - return changed; - } - } - - /** - * SortedSet decorator that stays in sync with the multimap values for a key. - */ - private class WrappedSortedSet extends WrappedCollection - implements SortedSet<V> { - WrappedSortedSet(@Nullable K key, SortedSet<V> delegate, - @Nullable WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - SortedSet<V> getSortedSetDelegate() { - return (SortedSet<V>) getDelegate(); - } - - @Override - public Comparator<? super V> comparator() { - return getSortedSetDelegate().comparator(); - } - - @Override - public V first() { - refreshIfEmpty(); - return getSortedSetDelegate().first(); - } - - @Override - public V last() { - refreshIfEmpty(); - return getSortedSetDelegate().last(); - } - - @Override - public SortedSet<V> headSet(V toElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), getSortedSetDelegate().headSet(toElement), - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public SortedSet<V> subSet(V fromElement, V toElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), getSortedSetDelegate().subSet(fromElement, toElement), - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public SortedSet<V> tailSet(V fromElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), getSortedSetDelegate().tailSet(fromElement), - (getAncestor() == null) ? this : getAncestor()); - } - } - - @GwtIncompatible("NavigableSet") - class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet<V> { - WrappedNavigableSet( - @Nullable K key, NavigableSet<V> delegate, @Nullable WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - @Override - NavigableSet<V> getSortedSetDelegate() { - return (NavigableSet<V>) super.getSortedSetDelegate(); - } - - @Override - public V lower(V v) { - return getSortedSetDelegate().lower(v); - } - - @Override - public V floor(V v) { - return getSortedSetDelegate().floor(v); - } - - @Override - public V ceiling(V v) { - return getSortedSetDelegate().ceiling(v); - } - - @Override - public V higher(V v) { - return getSortedSetDelegate().higher(v); - } - - @Override - public V pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public V pollLast() { - return Iterators.pollNext(descendingIterator()); - } - - private NavigableSet<V> wrap(NavigableSet<V> wrapped) { - return new WrappedNavigableSet(key, wrapped, - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public NavigableSet<V> descendingSet() { - return wrap(getSortedSetDelegate().descendingSet()); - } - - @Override - public Iterator<V> descendingIterator() { - return new WrappedIterator(getSortedSetDelegate().descendingIterator()); - } - - @Override - public NavigableSet<V> subSet( - V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - return wrap( - getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<V> headSet(V toElement, boolean inclusive) { - return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); - } - - @Override - public NavigableSet<V> tailSet(V fromElement, boolean inclusive) { - return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); - } - } - - /** List decorator that stays in sync with the multimap values for a key. */ - private class WrappedList extends WrappedCollection implements List<V> { - WrappedList(@Nullable K key, List<V> delegate, - @Nullable WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - List<V> getListDelegate() { - return (List<V>) getDelegate(); - } - - @Override - public boolean addAll(int index, Collection<? extends V> c) { - if (c.isEmpty()) { - return false; - } - int oldSize = size(); // calls refreshIfEmpty - boolean changed = getListDelegate().addAll(index, c); - if (changed) { - int newSize = getDelegate().size(); - totalSize += (newSize - oldSize); - if (oldSize == 0) { - addToMap(); - } - } - return changed; - } - - @Override - public V get(int index) { - refreshIfEmpty(); - return getListDelegate().get(index); - } - - @Override - public V set(int index, V element) { - refreshIfEmpty(); - return getListDelegate().set(index, element); - } - - @Override - public void add(int index, V element) { - refreshIfEmpty(); - boolean wasEmpty = getDelegate().isEmpty(); - getListDelegate().add(index, element); - totalSize++; - if (wasEmpty) { - addToMap(); - } - } - - @Override - public V remove(int index) { - refreshIfEmpty(); - V value = getListDelegate().remove(index); - totalSize--; - removeIfEmpty(); - return value; - } - - @Override - public int indexOf(Object o) { - refreshIfEmpty(); - return getListDelegate().indexOf(o); - } - - @Override - public int lastIndexOf(Object o) { - refreshIfEmpty(); - return getListDelegate().lastIndexOf(o); - } - - @Override - public ListIterator<V> listIterator() { - refreshIfEmpty(); - return new WrappedListIterator(); - } - - @Override - public ListIterator<V> listIterator(int index) { - refreshIfEmpty(); - return new WrappedListIterator(index); - } - - @Override - public List<V> subList(int fromIndex, int toIndex) { - refreshIfEmpty(); - return wrapList(getKey(), - getListDelegate().subList(fromIndex, toIndex), - (getAncestor() == null) ? this : getAncestor()); - } - - /** ListIterator decorator. */ - private class WrappedListIterator extends WrappedIterator - implements ListIterator<V> { - WrappedListIterator() {} - - public WrappedListIterator(int index) { - super(getListDelegate().listIterator(index)); - } - - private ListIterator<V> getDelegateListIterator() { - return (ListIterator<V>) getDelegateIterator(); - } - - @Override - public boolean hasPrevious() { - return getDelegateListIterator().hasPrevious(); - } - - @Override - public V previous() { - return getDelegateListIterator().previous(); - } - - @Override - public int nextIndex() { - return getDelegateListIterator().nextIndex(); - } - - @Override - public int previousIndex() { - return getDelegateListIterator().previousIndex(); - } - - @Override - public void set(V value) { - getDelegateListIterator().set(value); - } - - @Override - public void add(V value) { - boolean wasEmpty = isEmpty(); - getDelegateListIterator().add(value); - totalSize++; - if (wasEmpty) { - addToMap(); - } - } - } - } - - /** - * List decorator that stays in sync with the multimap values for a key and - * supports rapid random access. - */ - private class RandomAccessWrappedList extends WrappedList - implements RandomAccess { - RandomAccessWrappedList(@Nullable K key, List<V> delegate, - @Nullable WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - } - - @Override - Set<K> createKeySet() { - // TreeMultimap uses NavigableKeySet explicitly, but we don't handle that here for GWT - // compatibility reasons - return (map instanceof SortedMap) - ? new SortedKeySet((SortedMap<K, Collection<V>>) map) : new KeySet(map); - } - - private class KeySet extends Maps.KeySet<K, Collection<V>> { - - /** - * This is usually the same as map, except when someone requests a - * subcollection of a {@link SortedKeySet}. - */ - final Map<K, Collection<V>> subMap; - - KeySet(final Map<K, Collection<V>> subMap) { - this.subMap = subMap; - } - - @Override - Map<K, Collection<V>> map() { - return subMap; - } - - @Override public Iterator<K> iterator() { - final Iterator<Map.Entry<K, Collection<V>>> entryIterator - = subMap.entrySet().iterator(); - return new Iterator<K>() { - Map.Entry<K, Collection<V>> entry; - - @Override - public boolean hasNext() { - return entryIterator.hasNext(); - } - @Override - public K next() { - entry = entryIterator.next(); - return entry.getKey(); - } - @Override - public void remove() { - Iterators.checkRemove(entry != null); - Collection<V> collection = entry.getValue(); - entryIterator.remove(); - totalSize -= collection.size(); - collection.clear(); - } - }; - } - - // The following methods are included for better performance. - - @Override public boolean remove(Object key) { - int count = 0; - Collection<V> collection = subMap.remove(key); - if (collection != null) { - count = collection.size(); - collection.clear(); - totalSize -= count; - } - return count > 0; - } - - @Override - public void clear() { - Iterators.clear(iterator()); - } - - @Override public boolean containsAll(Collection<?> c) { - return subMap.keySet().containsAll(c); - } - - @Override public boolean equals(@Nullable Object object) { - return this == object || this.subMap.keySet().equals(object); - } - - @Override public int hashCode() { - return subMap.keySet().hashCode(); - } - } - - private class SortedKeySet extends KeySet implements SortedSet<K> { - - SortedKeySet(SortedMap<K, Collection<V>> subMap) { - super(subMap); - } - - SortedMap<K, Collection<V>> sortedMap() { - return (SortedMap<K, Collection<V>>) subMap; - } - - @Override - public Comparator<? super K> comparator() { - return sortedMap().comparator(); - } - - @Override - public K first() { - return sortedMap().firstKey(); - } - - @Override - public SortedSet<K> headSet(K toElement) { - return new SortedKeySet(sortedMap().headMap(toElement)); - } - - @Override - public K last() { - return sortedMap().lastKey(); - } - - @Override - public SortedSet<K> subSet(K fromElement, K toElement) { - return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); - } - - @Override - public SortedSet<K> tailSet(K fromElement) { - return new SortedKeySet(sortedMap().tailMap(fromElement)); - } - } - - @GwtIncompatible("NavigableSet") - class NavigableKeySet extends SortedKeySet implements NavigableSet<K> { - NavigableKeySet(NavigableMap<K, Collection<V>> subMap) { - super(subMap); - } - - @Override - NavigableMap<K, Collection<V>> sortedMap() { - return (NavigableMap<K, Collection<V>>) super.sortedMap(); - } - - @Override - public K lower(K k) { - return sortedMap().lowerKey(k); - } - - @Override - public K floor(K k) { - return sortedMap().floorKey(k); - } - - @Override - public K ceiling(K k) { - return sortedMap().ceilingKey(k); - } - - @Override - public K higher(K k) { - return sortedMap().higherKey(k); - } - - @Override - public K pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public K pollLast() { - return Iterators.pollNext(descendingIterator()); - } - - @Override - public NavigableSet<K> descendingSet() { - return new NavigableKeySet(sortedMap().descendingMap()); - } - - @Override - public Iterator<K> descendingIterator() { - return descendingSet().iterator(); - } - - @Override - public NavigableSet<K> headSet(K toElement) { - return headSet(toElement, false); - } - - @Override - public NavigableSet<K> headSet(K toElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); - } - - @Override - public NavigableSet<K> subSet(K fromElement, K toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override - public NavigableSet<K> subSet( - K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { - return new NavigableKeySet( - sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<K> tailSet(K fromElement) { - return tailSet(fromElement, true); - } - - @Override - public NavigableSet<K> tailSet(K fromElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); - } - } - - /** - * Removes all values for the provided key. Unlike {@link #removeAll}, it - * returns the number of removed mappings. - */ - private int removeValuesForKey(Object key) { - Collection<V> collection = Maps.safeRemove(map, key); - - int count = 0; - if (collection != null) { - count = collection.size(); - collection.clear(); - totalSize -= count; - } - return count; - } - - /** - * {@inheritDoc} - * - * <p>The iterator generated by the returned collection traverses the values - * for one key, followed by the values of a second key, and so on. - */ - @Override public Collection<V> values() { - return super.values(); - } - - /* - * TODO(kevinb): should we copy this javadoc to each concrete class, so that - * classes like LinkedHashMultimap that need to say something different are - * still able to {@inheritDoc} all the way from Multimap? - */ - - /** - * {@inheritDoc} - * - * <p>The iterator generated by the returned collection traverses the values - * for one key, followed by the values of a second key, and so on. - * - * <p>Each entry is an immutable snapshot of a key-value mapping in the - * multimap, taken at the time the entry is returned by a method call to the - * collection or its iterator. - */ - @Override - public Collection<Map.Entry<K, V>> entries() { - return super.entries(); - } - - /** - * Returns an iterator across all key-value map entries, used by {@code - * entries().iterator()} and {@code values().iterator()}. The default - * behavior, which traverses the values for one key, the values for a second - * key, and so on, suffices for most {@code AbstractMapBasedMultimap} implementations. - * - * @return an iterator across map entries - */ - @Override - Iterator<Map.Entry<K, V>> entryIterator() { - return new EntryIterator(); - } - - /** Iterator across all key-value pairs. */ - private class EntryIterator implements Iterator<Map.Entry<K, V>> { - final Iterator<Map.Entry<K, Collection<V>>> keyIterator; - K key; - Collection<V> collection; - Iterator<V> valueIterator; - - EntryIterator() { - keyIterator = map.entrySet().iterator(); - if (keyIterator.hasNext()) { - findValueIteratorAndKey(); - } else { - valueIterator = Iterators.emptyModifiableIterator(); - } - } - - void findValueIteratorAndKey() { - Map.Entry<K, Collection<V>> entry = keyIterator.next(); - key = entry.getKey(); - collection = entry.getValue(); - valueIterator = collection.iterator(); - } - - @Override - public boolean hasNext() { - return keyIterator.hasNext() || valueIterator.hasNext(); - } - - @Override - public Map.Entry<K, V> next() { - if (!valueIterator.hasNext()) { - findValueIteratorAndKey(); - } - return Maps.immutableEntry(key, valueIterator.next()); - } - - @Override - public void remove() { - valueIterator.remove(); - if (collection.isEmpty()) { - keyIterator.remove(); - } - totalSize--; - } - } - - @Override - Map<K, Collection<V>> createAsMap() { - // TreeMultimap uses NavigableAsMap explicitly, but we don't handle that here for GWT - // compatibility reasons - return (map instanceof SortedMap) - ? new SortedAsMap((SortedMap<K, Collection<V>>) map) : new AsMap(map); - } - - private class AsMap extends AbstractMap<K, Collection<V>> { - /** - * Usually the same as map, but smaller for the headMap(), tailMap(), or - * subMap() of a SortedAsMap. - */ - final transient Map<K, Collection<V>> submap; - - AsMap(Map<K, Collection<V>> submap) { - this.submap = submap; - } - - transient Set<Map.Entry<K, Collection<V>>> entrySet; - - @Override public Set<Map.Entry<K, Collection<V>>> entrySet() { - Set<Map.Entry<K, Collection<V>>> result = entrySet; - return (result == null) ? entrySet = new AsMapEntries() : result; - } - - // The following methods are included for performance. - - @Override public boolean containsKey(Object key) { - return Maps.safeContainsKey(submap, key); - } - - @Override public Collection<V> get(Object key) { - Collection<V> collection = Maps.safeGet(submap, key); - if (collection == null) { - return null; - } - @SuppressWarnings("unchecked") - K k = (K) key; - return wrapCollection(k, collection); - } - - @Override public Set<K> keySet() { - return AbstractMapBasedMultimap.this.keySet(); - } - - @Override - public int size() { - return submap.size(); - } - - @Override public Collection<V> remove(Object key) { - Collection<V> collection = submap.remove(key); - if (collection == null) { - return null; - } - - Collection<V> output = createCollection(); - output.addAll(collection); - totalSize -= collection.size(); - collection.clear(); - return output; - } - - @Override public boolean equals(@Nullable Object object) { - return this == object || submap.equals(object); - } - - @Override public int hashCode() { - return submap.hashCode(); - } - - @Override public String toString() { - return submap.toString(); - } - - @Override - public void clear() { - if (submap == map) { - AbstractMapBasedMultimap.this.clear(); - } else { - - Iterators.clear(new AsMapIterator()); - } - } - - Entry<K, Collection<V>> wrapEntry(Entry<K, Collection<V>> entry) { - K key = entry.getKey(); - return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); - } - - class AsMapEntries extends Maps.EntrySet<K, Collection<V>> { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override public Iterator<Map.Entry<K, Collection<V>>> iterator() { - return new AsMapIterator(); - } - - // The following methods are included for performance. - - @Override public boolean contains(Object o) { - return Collections2.safeContains(submap.entrySet(), o); - } - - @Override public boolean remove(Object o) { - if (!contains(o)) { - return false; - } - Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; - removeValuesForKey(entry.getKey()); - return true; - } - } - - /** Iterator across all keys and value collections. */ - class AsMapIterator implements Iterator<Map.Entry<K, Collection<V>>> { - final Iterator<Map.Entry<K, Collection<V>>> delegateIterator - = submap.entrySet().iterator(); - Collection<V> collection; - - @Override - public boolean hasNext() { - return delegateIterator.hasNext(); - } - - @Override - public Map.Entry<K, Collection<V>> next() { - Map.Entry<K, Collection<V>> entry = delegateIterator.next(); - collection = entry.getValue(); - return wrapEntry(entry); - } - - @Override - public void remove() { - delegateIterator.remove(); - totalSize -= collection.size(); - collection.clear(); - } - } - } - - private class SortedAsMap extends AsMap - implements SortedMap<K, Collection<V>> { - SortedAsMap(SortedMap<K, Collection<V>> submap) { - super(submap); - } - - SortedMap<K, Collection<V>> sortedMap() { - return (SortedMap<K, Collection<V>>) submap; - } - - @Override - public Comparator<? super K> comparator() { - return sortedMap().comparator(); - } - - @Override - public K firstKey() { - return sortedMap().firstKey(); - } - - @Override - public K lastKey() { - return sortedMap().lastKey(); - } - - @Override - public SortedMap<K, Collection<V>> headMap(K toKey) { - return new SortedAsMap(sortedMap().headMap(toKey)); - } - - @Override - public SortedMap<K, Collection<V>> subMap(K fromKey, K toKey) { - return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); - } - - @Override - public SortedMap<K, Collection<V>> tailMap(K fromKey) { - return new SortedAsMap(sortedMap().tailMap(fromKey)); - } - - SortedSet<K> sortedKeySet; - - // returns a SortedSet, even though returning a Set would be sufficient to - // satisfy the SortedMap.keySet() interface - @Override public SortedSet<K> keySet() { - SortedSet<K> result = sortedKeySet; - return (result == null) ? sortedKeySet = createKeySet() : result; - } - - SortedSet<K> createKeySet() { - return new SortedKeySet(sortedMap()); - } - } - - @GwtIncompatible("NavigableAsMap") - class NavigableAsMap extends SortedAsMap implements NavigableMap<K, Collection<V>> { - - NavigableAsMap(NavigableMap<K, Collection<V>> submap) { - super(submap); - } - - @Override - NavigableMap<K, Collection<V>> sortedMap() { - return (NavigableMap<K, Collection<V>>) super.sortedMap(); - } - - @Override - public Entry<K, Collection<V>> lowerEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().lowerEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K lowerKey(K key) { - return sortedMap().lowerKey(key); - } - - @Override - public Entry<K, Collection<V>> floorEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().floorEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K floorKey(K key) { - return sortedMap().floorKey(key); - } - - @Override - public Entry<K, Collection<V>> ceilingEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().ceilingEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K ceilingKey(K key) { - return sortedMap().ceilingKey(key); - } - - @Override - public Entry<K, Collection<V>> higherEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().higherEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K higherKey(K key) { - return sortedMap().higherKey(key); - } - - @Override - public Entry<K, Collection<V>> firstEntry() { - Entry<K, Collection<V>> entry = sortedMap().firstEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public Entry<K, Collection<V>> lastEntry() { - Entry<K, Collection<V>> entry = sortedMap().lastEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public Entry<K, Collection<V>> pollFirstEntry() { - return pollAsMapEntry(entrySet().iterator()); - } - - @Override - public Entry<K, Collection<V>> pollLastEntry() { - return pollAsMapEntry(descendingMap().entrySet().iterator()); - } - - Map.Entry<K, Collection<V>> pollAsMapEntry(Iterator<Entry<K, Collection<V>>> entryIterator) { - if (!entryIterator.hasNext()) { - return null; - } - Entry<K, Collection<V>> entry = entryIterator.next(); - Collection<V> output = createCollection(); - output.addAll(entry.getValue()); - entryIterator.remove(); - return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); - } - - @Override - public NavigableMap<K, Collection<V>> descendingMap() { - return new NavigableAsMap(sortedMap().descendingMap()); - } - - @Override - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - NavigableSet<K> createKeySet() { - return new NavigableKeySet(sortedMap()); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, Collection<V>> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, Collection<V>> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); - } - - @Override - public NavigableMap<K, Collection<V>> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public NavigableMap<K, Collection<V>> headMap(K toKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().headMap(toKey, false)); - } - - @Override - public NavigableMap<K, Collection<V>> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableMap<K, Collection<V>> tailMap(K fromKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); - } - } - - private static final long serialVersionUID = 2447537837011683357L; -} diff --git a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java index 6f14380..362386c 100644 --- a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java +++ b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java @@ -28,6 +28,7 @@ import com.google.common.primitives.Ints; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; @@ -37,7 +38,7 @@ import javax.annotation.Nullable; /** * Basic implementation of {@code Multiset<E>} backed by an instance of {@code - * Map<E, Count>}. + * Map<E, AtomicInteger>}. * * <p>For serialization to work, the subclass must specify explicit {@code * readObject} and {@code writeObject} methods. @@ -63,6 +64,10 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> this.size = super.size(); } + Map<E, Count> backingMap() { + return backingMap; + } + /** Used during deserialization only. The backing map must be empty. */ void setBackingMap(Map<E, Count> backingMap) { this.backingMap = backingMap; @@ -119,7 +124,8 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> @Override public void remove() { - Iterators.checkRemove(toRemove != null); + checkState(toRemove != null, + "no calls to next() since the last call to remove()"); size -= toRemove.getValue().getAndSet(0); backingEntries.remove(); toRemove = null; @@ -153,7 +159,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> /* * Not subclassing AbstractMultiset$MultisetIterator because next() needs to - * retrieve the Map.Entry<E, Count> entry, which can then be used for + * retrieve the Map.Entry<E, AtomicInteger> entry, which can then be used for * a more efficient remove() call. */ private class MapBasedMultisetIterator implements Iterator<E> { @@ -199,8 +205,14 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> } @Override public int count(@Nullable Object element) { - Count frequency = Maps.safeGet(backingMap, element); - return (frequency == null) ? 0 : frequency.get(); + try { + Count frequency = backingMap.get(element); + return (frequency == null) ? 0 : frequency.get(); + } catch (NullPointerException e) { + return 0; + } catch (ClassCastException e) { + return 0; + } } // Optional Operations - Modification Operations @@ -261,7 +273,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> } // Roughly a 33% performance improvement over AbstractMultiset.setCount(). - @Override public int setCount(@Nullable E element, int count) { + @Override public int setCount(E element, int count) { checkNonnegative(count, "count"); Count existingCounter; @@ -290,6 +302,98 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> return i.getAndSet(count); } + private int removeAllOccurrences(@Nullable Object element, + Map<E, Count> map) { + Count frequency = map.remove(element); + if (frequency == null) { + return 0; + } + int numberRemoved = frequency.getAndSet(0); + size -= numberRemoved; + return numberRemoved; + } + + // Views + + @Override Set<E> createElementSet() { + return new MapBasedElementSet(backingMap); + } + + // TODO(user): once TreeMultiset is replaced with a SortedMultiset + // implementation, replace this with a subclass of Multisets.ElementSet. + class MapBasedElementSet extends ForwardingSet<E> { + + // This mapping is the usually the same as 'backingMap', but can be a + // submap in some implementations. + private final Map<E, Count> map; + private final Set<E> delegate; + + MapBasedElementSet(Map<E, Count> map) { + this.map = map; + delegate = map.keySet(); + } + + @Override protected Set<E> delegate() { + return delegate; + } + + @Override public Iterator<E> iterator() { + final Iterator<Map.Entry<E, Count>> entries + = map.entrySet().iterator(); + return new Iterator<E>() { + Map.Entry<E, Count> toRemove; + + @Override + public boolean hasNext() { + return entries.hasNext(); + } + + @Override + public E next() { + toRemove = entries.next(); + return toRemove.getKey(); + } + + @Override + public void remove() { + checkState(toRemove != null, + "no calls to next() since the last call to remove()"); + size -= toRemove.getValue().getAndSet(0); + entries.remove(); + toRemove = null; + } + }; + } + + @Override public boolean remove(Object element) { + return removeAllOccurrences(element, map) != 0; + } + + @Override public boolean removeAll(Collection<?> elementsToRemove) { + return Iterators.removeAll(iterator(), elementsToRemove); + } + + @Override public boolean retainAll(Collection<?> elementsToRetain) { + return Iterators.retainAll(iterator(), elementsToRetain); + } + + @Override public void clear() { + if (map == backingMap) { + AbstractMapBasedMultiset.this.clear(); + } else { + Iterator<E> i = iterator(); + while (i.hasNext()) { + i.next(); + i.remove(); + } + } + } + + public Map<E, Count> getMap() { + return map; + } + } + // Don't allow default serialization. @GwtIncompatible("java.io.ObjectStreamException") @SuppressWarnings("unused") // actually used during deserialization diff --git a/guava/src/com/google/common/collect/AbstractMultimap.java b/guava/src/com/google/common/collect/AbstractMultimap.java index 13fdd00..38f69ec 100644 --- a/guava/src/com/google/common/collect/AbstractMultimap.java +++ b/guava/src/com/google/common/collect/AbstractMultimap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Guava Authors + * 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. @@ -16,33 +16,170 @@ 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 com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractMap; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; +import java.util.RandomAccess; import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; import javax.annotation.Nullable; /** - * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. - * + * Basic implementation of the {@link Multimap} interface. This class represents + * a multimap as a map that associates each key with a collection of values. All + * methods of {@link Multimap} are supported, including those specified as + * optional in the interface. + * + * <p>To implement a multimap, a subclass must define the method {@link + * #createCollection()}, which creates an empty collection of values for a key. + * + * <p>The multimap constructor takes a map that has a single entry for each + * distinct key. When you insert a key-value pair with a key that isn't already + * in the multimap, {@code AbstractMultimap} calls {@link #createCollection()} + * to create the collection of values for that key. The subclass should not call + * {@link #createCollection()} directly, and a new instance should be created + * every time the method is called. + * + * <p>For example, the subclass could pass a {@link java.util.TreeMap} during + * construction, and {@link #createCollection()} could return a {@link + * java.util.TreeSet}, in which case the multimap's iterators would propagate + * through the keys and values in sorted order. + * + * <p>Keys and values may be null, as long as the underlying collection classes + * support null elements. + * + * <p>The collections created by {@link #createCollection()} may or may not + * allow duplicates. If the collection, such as a {@link Set}, does not support + * duplicates, an added key-value pair will replace an existing pair with the + * same key and value, if such a pair is present. With collections like {@link + * List} that allow duplicates, the collection will keep the existing key-value + * pairs while adding a new pair. + * + * <p>This class is not threadsafe when any concurrent operations update the + * multimap, even if the underlying map and {@link #createCollection()} method + * return threadsafe classes. Concurrent read operations will work correctly. To + * allow concurrent update operations, wrap your multimap with a call to {@link + * Multimaps#synchronizedMultimap}. + * + * <p>For serialization to work, the subclass must specify explicit + * {@code readObject} and {@code writeObject} methods. + * + * @author Jared Levy * @author Louis Wasserman */ @GwtCompatible -abstract class AbstractMultimap<K, V> implements Multimap<K, V> { +abstract class AbstractMultimap<K, V> implements Multimap<K, V>, Serializable { + /* + * Here's an outline of the overall design. + * + * The map variable contains the collection of values associated with each + * key. When a key-value pair is added to a multimap that didn't previously + * contain any values for that key, a new collection generated by + * createCollection is added to the map. That same collection instance + * remains in the map as long as the multimap has any values for the key. If + * all values for the key are removed, the key and collection are removed + * from the map. + * + * The get method returns a WrappedCollection, which decorates the collection + * in the map (if the key is present) or an empty collection (if the key is + * not present). When the collection delegate in the WrappedCollection is + * empty, the multimap may contain subsequently added values for that key. To + * handle that situation, the WrappedCollection checks whether map contains + * an entry for the provided key, and if so replaces the delegate. + */ + + private transient Map<K, Collection<V>> map; + private transient int totalSize; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @throws IllegalArgumentException if {@code map} is not empty + */ + protected AbstractMultimap(Map<K, Collection<V>> map) { + checkArgument(map.isEmpty()); + this.map = map; + } + + /** Used during deserialization only. */ + final void setMap(Map<K, Collection<V>> map) { + this.map = map; + totalSize = 0; + for (Collection<V> values : map.values()) { + checkArgument(!values.isEmpty()); + totalSize += values.size(); + } + } + + /** + * Creates the collection of values for a single key. + * + * <p>Collections with weak, soft, or phantom references are not supported. + * Each call to {@code createCollection} should create a new instance. + * + * <p>The returned collection class determines whether duplicate key-value + * pairs are allowed. + * + * @return an empty collection of values + */ + abstract Collection<V> createCollection(); + + /** + * Creates the collection of values for an explicitly provided key. By + * default, it simply calls {@link #createCollection()}, which is the correct + * behavior for most implementations. The {@link LinkedHashMultimap} class + * overrides it. + * + * @param key key to associate with values in the collection + * @return an empty collection of values + */ + Collection<V> createCollection(@Nullable K key) { + return createCollection(); + } + + Map<K, Collection<V>> backingMap() { + return map; + } + + // Query Operations + + @Override + public int size() { + return totalSize; + } + @Override public boolean isEmpty() { - return size() == 0; + return totalSize == 0; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); } @Override public boolean containsValue(@Nullable Object value) { - for (Collection<V> collection : asMap().values()) { + for (Collection<V> collection : map.values()) { if (collection.contains(value)) { return true; } @@ -53,25 +190,72 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { @Override public boolean containsEntry(@Nullable Object key, @Nullable Object value) { - Collection<V> collection = asMap().get(key); + Collection<V> collection = map.get(key); return collection != null && collection.contains(value); } - + + // Modification Operations + @Override - public boolean remove(@Nullable Object key, @Nullable Object value) { - Collection<V> collection = asMap().get(key); - return collection != null && collection.remove(value); + public boolean put(@Nullable K key, @Nullable V value) { + Collection<V> collection = getOrCreateCollection(key); + + if (collection.add(value)) { + totalSize++; + return true; + } else { + return false; + } + } + + private Collection<V> getOrCreateCollection(@Nullable K key) { + Collection<V> collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + map.put(key, collection); + } + return collection; } @Override - public boolean put(@Nullable K key, @Nullable V value) { - return get(key).add(value); + public boolean remove(@Nullable Object key, @Nullable Object value) { + Collection<V> collection = map.get(key); + if (collection == null) { + return false; + } + + boolean changed = collection.remove(value); + if (changed) { + totalSize--; + if (collection.isEmpty()) { + map.remove(key); + } + } + return changed; } + // Bulk Operations + @Override public boolean putAll(@Nullable K key, Iterable<? extends V> values) { - checkNotNull(values); - return values.iterator().hasNext() && Iterables.addAll(get(key), values); + if (!values.iterator().hasNext()) { + return false; + } + Collection<V> collection = getOrCreateCollection(key); + int oldSize = collection.size(); + + boolean changed = false; + if (values instanceof Collection) { + Collection<? extends V> c = Collections2.cast(values); + changed = collection.addAll(c); + } else { + for (V value : values) { + changed |= collection.add(value); + } + } + + totalSize += (collection.size() - oldSize); + return changed; } @Override @@ -83,50 +267,597 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { return changed; } + /** + * {@inheritDoc} + * + * <p>The returned collection is immutable. + */ @Override - public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { - checkNotNull(values); - Collection<V> result = removeAll(key); - putAll(key, values); - return result; + public Collection<V> replaceValues( + @Nullable K key, Iterable<? extends V> values) { + Iterator<? extends V> iterator = values.iterator(); + if (!iterator.hasNext()) { + return removeAll(key); + } + + Collection<V> collection = getOrCreateCollection(key); + Collection<V> oldValues = createCollection(); + oldValues.addAll(collection); + + totalSize -= collection.size(); + collection.clear(); + + while (iterator.hasNext()) { + if (collection.add(iterator.next())) { + totalSize++; + } + } + + return unmodifiableCollectionSubclass(oldValues); } - - private transient Collection<Entry<K, V>> entries; + /** + * {@inheritDoc} + * + * <p>The returned collection is immutable. + */ @Override - public Collection<Entry<K, V>> entries() { - Collection<Entry<K, V>> result = entries; - return (result == null) ? entries = createEntries() : result; + public Collection<V> removeAll(@Nullable Object key) { + Collection<V> collection = map.remove(key); + Collection<V> output = createCollection(); + + if (collection != null) { + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + } + + return unmodifiableCollectionSubclass(output); } - - Collection<Entry<K, V>> createEntries() { - if (this instanceof SetMultimap) { - return new Multimaps.EntrySet<K, V>() { - @Override - Multimap<K, V> multimap() { - return AbstractMultimap.this; + + private Collection<V> unmodifiableCollectionSubclass( + Collection<V> collection) { + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet<V>) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set<V>) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List<V>) collection); + } else { + return Collections.unmodifiableCollection(collection); + } + } + + @Override + public void clear() { + // Clear each collection, to make previously returned collections empty. + for (Collection<V> collection : map.values()) { + collection.clear(); + } + map.clear(); + totalSize = 0; + } + + // Views + + /** + * {@inheritDoc} + * + * <p>The returned collection is not serializable. + */ + @Override + public Collection<V> get(@Nullable K key) { + Collection<V> collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + } + return wrapCollection(key, collection); + } + + /** + * Generates a decorated collection that remains consistent with the values in + * the multimap for the provided key. Changes to the multimap may alter the + * returned collection, and vice versa. + */ + private Collection<V> wrapCollection( + @Nullable K key, Collection<V> collection) { + if (collection instanceof SortedSet) { + return new WrappedSortedSet(key, (SortedSet<V>) collection, null); + } else if (collection instanceof Set) { + return new WrappedSet(key, (Set<V>) collection); + } else if (collection instanceof List) { + return wrapList(key, (List<V>) collection, null); + } else { + return new WrappedCollection(key, collection, null); + } + } + + private List<V> wrapList( + @Nullable K key, List<V> list, @Nullable WrappedCollection ancestor) { + return (list instanceof RandomAccess) + ? new RandomAccessWrappedList(key, list, ancestor) + : new WrappedList(key, list, ancestor); + } + + /** + * Collection decorator that stays in sync with the multimap values for a key. + * There are two kinds of wrapped collections: full and subcollections. Both + * have a delegate pointing to the underlying collection class. + * + * <p>Full collections, identified by a null ancestor field, contain all + * multimap values for a given key. Its delegate is a value in {@link + * AbstractMultimap#map} whenever the delegate is non-empty. The {@code + * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure + * that the {@code WrappedCollection} and map remain consistent. + * + * <p>A subcollection, such as a sublist, contains some of the values for a + * given key. Its ancestor field points to the full wrapped collection with + * all values for the key. The subcollection {@code refreshIfEmpty}, {@code + * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods + * of the full wrapped collection. + */ + private class WrappedCollection extends AbstractCollection<V> { + final K key; + Collection<V> delegate; + final WrappedCollection ancestor; + final Collection<V> ancestorDelegate; + + WrappedCollection(@Nullable K key, Collection<V> delegate, + @Nullable WrappedCollection ancestor) { + this.key = key; + this.delegate = delegate; + this.ancestor = ancestor; + this.ancestorDelegate + = (ancestor == null) ? null : ancestor.getDelegate(); + } + + /** + * If the delegate collection is empty, but the multimap has values for the + * key, replace the delegate with the new collection for the key. + * + * <p>For a subcollection, refresh its ancestor and validate that the + * ancestor delegate hasn't changed. + */ + void refreshIfEmpty() { + if (ancestor != null) { + ancestor.refreshIfEmpty(); + if (ancestor.getDelegate() != ancestorDelegate) { + throw new ConcurrentModificationException(); } + } else if (delegate.isEmpty()) { + Collection<V> newDelegate = map.get(key); + if (newDelegate != null) { + delegate = newDelegate; + } + } + } - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); + /** + * If collection is empty, remove it from {@code AbstractMultimap.this.map}. + * For subcollections, check whether the ancestor collection is empty. + */ + void removeIfEmpty() { + if (ancestor != null) { + ancestor.removeIfEmpty(); + } else if (delegate.isEmpty()) { + map.remove(key); + } + } + + K getKey() { + return key; + } + + /** + * Add the delegate to the map. Other {@code WrappedCollection} methods + * should call this method after adding elements to a previously empty + * collection. + * + * <p>Subcollection add the ancestor's delegate instead. + */ + void addToMap() { + if (ancestor != null) { + ancestor.addToMap(); + } else { + map.put(key, delegate); + } + } + + @Override public int size() { + refreshIfEmpty(); + return delegate.size(); + } + + @Override public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + refreshIfEmpty(); + return delegate.equals(object); + } + + @Override public int hashCode() { + refreshIfEmpty(); + return delegate.hashCode(); + } + + @Override public String toString() { + refreshIfEmpty(); + return delegate.toString(); + } + + Collection<V> getDelegate() { + return delegate; + } + + @Override public Iterator<V> iterator() { + refreshIfEmpty(); + return new WrappedIterator(); + } + + /** Collection iterator for {@code WrappedCollection}. */ + class WrappedIterator implements Iterator<V> { + final Iterator<V> delegateIterator; + final Collection<V> originalDelegate = delegate; + + WrappedIterator() { + delegateIterator = iteratorOrListIterator(delegate); + } + + WrappedIterator(Iterator<V> delegateIterator) { + this.delegateIterator = delegateIterator; + } + + /** + * If the delegate changed since the iterator was created, the iterator is + * no longer valid. + */ + void validateIterator() { + refreshIfEmpty(); + if (delegate != originalDelegate) { + throw new ConcurrentModificationException(); } - }; + } + + @Override + public boolean hasNext() { + validateIterator(); + return delegateIterator.hasNext(); + } + + @Override + public V next() { + validateIterator(); + return delegateIterator.next(); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize--; + removeIfEmpty(); + } + + Iterator<V> getDelegateIterator() { + validateIterator(); + return delegateIterator; + } } - return new Multimaps.Entries<K, V>() { + + @Override public boolean add(V value) { + refreshIfEmpty(); + boolean wasEmpty = delegate.isEmpty(); + boolean changed = delegate.add(value); + if (changed) { + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + return changed; + } + + WrappedCollection getAncestor() { + return ancestor; + } + + // The following methods are provided for better performance. + + @Override public boolean addAll(Collection<? extends V> collection) { + if (collection.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.addAll(collection); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override public boolean contains(Object o) { + refreshIfEmpty(); + return delegate.contains(o); + } + + @Override public boolean containsAll(Collection<?> c) { + refreshIfEmpty(); + return delegate.containsAll(c); + } + + @Override public void clear() { + int oldSize = size(); // calls refreshIfEmpty + if (oldSize == 0) { + return; + } + delegate.clear(); + totalSize -= oldSize; + removeIfEmpty(); // maybe shouldn't be removed if this is a sublist + } + + @Override public boolean remove(Object o) { + refreshIfEmpty(); + boolean changed = delegate.remove(o); + if (changed) { + totalSize--; + removeIfEmpty(); + } + return changed; + } + + @Override public boolean removeAll(Collection<?> c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.removeAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + + @Override public boolean retainAll(Collection<?> c) { + checkNotNull(c); + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.retainAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + private Iterator<V> iteratorOrListIterator(Collection<V> collection) { + return (collection instanceof List) + ? ((List<V>) collection).listIterator() + : collection.iterator(); + } + + /** Set decorator that stays in sync with the multimap values for a key. */ + private class WrappedSet extends WrappedCollection implements Set<V> { + WrappedSet(@Nullable K key, Set<V> delegate) { + super(key, delegate, null); + } + } + + /** + * SortedSet decorator that stays in sync with the multimap values for a key. + */ + private class WrappedSortedSet extends WrappedCollection + implements SortedSet<V> { + WrappedSortedSet(@Nullable K key, SortedSet<V> delegate, + @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + SortedSet<V> getSortedSetDelegate() { + return (SortedSet<V>) getDelegate(); + } + + @Override + public Comparator<? super V> comparator() { + return getSortedSetDelegate().comparator(); + } + + @Override + public V first() { + refreshIfEmpty(); + return getSortedSetDelegate().first(); + } + + @Override + public V last() { + refreshIfEmpty(); + return getSortedSetDelegate().last(); + } + + @Override + public SortedSet<V> headSet(V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().headSet(toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet<V> subSet(V fromElement, V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().subSet(fromElement, toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet<V> tailSet(V fromElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().tailSet(fromElement), + (getAncestor() == null) ? this : getAncestor()); + } + } + + /** List decorator that stays in sync with the multimap values for a key. */ + private class WrappedList extends WrappedCollection implements List<V> { + WrappedList(@Nullable K key, List<V> delegate, + @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + List<V> getListDelegate() { + return (List<V>) getDelegate(); + } + + @Override + public boolean addAll(int index, Collection<? extends V> c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = getListDelegate().addAll(index, c); + if (changed) { + int newSize = getDelegate().size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + public V get(int index) { + refreshIfEmpty(); + return getListDelegate().get(index); + } + + @Override + public V set(int index, V element) { + refreshIfEmpty(); + return getListDelegate().set(index, element); + } + + @Override + public void add(int index, V element) { + refreshIfEmpty(); + boolean wasEmpty = getDelegate().isEmpty(); + getListDelegate().add(index, element); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + + @Override + public V remove(int index) { + refreshIfEmpty(); + V value = getListDelegate().remove(index); + totalSize--; + removeIfEmpty(); + return value; + } + + @Override + public int indexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().lastIndexOf(o); + } + + @Override + public ListIterator<V> listIterator() { + refreshIfEmpty(); + return new WrappedListIterator(); + } + + @Override + public ListIterator<V> listIterator(int index) { + refreshIfEmpty(); + return new WrappedListIterator(index); + } + + @Override + public List<V> subList(int fromIndex, int toIndex) { + refreshIfEmpty(); + return wrapList(getKey(), + getListDelegate().subList(fromIndex, toIndex), + (getAncestor() == null) ? this : getAncestor()); + } + + /** ListIterator decorator. */ + private class WrappedListIterator extends WrappedIterator + implements ListIterator<V> { + WrappedListIterator() {} + + public WrappedListIterator(int index) { + super(getListDelegate().listIterator(index)); + } + + private ListIterator<V> getDelegateListIterator() { + return (ListIterator<V>) getDelegateIterator(); + } + @Override - Multimap<K, V> multimap() { - return AbstractMultimap.this; + public boolean hasPrevious() { + return getDelegateListIterator().hasPrevious(); } @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); + public V previous() { + return getDelegateListIterator().previous(); } - }; + + @Override + public int nextIndex() { + return getDelegateListIterator().nextIndex(); + } + + @Override + public int previousIndex() { + return getDelegateListIterator().previousIndex(); + } + + @Override + public void set(V value) { + getDelegateListIterator().set(value); + } + + @Override + public void add(V value) { + boolean wasEmpty = isEmpty(); + getDelegateListIterator().add(value); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + } + } + + /** + * List decorator that stays in sync with the multimap values for a key and + * supports rapid random access. + */ + private class RandomAccessWrappedList extends WrappedList + implements RandomAccess { + RandomAccessWrappedList(@Nullable K key, List<V> delegate, + @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } } - - abstract Iterator<Entry<K, V>> entryIterator(); private transient Set<K> keySet; @@ -136,48 +867,484 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { return (result == null) ? keySet = createKeySet() : result; } - Set<K> createKeySet() { - return new Maps.KeySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return asMap(); + private Set<K> createKeySet() { + return (map instanceof SortedMap) + ? new SortedKeySet((SortedMap<K, Collection<V>>) map) : new KeySet(map); + } + + private class KeySet extends Maps.KeySet<K, Collection<V>> { + + /** + * This is usually the same as map, except when someone requests a + * subcollection of a {@link SortedKeySet}. + */ + final Map<K, Collection<V>> subMap; + + KeySet(final Map<K, Collection<V>> subMap) { + this.subMap = subMap; + } + + @Override + Map<K, Collection<V>> map() { + return subMap; + } + + @Override public Iterator<K> iterator() { + return new Iterator<K>() { + final Iterator<Map.Entry<K, Collection<V>>> entryIterator + = subMap.entrySet().iterator(); + Map.Entry<K, Collection<V>> entry; + + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override + public K next() { + entry = entryIterator.next(); + return entry.getKey(); + } + @Override + public void remove() { + checkState(entry != null); + Collection<V> collection = entry.getValue(); + entryIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + }; + } + + // The following methods are included for better performance. + + @Override public boolean remove(Object key) { + int count = 0; + Collection<V> collection = subMap.remove(key); + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; } - }; + return count > 0; + } + + @Override + public void clear() { + Iterators.clear(iterator()); + } + + @Override public boolean containsAll(Collection<?> c) { + return subMap.keySet().containsAll(c); + } + + @Override public boolean equals(@Nullable Object object) { + return this == object || this.subMap.keySet().equals(object); + } + + @Override public int hashCode() { + return subMap.keySet().hashCode(); + } + } + + private class SortedKeySet extends KeySet implements SortedSet<K> { + + SortedKeySet(SortedMap<K, Collection<V>> subMap) { + super(subMap); + } + + SortedMap<K, Collection<V>> sortedMap() { + return (SortedMap<K, Collection<V>>) subMap; + } + + @Override + public Comparator<? super K> comparator() { + return sortedMap().comparator(); + } + + @Override + public K first() { + return sortedMap().firstKey(); + } + + @Override + public SortedSet<K> headSet(K toElement) { + return new SortedKeySet(sortedMap().headMap(toElement)); + } + + @Override + public K last() { + return sortedMap().lastKey(); + } + + @Override + public SortedSet<K> subSet(K fromElement, K toElement) { + return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); + } + + @Override + public SortedSet<K> tailSet(K fromElement) { + return new SortedKeySet(sortedMap().tailMap(fromElement)); + } } - - private transient Multiset<K> keys; - + + private transient Multiset<K> multiset; + @Override public Multiset<K> keys() { - Multiset<K> result = keys; - return (result == null) ? keys = createKeys() : result; + Multiset<K> result = multiset; + if (result == null) { + return multiset = new Multimaps.Keys<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + }; + } + return result; } - - Multiset<K> createKeys() { - return new Multimaps.Keys<K, V>(this); + + /** + * Removes all values for the provided key. Unlike {@link #removeAll}, it + * returns the number of removed mappings. + */ + private int removeValuesForKey(Object key) { + Collection<V> collection; + try { + collection = map.remove(key); + } catch (NullPointerException e) { + return 0; + } catch (ClassCastException e) { + return 0; + } + + int count = 0; + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count; } - - private transient Collection<V> values; - + + private transient Collection<V> valuesCollection; + + /** + * {@inheritDoc} + * + * <p>The iterator generated by the returned collection traverses the values + * for one key, followed by the values of a second key, and so on. + */ + @Override public Collection<V> values() { + Collection<V> result = valuesCollection; + if (result == null) { + return valuesCollection = new Multimaps.Values<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + }; + } + return result; + } + + private transient Collection<Map.Entry<K, V>> entries; + + /* + * TODO(kevinb): should we copy this javadoc to each concrete class, so that + * classes like LinkedHashMultimap that need to say something different are + * still able to {@inheritDoc} all the way from Multimap? + */ + + /** + * {@inheritDoc} + * + * <p>The iterator generated by the returned collection traverses the values + * for one key, followed by the values of a second key, and so on. + * + * <p>Each entry is an immutable snapshot of a key-value mapping in the + * multimap, taken at the time the entry is returned by a method call to the + * collection or its iterator. + */ @Override - public Collection<V> values() { - Collection<V> result = values; - return (result == null) ? values = createValues() : result; + public Collection<Map.Entry<K, V>> entries() { + Collection<Map.Entry<K, V>> result = entries; + return (result == null) ? entries = createEntries() : result; } - - Collection<V> createValues() { - return new Multimaps.Values<K, V>(this); + + Collection<Map.Entry<K, V>> createEntries() { + if (this instanceof SetMultimap) { + return new Multimaps.EntrySet<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + + @Override public Iterator<Entry<K, V>> iterator() { + return createEntryIterator(); + } + }; + } + return new Multimaps.Entries<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + + @Override public Iterator<Entry<K, V>> iterator() { + return createEntryIterator(); + } + }; } - + + /** + * Returns an iterator across all key-value map entries, used by {@code + * entries().iterator()} and {@code values().iterator()}. The default + * behavior, which traverses the values for one key, the values for a second + * key, and so on, suffices for most {@code AbstractMultimap} implementations. + * + * @return an iterator across map entries + */ + Iterator<Map.Entry<K, V>> createEntryIterator() { + return new EntryIterator(); + } + + /** Iterator across all key-value pairs. */ + private class EntryIterator implements Iterator<Map.Entry<K, V>> { + final Iterator<Map.Entry<K, Collection<V>>> keyIterator; + K key; + Collection<V> collection; + Iterator<V> valueIterator; + + EntryIterator() { + keyIterator = map.entrySet().iterator(); + if (keyIterator.hasNext()) { + findValueIteratorAndKey(); + } else { + valueIterator = Iterators.emptyModifiableIterator(); + } + } + + void findValueIteratorAndKey() { + Map.Entry<K, Collection<V>> entry = keyIterator.next(); + key = entry.getKey(); + collection = entry.getValue(); + valueIterator = collection.iterator(); + } + + @Override + public boolean hasNext() { + return keyIterator.hasNext() || valueIterator.hasNext(); + } + + @Override + public Map.Entry<K, V> next() { + if (!valueIterator.hasNext()) { + findValueIteratorAndKey(); + } + return Maps.immutableEntry(key, valueIterator.next()); + } + + @Override + public void remove() { + valueIterator.remove(); + if (collection.isEmpty()) { + keyIterator.remove(); + } + totalSize--; + } + } + private transient Map<K, Collection<V>> asMap; - + @Override public Map<K, Collection<V>> asMap() { Map<K, Collection<V>> result = asMap; return (result == null) ? asMap = createAsMap() : result; } - - abstract Map<K, Collection<V>> createAsMap(); + + private Map<K, Collection<V>> createAsMap() { + return (map instanceof SortedMap) + ? new SortedAsMap((SortedMap<K, Collection<V>>) map) : new AsMap(map); + } + + private class AsMap extends AbstractMap<K, Collection<V>> { + /** + * Usually the same as map, but smaller for the headMap(), tailMap(), or + * subMap() of a SortedAsMap. + */ + final transient Map<K, Collection<V>> submap; + + AsMap(Map<K, Collection<V>> submap) { + this.submap = submap; + } + + transient Set<Map.Entry<K, Collection<V>>> entrySet; + + @Override public Set<Map.Entry<K, Collection<V>>> entrySet() { + Set<Map.Entry<K, Collection<V>>> result = entrySet; + return (result == null) ? entrySet = new AsMapEntries() : result; + } + + // The following methods are included for performance. + + @Override public boolean containsKey(Object key) { + return Maps.safeContainsKey(submap, key); + } + + @Override public Collection<V> get(Object key) { + Collection<V> collection = Maps.safeGet(submap, key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") + K k = (K) key; + return wrapCollection(k, collection); + } + + @Override public Set<K> keySet() { + return AbstractMultimap.this.keySet(); + } + + @Override + public int size() { + return submap.size(); + } + + @Override public Collection<V> remove(Object key) { + Collection<V> collection = submap.remove(key); + if (collection == null) { + return null; + } + + Collection<V> output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + return output; + } + + @Override public boolean equals(@Nullable Object object) { + return this == object || submap.equals(object); + } + + @Override public int hashCode() { + return submap.hashCode(); + } + + @Override public String toString() { + return submap.toString(); + } + + @Override + public void clear() { + if (submap == map) { + AbstractMultimap.this.clear(); + } else { + + Iterators.clear(new AsMapIterator()); + } + } + + class AsMapEntries extends Maps.EntrySet<K, Collection<V>> { + @Override + Map<K, Collection<V>> map() { + return AsMap.this; + } + + @Override public Iterator<Map.Entry<K, Collection<V>>> iterator() { + return new AsMapIterator(); + } + + // The following methods are included for performance. + + @Override public boolean contains(Object o) { + return Collections2.safeContains(submap.entrySet(), o); + } + + @Override public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; + removeValuesForKey(entry.getKey()); + return true; + } + } + + /** Iterator across all keys and value collections. */ + class AsMapIterator implements Iterator<Map.Entry<K, Collection<V>>> { + final Iterator<Map.Entry<K, Collection<V>>> delegateIterator + = submap.entrySet().iterator(); + Collection<V> collection; + + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + + @Override + public Map.Entry<K, Collection<V>> next() { + Map.Entry<K, Collection<V>> entry = delegateIterator.next(); + K key = entry.getKey(); + collection = entry.getValue(); + return Maps.immutableEntry(key, wrapCollection(key, collection)); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + } + } + + private class SortedAsMap extends AsMap + implements SortedMap<K, Collection<V>> { + SortedAsMap(SortedMap<K, Collection<V>> submap) { + super(submap); + } + + SortedMap<K, Collection<V>> sortedMap() { + return (SortedMap<K, Collection<V>>) submap; + } + + @Override + public Comparator<? super K> comparator() { + return sortedMap().comparator(); + } + + @Override + public K firstKey() { + return sortedMap().firstKey(); + } + + @Override + public K lastKey() { + return sortedMap().lastKey(); + } + + @Override + public SortedMap<K, Collection<V>> headMap(K toKey) { + return new SortedAsMap(sortedMap().headMap(toKey)); + } + + @Override + public SortedMap<K, Collection<V>> subMap(K fromKey, K toKey) { + return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); + } + + @Override + public SortedMap<K, Collection<V>> tailMap(K fromKey) { + return new SortedAsMap(sortedMap().tailMap(fromKey)); + } + + SortedSet<K> sortedKeySet; + + // returns a SortedSet, even though returning a Set would be sufficient to + // satisfy the SortedMap.keySet() interface + @Override public SortedSet<K> keySet() { + SortedSet<K> result = sortedKeySet; + return (result == null) + ? sortedKeySet = new SortedKeySet(sortedMap()) : result; + } + } // Comparison and hashing @@ -187,7 +1354,7 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { } if (object instanceof Multimap) { Multimap<?, ?> that = (Multimap<?, ?>) object; - return this.asMap().equals(that.asMap()); + return this.map.equals(that.asMap()); } return false; } @@ -201,7 +1368,7 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { * @see Map#hashCode */ @Override public int hashCode() { - return asMap().hashCode(); + return map.hashCode(); } /** @@ -212,6 +1379,9 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { */ @Override public String toString() { - return asMap().toString(); + return map.toString(); } + + private static final long serialVersionUID = 2447537837011683357L; } + diff --git a/guava/src/com/google/common/collect/AbstractMultiset.java b/guava/src/com/google/common/collect/AbstractMultiset.java index 20ea93f..c0d2c4a 100644 --- a/guava/src/com/google/common/collect/AbstractMultiset.java +++ b/guava/src/com/google/common/collect/AbstractMultiset.java @@ -65,7 +65,7 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } @Override - public int count(@Nullable Object element) { + public int count(Object element) { for (Entry<E> entry : entrySet()) { if (Objects.equal(entry.getElement(), element)) { return entry.getCount(); @@ -82,26 +82,26 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } @Override - public int add(@Nullable E element, int occurrences) { + public int add(E element, int occurrences) { throw new UnsupportedOperationException(); } - @Override public boolean remove(@Nullable Object element) { + @Override public boolean remove(Object element) { return remove(element, 1) > 0; } @Override - public int remove(@Nullable Object element, int occurrences) { + public int remove(Object element, int occurrences) { throw new UnsupportedOperationException(); } @Override - public int setCount(@Nullable E element, int count) { + public int setCount(E element, int count) { return setCountImpl(this, element, count); } @Override - public boolean setCount(@Nullable E element, int oldCount, int newCount) { + public boolean setCount(E element, int oldCount, int newCount) { return setCountImpl(this, element, oldCount, newCount); } @@ -109,7 +109,7 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> /** * {@inheritDoc} - * + * * <p>This implementation is highly efficient when {@code elementsToAdd} * is itself a {@link Multiset}. */ @@ -158,11 +158,11 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } abstract Iterator<Entry<E>> entryIterator(); - + abstract int distinctElements(); private transient Set<Entry<E>> entrySet; - + @Override public Set<Entry<E>> entrySet() { Set<Entry<E>> result = entrySet; return (result == null) ? entrySet = createEntrySet() : result; diff --git a/guava/src/com/google/common/collect/AbstractNavigableMap.java b/guava/src/com/google/common/collect/AbstractNavigableMap.java deleted file mode 100644 index f6defe6..0000000 --- a/guava/src/com/google/common/collect/AbstractNavigableMap.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.AbstractMap; -import java.util.Iterator; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedMap; - -import javax.annotation.Nullable; - -/** - * Skeletal implementation of {@link NavigableMap}. - * - * @author Louis Wasserman - */ -abstract class AbstractNavigableMap<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V> { - - @Override - @Nullable - public abstract V get(@Nullable Object key); - - @Override - @Nullable - public Entry<K, V> firstEntry() { - return Iterators.getNext(entryIterator(), null); - } - - @Override - @Nullable - public Entry<K, V> lastEntry() { - return Iterators.getNext(descendingEntryIterator(), null); - } - - @Override - @Nullable - public Entry<K, V> pollFirstEntry() { - return Iterators.pollNext(entryIterator()); - } - - @Override - @Nullable - public Entry<K, V> pollLastEntry() { - return Iterators.pollNext(descendingEntryIterator()); - } - - @Override - public K firstKey() { - Entry<K, V> entry = firstEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public K lastKey() { - Entry<K, V> entry = lastEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - @Nullable - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - @Nullable - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - @Nullable - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - @Nullable - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K lowerKey(K key) { - return Maps.keyOrNull(lowerEntry(key)); - } - - @Override - public K floorKey(K key) { - return Maps.keyOrNull(floorEntry(key)); - } - - @Override - public K ceilingKey(K key) { - return Maps.keyOrNull(ceilingEntry(key)); - } - - @Override - public K higherKey(K key) { - return Maps.keyOrNull(higherEntry(key)); - } - - abstract Iterator<Entry<K, V>> entryIterator(); - - abstract Iterator<Entry<K, V>> descendingEntryIterator(); - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return new Maps.NavigableKeySet<K, V>(this); - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - @Override - public abstract int size(); - - @Override - public Set<Entry<K, V>> entrySet() { - return new Maps.EntrySet<K, V>() { - @Override - Map<K, V> map() { - return AbstractNavigableMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - }; - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return new DescendingMap(); - } - - private final class DescendingMap extends Maps.DescendingMap<K, V> { - @Override - NavigableMap<K, V> forward() { - return AbstractNavigableMap.this; - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return descendingEntryIterator(); - } - } - -} diff --git a/guava/src/com/google/common/collect/AbstractRangeSet.java b/guava/src/com/google/common/collect/AbstractRangeSet.java deleted file mode 100644 index e02f5da..0000000 --- a/guava/src/com/google/common/collect/AbstractRangeSet.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 javax.annotation.Nullable; - -/** - * A skeletal implementation of {@code RangeSet}. - * - * @author Louis Wasserman - */ -abstract class AbstractRangeSet<C extends Comparable> implements RangeSet<C> { - AbstractRangeSet() {} - - @Override - public boolean contains(C value) { - return rangeContaining(value) != null; - } - - @Override - public Range<C> rangeContaining(C value) { - checkNotNull(value); - for (Range<C> range : asRanges()) { - if (range.contains(value)) { - return range; - } - } - return null; - } - - @Override - public boolean isEmpty() { - return asRanges().isEmpty(); - } - - @Override - public void add(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - remove(Range.<C>all()); - } - - @Override - public boolean enclosesAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - if (!encloses(range)) { - return false; - } - } - return true; - } - - @Override - public void addAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - add(range); - } - } - - @Override - public void removeAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - remove(range); - } - } - - @Override - public boolean encloses(Range<C> otherRange) { - for (Range<C> range : asRanges()) { - if (range.encloses(otherRange)) { - return true; - } - } - return false; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof RangeSet) { - RangeSet<?> other = (RangeSet<?>) obj; - return this.asRanges().equals(other.asRanges()); - } - return false; - } - - @Override - public final int hashCode() { - return asRanges().hashCode(); - } - - @Override - public final String toString() { - StringBuilder builder = new StringBuilder(); - builder.append('{'); - for (Range<C> range : asRanges()) { - builder.append(range); - } - builder.append('}'); - return builder.toString(); - } -} diff --git a/guava/src/com/google/common/collect/AbstractSetMultimap.java b/guava/src/com/google/common/collect/AbstractSetMultimap.java index e43d8b1..fe68470 100644 --- a/guava/src/com/google/common/collect/AbstractSetMultimap.java +++ b/guava/src/com/google/common/collect/AbstractSetMultimap.java @@ -26,14 +26,14 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link SetMultimap} interface. It's a wrapper - * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * around {@link AbstractMultimap} that converts the returned collections into * {@code Sets}. The {@link #createCollection} method must return a {@code Set}. * * @author Jared Levy */ @GwtCompatible abstract class AbstractSetMultimap<K, V> - extends AbstractMapBasedMultimap<K, V> implements SetMultimap<K, V> { + extends AbstractMultimap<K, V> implements SetMultimap<K, V> { /** * Creates a new multimap that uses the provided map. * @@ -46,10 +46,6 @@ abstract class AbstractSetMultimap<K, V> @Override abstract Set<V> createCollection(); - @Override Set<V> createUnmodifiableEmptyCollection() { - return ImmutableSet.of(); - } - // Following Javadoc copied from SetMultimap. /** @@ -117,7 +113,7 @@ abstract class AbstractSetMultimap<K, V> * @return {@code true} if the method increased the size of the multimap, or * {@code false} if the multimap already contained the key-value pair */ - @Override public boolean put(@Nullable K key, @Nullable V value) { + @Override public boolean put(K key, V value) { return super.put(key, value); } diff --git a/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java deleted file mode 100644 index c561b87..0000000 --- a/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.util.Collection; -import java.util.SortedMap; -import java.util.SortedSet; - -/** - * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. - * - * This superclass allows {@code TreeMultimap} to override methods to return - * navigable set and map types in non-GWT only, while GWT code will inherit the - * SortedMap/SortedSet overrides. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class AbstractSortedKeySortedSetMultimap<K, V> extends AbstractSortedSetMultimap<K, V> { - - AbstractSortedKeySortedSetMultimap(SortedMap<K, Collection<V>> map) { - super(map); - } - - @Override - public SortedMap<K, Collection<V>> asMap() { - return (SortedMap<K, Collection<V>>) super.asMap(); - } - - @Override - SortedMap<K, Collection<V>> backingMap() { - return (SortedMap<K, Collection<V>>) super.backingMap(); - } - - @Override - public SortedSet<K> keySet() { - return (SortedSet<K>) super.keySet(); - } - -} diff --git a/guava/src/com/google/common/collect/AbstractSortedMultiset.java b/guava/src/com/google/common/collect/AbstractSortedMultiset.java index 7c277f8..b1a1d54 100644 --- a/guava/src/com/google/common/collect/AbstractSortedMultiset.java +++ b/guava/src/com/google/common/collect/AbstractSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -20,9 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; - -import javax.annotation.Nullable; +import java.util.SortedSet; /** * This class provides a skeletal implementation of the {@link SortedMultiset} interface. @@ -33,28 +31,33 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@GwtCompatible(emulated = true) +@GwtCompatible abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements SortedMultiset<E> { - @GwtTransient final Comparator<? super E> comparator; + final Comparator<? super E> comparator; // needed for serialization @SuppressWarnings("unchecked") AbstractSortedMultiset() { this((Comparator) Ordering.natural()); } - + AbstractSortedMultiset(Comparator<? super E> comparator) { this.comparator = checkNotNull(comparator); } @Override - public NavigableSet<E> elementSet() { - return (NavigableSet<E>) super.elementSet(); + public SortedSet<E> elementSet() { + return (SortedSet<E>) super.elementSet(); } @Override - NavigableSet<E> createElementSet() { - return new SortedMultisets.NavigableElementSet<E>(this); + SortedSet<E> createElementSet() { + return new SortedMultisets.ElementSet<E>() { + @Override + SortedMultiset<E> multiset() { + return AbstractSortedMultiset.this; + } + }; } @Override @@ -99,11 +102,8 @@ abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements } @Override - public SortedMultiset<E> subMultiset(@Nullable E fromElement, BoundType fromBoundType, - @Nullable E toElement, BoundType toBoundType) { - // These are checked elsewhere, but NullPointerTester wants them checked eagerly. - checkNotNull(fromBoundType); - checkNotNull(toBoundType); + public SortedMultiset<E> subMultiset(E fromElement, BoundType fromBoundType, E toElement, + BoundType toBoundType) { return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType); } @@ -122,7 +122,7 @@ abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements } SortedMultiset<E> createDescendingMultiset() { - return new DescendingMultiset<E>() { + return new SortedMultisets.DescendingMultiset<E>() { @Override SortedMultiset<E> forwardMultiset() { return AbstractSortedMultiset.this; diff --git a/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java b/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java index ac74155..2be5f4b 100644 --- a/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java +++ b/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java @@ -19,8 +19,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.Map; import java.util.SortedSet; @@ -28,7 +26,7 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link SortedSetMultimap} interface. It's a - * wrapper around {@link AbstractMapBasedMultimap} that converts the returned + * wrapper around {@link AbstractMultimap} that converts the returned * collections into sorted sets. The {@link #createCollection} method * must return a {@code SortedSet}. * @@ -47,18 +45,7 @@ abstract class AbstractSortedSetMultimap<K, V> super(map); } - @Override - abstract SortedSet<V> createCollection(); - - @Override - SortedSet<V> createUnmodifiableEmptyCollection() { - Comparator<? super V> comparator = valueComparator(); - if (comparator == null) { - return Collections.unmodifiableSortedSet(createCollection()); - } else { - return ImmutableSortedSet.emptySet(valueComparator()); - } - } + @Override abstract SortedSet<V> createCollection(); // Following Javadoc copied from Multimap and SortedSetMultimap. @@ -101,7 +88,7 @@ abstract class AbstractSortedSetMultimap<K, V> * <p>Any duplicates in {@code values} will be stored in the multimap once. */ @Override public SortedSet<V> replaceValues( - @Nullable K key, Iterable<? extends V> values) { + K key, Iterable<? extends V> values) { return (SortedSet<V>) super.replaceValues(key, values); } diff --git a/guava/src/com/google/common/collect/AllEqualOrdering.java b/guava/src/com/google/common/collect/AllEqualOrdering.java deleted file mode 100644 index c30164b..0000000 --- a/guava/src/com/google/common/collect/AllEqualOrdering.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.List; - -import javax.annotation.Nullable; - -/** - * An ordering that treats all references as equals, even nulls. - * - * @author Emily Soldal - */ -@GwtCompatible(serializable = true) -final class AllEqualOrdering extends Ordering<Object> implements Serializable { - static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); - - @Override - public int compare(@Nullable Object left, @Nullable Object right) { - return 0; - } - - @Override - public <E> List<E> sortedCopy(Iterable<E> iterable) { - return Lists.newArrayList(iterable); - } - - @Override - public <E> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) { - return ImmutableList.copyOf(iterable); - } - - @SuppressWarnings("unchecked") - @Override - public <S> Ordering<S> reverse() { - return (Ordering<S>) this; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "Ordering.allEqual()"; - } - - private static final long serialVersionUID = 0; -} diff --git a/guava/src/com/google/common/collect/ArrayListMultimap.java b/guava/src/com/google/common/collect/ArrayListMultimap.java index 759c073..43d42c3 100644 --- a/guava/src/com/google/common/collect/ArrayListMultimap.java +++ b/guava/src/com/google/common/collect/ArrayListMultimap.java @@ -55,10 +55,6 @@ import java.util.Map; * multimap. Concurrent read operations will work correctly. To allow concurrent * update operations, wrap your multimap with a call to {@link * Multimaps#synchronizedListMultimap}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) @@ -66,7 +62,7 @@ import java.util.Map; @GwtCompatible(serializable = true, emulated = true) public final class ArrayListMultimap<K, V> extends AbstractListMultimap<K, V> { // Default from ArrayList - private static final int DEFAULT_VALUES_PER_KEY = 3; + private static final int DEFAULT_VALUES_PER_KEY = 10; @VisibleForTesting transient int expectedValuesPerKey; diff --git a/guava/src/com/google/common/collect/ArrayTable.java b/guava/src/com/google/common/collect/ArrayTable.java index 554265c..28eb5b8 100644 --- a/guava/src/com/google/common/collect/ArrayTable.java +++ b/guava/src/com/google/common/collect/ArrayTable.java @@ -17,23 +17,21 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Objects; import java.io.Serializable; import java.lang.reflect.Array; import java.util.AbstractCollection; +import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; @@ -76,15 +74,10 @@ import javax.annotation.Nullable; * implementations, synchronization is unnecessary between a thread that writes * to one cell and a thread that reads from another. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table"> - * {@code Table}</a>. - * * @author Jared Levy * @since 10.0 */ @Beta -@GwtCompatible(emulated = true) public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { /** @@ -162,23 +155,22 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * columnKeys is empty but rowKeys isn't, the table is empty but * containsRow() can return true and rowKeySet() isn't empty. */ - rowKeyToIndex = index(rowList); - columnKeyToIndex = index(columnList); + ImmutableMap.Builder<R, Integer> rowBuilder = ImmutableMap.builder(); + for (int i = 0; i < rowList.size(); i++) { + rowBuilder.put(rowList.get(i), i); + } + rowKeyToIndex = rowBuilder.build(); + + ImmutableMap.Builder<C, Integer> columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < columnList.size(); i++) { + columnBuilder.put(columnList.get(i), i); + } + columnKeyToIndex = columnBuilder.build(); @SuppressWarnings("unchecked") V[][] tmpArray = (V[][]) new Object[rowList.size()][columnList.size()]; array = tmpArray; - // Necessary because in GWT the arrays are initialized with "undefined" instead of null. - eraseAll(); - } - - private static <E> ImmutableMap<E, Integer> index(List<E> list) { - ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder(); - for (int i = 0; i < list.size(); i++) { - columnBuilder.put(list.get(i), i); - } - return columnBuilder.build(); } private ArrayTable(Table<R, C, V> table) { @@ -194,116 +186,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @SuppressWarnings("unchecked") V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()]; array = copy; - // Necessary because in GWT the arrays are initialized with "undefined" instead of null. - eraseAll(); for (int i = 0; i < rowList.size(); i++) { System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); } } - private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> { - private final ImmutableMap<K, Integer> keyIndex; - - private ArrayMap(ImmutableMap<K, Integer> keyIndex) { - this.keyIndex = keyIndex; - } - - @Override - public Set<K> keySet() { - return keyIndex.keySet(); - } - - K getKey(int index) { - return keyIndex.keySet().asList().get(index); - } - - abstract String getKeyRole(); - - @Nullable abstract V getValue(int index); - - @Nullable abstract V setValue(int index, V newValue); - - @Override - public int size() { - return keyIndex.size(); - } - - @Override - public boolean isEmpty() { - return keyIndex.isEmpty(); - } - - @Override - protected Set<Entry<K, V>> createEntrySet() { - return new Maps.EntrySet<K, V>() { - @Override - Map<K, V> map() { - return ArrayMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return new AbstractIndexedListIterator<Entry<K, V>>(size()) { - @Override - protected Entry<K, V> get(final int index) { - return new AbstractMapEntry<K, V>() { - @Override - public K getKey() { - return ArrayMap.this.getKey(index); - } - - @Override - public V getValue() { - return ArrayMap.this.getValue(index); - } - - @Override - public V setValue(V value) { - return ArrayMap.this.setValue(index, value); - } - }; - } - }; - } - }; - } - - @Override - public boolean containsKey(@Nullable Object key) { - return keyIndex.containsKey(key); - } - - @Override - public V get(@Nullable Object key) { - Integer index = keyIndex.get(key); - if (index == null) { - return null; - } else { - return getValue(index); - } - } - - @Override - public V put(K key, V value) { - Integer index = keyIndex.get(key); - if (index == null) { - throw new IllegalArgumentException( - getKeyRole() + " " + key + " not in " + keyIndex.keySet()); - } - return setValue(index, value); - } - - @Override - public V remove(Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - } - /** * Returns, as an immutable list, the row keys provided when the table was * constructed, including those that are mapped to null values only. @@ -335,9 +222,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * allowed column keys */ public V at(int rowIndex, int columnIndex) { - // In GWT array access never throws IndexOutOfBoundsException. - checkElementIndex(rowIndex, rowList.size()); - checkElementIndex(columnIndex, columnList.size()); return array[rowIndex][columnIndex]; } @@ -357,9 +241,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * allowed column keys */ public V set(int rowIndex, int columnIndex, @Nullable V value) { - // In GWT array access never throws IndexOutOfBoundsException. - checkElementIndex(rowIndex, rowList.size()); - checkElementIndex(columnIndex, columnList.size()); V oldValue = array[rowIndex][columnIndex]; array[rowIndex][columnIndex] = value; return oldValue; @@ -375,7 +256,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * * @param valueClass class of values stored in the returned array */ - @GwtIncompatible("reflection") public V[][] toArray(Class<V> valueClass) { // Can change to use varargs in JDK 1.6 if we want @SuppressWarnings("unchecked") // TODO: safe? @@ -451,8 +331,12 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { public V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + private V getIndexed(Integer rowIndex, Integer columnIndex) { return (rowIndex == null || columnIndex == null) - ? null : at(rowIndex, columnIndex); + ? null : array[rowIndex][columnIndex]; } /** @@ -602,7 +486,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { } @Override public V getValue() { - return at(rowIndex, columnIndex); + return array[rowIndex][columnIndex]; } }; } @@ -620,7 +504,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { Integer columnIndex = columnKeyToIndex.get(cell.getColumnKey()); return rowIndex != null && columnIndex != null - && Objects.equal(at(rowIndex, columnIndex), cell.getValue()); + && Objects.equal(array[rowIndex][columnIndex], cell.getValue()); } return false; } @@ -646,27 +530,68 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { ? ImmutableMap.<R, V>of() : new Column(columnIndex); } - private class Column extends ArrayMap<R, V> { + private class Column extends AbstractMap<R, V> { final int columnIndex; Column(int columnIndex) { - super(rowKeyToIndex); this.columnIndex = columnIndex; } - @Override - String getKeyRole() { - return "Row"; + ColumnEntrySet entrySet; + + @Override public Set<Entry<R, V>> entrySet() { + ColumnEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnEntrySet(columnIndex) : set; + } + + @Override public V get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object rowKey) { + return rowKeyToIndex.containsKey(rowKey); + } + + @Override public V put(R rowKey, V value) { + checkNotNull(rowKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList); + return set(rowIndex, columnIndex, value); + } + + @Override public Set<R> keySet() { + return rowKeySet(); + } + } + + private class ColumnEntrySet extends AbstractSet<Entry<R, V>> { + final int columnIndex; + + ColumnEntrySet(int columnIndex) { + this.columnIndex = columnIndex; } - @Override - V getValue(int index) { - return at(index, columnIndex); + @Override public Iterator<Entry<R, V>> iterator() { + return new AbstractIndexedListIterator<Entry<R, V>>(size()) { + @Override protected Entry<R, V> get(final int rowIndex) { + return new AbstractMapEntry<R, V>() { + @Override public R getKey() { + return rowList.get(rowIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; } - @Override - V setValue(int index, V newValue) { - return set(index, columnIndex, newValue); + @Override public int size() { + return rowList.size(); } } @@ -689,32 +614,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? columnMap = new ColumnMap() : map; } - private class ColumnMap extends ArrayMap<C, Map<R, V>> { - private ColumnMap() { - super(columnKeyToIndex); + private class ColumnMap extends AbstractMap<C, Map<R, V>> { + transient ColumnMapEntrySet entrySet; + + @Override public Set<Entry<C, Map<R, V>>> entrySet() { + ColumnMapEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Column"; + @Override public Map<R, V> get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (columnIndex == null) ? null : new Column(columnIndex); } - @Override - Map<R, V> getValue(int index) { - return new Column(index); + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); } - @Override - Map<R, V> setValue(int index, Map<R, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<C> keySet() { + return columnKeySet(); } - @Override - public Map<R, V> put(C key, Map<R, V> value) { + @Override public Map<R, V> remove(Object columnKey) { throw new UnsupportedOperationException(); } } + private class ColumnMapEntrySet extends AbstractSet<Entry<C, Map<R, V>>> { + @Override public Iterator<Entry<C, Map<R, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<C, Map<R, V>>>(size()) { + @Override protected Entry<C, Map<R, V>> get(int index) { + return Maps.<C, Map<R, V>>immutableEntry(columnList.get(index), + new Column(index)); + } + }; + } + + @Override public int size() { + return columnList.size(); + } + } + /** * Returns a view of all mappings that have the given row key. If the * row key isn't in {@link #rowKeySet()}, an empty immutable map is @@ -735,27 +675,69 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex); } - private class Row extends ArrayMap<C, V> { + private class Row extends AbstractMap<C, V> { final int rowIndex; Row(int rowIndex) { - super(columnKeyToIndex); this.rowIndex = rowIndex; } - @Override - String getKeyRole() { - return "Column"; + RowEntrySet entrySet; + + @Override public Set<Entry<C, V>> entrySet() { + RowEntrySet set = entrySet; + return (set == null) ? entrySet = new RowEntrySet(rowIndex) : set; + } + + @Override public V get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); + } + + @Override public V put(C columnKey, V value) { + checkNotNull(columnKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + checkArgument(columnIndex != null, + "Column %s not in %s", columnKey, columnList); + return set(rowIndex, columnIndex, value); } - @Override - V getValue(int index) { - return at(rowIndex, index); + @Override public Set<C> keySet() { + return columnKeySet(); } + } + + private class RowEntrySet extends AbstractSet<Entry<C, V>> { + final int rowIndex; - @Override - V setValue(int index, V newValue) { - return set(rowIndex, index, newValue); + RowEntrySet(int rowIndex) { + this.rowIndex = rowIndex; + } + + @Override public Iterator<Entry<C, V>> iterator() { + return new AbstractIndexedListIterator<Entry<C, V>>(size()) { + @Override protected Entry<C, V> get(final int columnIndex) { + return new AbstractMapEntry<C, V>() { + @Override public C getKey() { + return columnList.get(columnIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; + } + + @Override public int size() { + return columnList.size(); } } @@ -778,32 +760,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? rowMap = new RowMap() : map; } - private class RowMap extends ArrayMap<R, Map<C, V>> { - private RowMap() { - super(rowKeyToIndex); + private class RowMap extends AbstractMap<R, Map<C, V>> { + transient RowMapEntrySet entrySet; + + @Override public Set<Entry<R, Map<C, V>>> entrySet() { + RowMapEntrySet set = entrySet; + return (set == null) ? entrySet = new RowMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Row"; + @Override public Map<C, V> get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return (rowIndex == null) ? null : new Row(rowIndex); } - @Override - Map<C, V> getValue(int index) { - return new Row(index); + @Override public boolean containsKey(Object rowKey) { + return containsRow(rowKey); } - @Override - Map<C, V> setValue(int index, Map<C, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<R> keySet() { + return rowKeySet(); } - @Override - public Map<C, V> put(R key, Map<C, V> value) { + @Override public Map<C, V> remove(Object rowKey) { throw new UnsupportedOperationException(); } } + private class RowMapEntrySet extends AbstractSet<Entry<R, Map<C, V>>> { + @Override public Iterator<Entry<R, Map<C, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<R, Map<C, V>>>(size()) { + @Override protected Entry<R, Map<C, V>> get(int index) { + return Maps.<R, Map<C, V>>immutableEntry(rowList.get(index), + new Row(index)); + } + }; + } + + @Override public int size() { + return rowList.size(); + } + } + private transient Collection<V> values; /** @@ -823,10 +820,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { private class Values extends AbstractCollection<V> { @Override public Iterator<V> iterator() { - return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) { - @Override - V transform(Cell<R, C, V> cell) { - return cell.getValue(); + return new AbstractIndexedListIterator<V>(size()) { + @Override protected V get(int index) { + int rowIndex = index / columnList.size(); + int columnIndex = index % columnList.size(); + return array[rowIndex][columnIndex]; } }; } @@ -834,6 +832,10 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @Override public int size() { return ArrayTable.this.size(); } + + @Override public boolean contains(Object value) { + return containsValue(value); + } } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/AsynchronousComputationException.java b/guava/src/com/google/common/collect/AsynchronousComputationException.java new file mode 100644 index 0000000..e64e17b --- /dev/null +++ b/guava/src/com/google/common/collect/AsynchronousComputationException.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 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; + +/** + * Wraps an exception that occurred during a computation in a different thread. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + * @deprecated this class is unused by com.google.common.collect. <b>This class + * is scheduled for deletion in November 2012.</b> + */ +@Deprecated +public +class AsynchronousComputationException extends ComputationException { + /** + * Creates a new instance with the given cause. + */ + public AsynchronousComputationException(Throwable cause) { + super(cause); + } + private static final long serialVersionUID = 0; +} diff --git a/guava/src/com/google/common/collect/BiMap.java b/guava/src/com/google/common/collect/BiMap.java index a6203e0..34d6677 100644 --- a/guava/src/com/google/common/collect/BiMap.java +++ b/guava/src/com/google/common/collect/BiMap.java @@ -28,10 +28,6 @@ import javax.annotation.Nullable; * its values as well as that of its keys. This constraint enables bimaps to * support an "inverse view", which is another bimap containing the same entries * as this bimap but with reversed keys and values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap"> - * {@code BiMap}</a>. * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/BoundType.java b/guava/src/com/google/common/collect/BoundType.java index 7b8f34b..3632b32 100644 --- a/guava/src/com/google/common/collect/BoundType.java +++ b/guava/src/com/google/common/collect/BoundType.java @@ -14,6 +14,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -23,26 +24,18 @@ import com.google.common.annotations.GwtCompatible; * * @since 10.0 */ +@Beta @GwtCompatible public enum BoundType { /** * The endpoint value <i>is not</i> considered part of the set ("exclusive"). */ - OPEN { - @Override - BoundType flip() { - return CLOSED; - } - }, + OPEN, + /** * The endpoint value <i>is</i> considered part of the set ("inclusive"). */ - CLOSED { - @Override - BoundType flip() { - return OPEN; - } - }; + CLOSED; /** * Returns the bound type corresponding to a boolean value for inclusivity. @@ -50,6 +43,4 @@ public enum BoundType { static BoundType forBoolean(boolean inclusive) { return inclusive ? CLOSED : OPEN; } - - abstract BoundType flip(); } diff --git a/guava/src/com/google/common/collect/BstAggregate.java b/guava/src/com/google/common/collect/BstAggregate.java new file mode 100644 index 0000000..84d150f --- /dev/null +++ b/guava/src/com/google/common/collect/BstAggregate.java @@ -0,0 +1,41 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * An integer-valued function on binary search tree nodes that adds between nodes. + * + * <p>The value of individual entries must fit into an {@code int}, but the value of an entire + * tree can require a {@code long}. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface BstAggregate<N extends BstNode<?, N>> { + /** + * The total value on an entire subtree. Must be equal to the sum of the {@link #entryValue + * entryValue} of this node and all its descendants. + */ + long treeValue(@Nullable N tree); + + /** + * The value on a single entry, ignoring its descendants. + */ + int entryValue(N entry); +} diff --git a/guava/src/com/google/common/collect/BstBalancePolicy.java b/guava/src/com/google/common/collect/BstBalancePolicy.java new file mode 100644 index 0000000..d1e93d0 --- /dev/null +++ b/guava/src/com/google/common/collect/BstBalancePolicy.java @@ -0,0 +1,45 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A local balancing policy for modified nodes in binary search trees. + * + * @author Louis Wasserman + * @param <N> The type of the nodes in the trees that this {@code BstRebalancePolicy} can + * rebalance. + */ +@GwtCompatible +interface BstBalancePolicy<N extends BstNode<?, N>> { + /** + * Constructs a locally balanced tree around the key and value data in {@code source}, and the + * subtrees {@code left} and {@code right}. It is guaranteed that the resulting tree will have + * the same inorder traversal order as the subtree {@code left}, then the entry {@code source}, + * then the subtree {@code right}. + */ + N balance(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right); + + /** + * Constructs a locally balanced tree around the subtrees {@code left} and {@code right}. It is + * guaranteed that the resulting tree will have the same inorder traversal order as the subtree + * {@code left}, then the subtree {@code right}. + */ + @Nullable + N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right); +} diff --git a/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java b/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java new file mode 100644 index 0000000..5b98b91 --- /dev/null +++ b/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java @@ -0,0 +1,212 @@ +/* + * 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.BstOperations.extractMax; +import static com.google.common.collect.BstOperations.extractMin; +import static com.google.common.collect.BstOperations.insertMax; +import static com.google.common.collect.BstOperations.insertMin; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A tree-size-based set of balancing policies, based on <a + * href="http://www.swiss.ai.mit.edu/~adams/BB/"> Stephen Adams, "Efficient sets: a balancing + * act."</a>. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstCountBasedBalancePolicies { + private BstCountBasedBalancePolicies() {} + + private static final int SINGLE_ROTATE_RATIO = 4; + private static final int SECOND_ROTATE_RATIO = 2; + + /** + * Returns a balance policy that does no balancing or the bare minimum (for {@code combine}). + */ + public static <N extends BstNode<?, N>> BstBalancePolicy<N> noRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + return checkNotNull(nodeFactory).createNode(source, left, right); + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } else if (countAggregate.treeValue(left) > countAggregate.treeValue(right)) { + return nodeFactory.createNode( + left, left.childOrNull(LEFT), combine(nodeFactory, left.childOrNull(RIGHT), right)); + } else { + return nodeFactory.createNode(right, combine(nodeFactory, left, right.childOrNull(LEFT)), + right.childOrNull(RIGHT)); + } + } + }; + } + + /** + * Returns a balance policy that expects the sizes of each side to be at most one node (added or + * removed) away from being balanced. {@code balance} takes {@code O(1)} time, and {@code + * combine} takes {@code O(log n)} time. + */ + public static <K, N extends BstNode<K, N>> BstBalancePolicy<N> singleRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (countL + countR > 1) { + if (countR >= SINGLE_ROTATE_RATIO * countL) { + return rotateL(nodeFactory, source, left, right); + } else if (countL >= SINGLE_ROTATE_RATIO * countR) { + return rotateR(nodeFactory, source, left, right); + } + } + return nodeFactory.createNode(source, left, right); + } + + private N rotateL(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, N right) { + checkNotNull(right); + N rl = right.childOrNull(LEFT); + N rr = right.childOrNull(RIGHT); + if (countAggregate.treeValue(rl) >= SECOND_ROTATE_RATIO * countAggregate.treeValue(rr)) { + right = singleR(nodeFactory, right, rl, rr); + } + return singleL(nodeFactory, source, left, right); + } + + private N rotateR(BstNodeFactory<N> nodeFactory, N source, N left, @Nullable N right) { + checkNotNull(left); + N lr = left.childOrNull(RIGHT); + N ll = left.childOrNull(LEFT); + if (countAggregate.treeValue(lr) >= SECOND_ROTATE_RATIO * countAggregate.treeValue(ll)) { + left = singleL(nodeFactory, left, ll, lr); + } + return singleR(nodeFactory, source, left, right); + } + + private N singleL(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, N right) { + checkNotNull(right); + return nodeFactory.createNode(right, + nodeFactory.createNode(source, left, right.childOrNull(LEFT)), + right.childOrNull(RIGHT)); + } + + private N singleR(BstNodeFactory<N> nodeFactory, N source, N left, @Nullable N right) { + checkNotNull(left); + return nodeFactory.createNode(left, left.childOrNull(LEFT), + nodeFactory.createNode(source, left.childOrNull(RIGHT), right)); + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } + N newRootSource; + if (countAggregate.treeValue(left) > countAggregate.treeValue(right)) { + BstMutationResult<K, N> extractLeftMax = extractMax(left, nodeFactory, this); + newRootSource = extractLeftMax.getOriginalTarget(); + left = extractLeftMax.getChangedRoot(); + } else { + BstMutationResult<K, N> extractRightMin = extractMin(right, nodeFactory, this); + newRootSource = extractRightMin.getOriginalTarget(); + right = extractRightMin.getChangedRoot(); + } + return nodeFactory.createNode(newRootSource, left, right); + } + }; + } + + /** + * Returns a balance policy that makes no assumptions on the relative balance of the two sides + * and performs a full rebalancing as necessary. Both {@code balance} and {@code combine} take + * {@code O(log n)} time. + */ + public static <K, N extends BstNode<K, N>> BstBalancePolicy<N> fullRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + final BstBalancePolicy<N> singleBalancePolicy = + BstCountBasedBalancePolicies.<K, N>singleRebalancePolicy(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + if (left == null) { + return insertMin(right, source, nodeFactory, singleBalancePolicy); + } else if (right == null) { + return insertMax(left, source, nodeFactory, singleBalancePolicy); + } + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (SINGLE_ROTATE_RATIO * countL <= countR) { + N resultLeft = balance(nodeFactory, source, left, right.childOrNull(LEFT)); + return singleBalancePolicy.balance( + nodeFactory, right, resultLeft, right.childOrNull(RIGHT)); + } else if (SINGLE_ROTATE_RATIO * countR <= countL) { + N resultRight = balance(nodeFactory, source, left.childOrNull(RIGHT), right); + return singleBalancePolicy.balance( + nodeFactory, left, left.childOrNull(LEFT), resultRight); + } else { + return nodeFactory.createNode(source, left, right); + } + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (SINGLE_ROTATE_RATIO * countL <= countR) { + N resultLeft = combine(nodeFactory, left, right.childOrNull(LEFT)); + return singleBalancePolicy.balance( + nodeFactory, right, resultLeft, right.childOrNull(RIGHT)); + } else if (SINGLE_ROTATE_RATIO * countR <= countL) { + N resultRight = combine(nodeFactory, left.childOrNull(RIGHT), right); + return singleBalancePolicy.balance( + nodeFactory, left, left.childOrNull(LEFT), resultRight); + } else { + return singleBalancePolicy.combine(nodeFactory, left, right); + } + } + }; + } +} diff --git a/guava/src/com/google/common/collect/BstInOrderPath.java b/guava/src/com/google/common/collect/BstInOrderPath.java new file mode 100644 index 0000000..de14c39 --- /dev/null +++ b/guava/src/com/google/common/collect/BstInOrderPath.java @@ -0,0 +1,126 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; +import com.google.common.base.Optional; + +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +/** + * A {@code BstPath} supporting inorder traversal operations. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstInOrderPath<N extends BstNode<?, N>> extends BstPath<N, BstInOrderPath<N>> { + /** + * The factory to use to construct {@code BstInOrderPath} values. + */ + public static <N extends BstNode<?, N>> BstPathFactory<N, BstInOrderPath<N>> inOrderFactory() { + return new BstPathFactory<N, BstInOrderPath<N>>() { + @Override + public BstInOrderPath<N> extension(BstInOrderPath<N> path, BstSide side) { + return BstInOrderPath.extension(path, side); + } + + @Override + public BstInOrderPath<N> initialPath(N root) { + return new BstInOrderPath<N>(root, null, null); + } + }; + } + + private static <N extends BstNode<?, N>> BstInOrderPath<N> extension( + BstInOrderPath<N> path, BstSide side) { + checkNotNull(path); + N tip = path.getTip(); + return new BstInOrderPath<N>(tip.getChild(side), side, path); + } + + private final BstSide sideExtension; + private transient Optional<BstInOrderPath<N>> prevInOrder; + private transient Optional<BstInOrderPath<N>> nextInOrder; + + private BstInOrderPath( + N tip, @Nullable BstSide sideExtension, @Nullable BstInOrderPath<N> tail) { + super(tip, tail); + this.sideExtension = sideExtension; + assert (sideExtension == null) == (tail == null); + } + + private Optional<BstInOrderPath<N>> computeNextInOrder(BstSide side) { + if (getTip().hasChild(side)) { + BstInOrderPath<N> path = extension(this, side); + BstSide otherSide = side.other(); + while (path.getTip().hasChild(otherSide)) { + path = extension(path, otherSide); + } + return Optional.of(path); + } else { + BstInOrderPath<N> current = this; + while (current.sideExtension == side) { + current = current.getPrefix(); + } + current = current.prefixOrNull(); + return Optional.fromNullable(current); + } + } + + private Optional<BstInOrderPath<N>> nextInOrder(BstSide side) { + Optional<BstInOrderPath<N>> result; + switch (side) { + case LEFT: + result = prevInOrder; + return (result == null) ? prevInOrder = computeNextInOrder(side) : result; + case RIGHT: + result = nextInOrder; + return (result == null) ? nextInOrder = computeNextInOrder(side) : result; + default: + throw new AssertionError(); + } + } + + /** + * Returns {@code true} if there is a next path in an in-order traversal in the given direction. + */ + public boolean hasNext(BstSide side) { + return nextInOrder(side).isPresent(); + } + + /** + * Returns the next path in an in-order traversal in the given direction. + * + * @throws NoSuchElementException if this would be the last path in an in-order traversal + */ + public BstInOrderPath<N> next(BstSide side) { + if (!hasNext(side)) { + throw new NoSuchElementException(); + } + return nextInOrder(side).get(); + } + + /** + * Returns the direction this path went in relative to its tail path, or {@code null} if this + * path has no tail. + */ + public BstSide getSideOfExtension() { + return sideExtension; + } +} diff --git a/guava/src/com/google/common/collect/BstModificationResult.java b/guava/src/com/google/common/collect/BstModificationResult.java new file mode 100644 index 0000000..2c7c036 --- /dev/null +++ b/guava/src/com/google/common/collect/BstModificationResult.java @@ -0,0 +1,74 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * The result of a {@code BstModifier}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstModificationResult<N extends BstNode<?, N>> { + enum ModificationType { + IDENTITY, REBUILDING_CHANGE, REBALANCING_CHANGE; + } + + static <N extends BstNode<?, N>> BstModificationResult<N> identity(@Nullable N target) { + return new BstModificationResult<N>(target, target, ModificationType.IDENTITY); + } + + static <N extends BstNode<?, N>> BstModificationResult<N> rebuildingChange( + @Nullable N originalTarget, @Nullable N changedTarget) { + return new BstModificationResult<N>( + originalTarget, changedTarget, ModificationType.REBUILDING_CHANGE); + } + + static <N extends BstNode<?, N>> BstModificationResult<N> rebalancingChange( + @Nullable N originalTarget, @Nullable N changedTarget) { + return new BstModificationResult<N>( + originalTarget, changedTarget, ModificationType.REBALANCING_CHANGE); + } + + @Nullable private final N originalTarget; + @Nullable private final N changedTarget; + private final ModificationType type; + + private BstModificationResult( + @Nullable N originalTarget, @Nullable N changedTarget, ModificationType type) { + this.originalTarget = originalTarget; + this.changedTarget = changedTarget; + this.type = checkNotNull(type); + } + + @Nullable + N getOriginalTarget() { + return originalTarget; + } + + @Nullable + N getChangedTarget() { + return changedTarget; + } + + ModificationType getType() { + return type; + } +} diff --git a/guava/src/com/google/common/collect/BstModifier.java b/guava/src/com/google/common/collect/BstModifier.java new file mode 100644 index 0000000..d972800 --- /dev/null +++ b/guava/src/com/google/common/collect/BstModifier.java @@ -0,0 +1,49 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A specification for a local change to an entry in a binary search tree. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface BstModifier<K, N extends BstNode<K, N>> { + + /** + * Given a target key and the original entry (if any) with the specified key, returns the entry + * with key {@code key} after this mutation has been performed. The result must either be {@code + * null} or must have a key that compares as equal to {@code key}. A deletion operation, for + * example, would always return {@code null}, or an insertion operation would always return a + * non-null {@code insertedEntry}. + * + * <p>If this method returns a non-null entry of type {@code N}, any children it has will be + * ignored. + * + * <p>This method may return {@code originalEntry} itself to indicate that no change is made. + * + * @param key The key being targeted for modification. + * @param originalEntry The original entry in the binary search tree with the specified key, if + * any. No guarantees are made about the children of this entry when treated as a node; in + * particular, they are not necessarily the children of the corresponding node in the + * binary search tree. + * @return the entry (if any) with the specified key after this modification is performed + */ + BstModificationResult<N> modify(K key, @Nullable N originalEntry); +} diff --git a/guava/src/com/google/common/collect/BstMutationResult.java b/guava/src/com/google/common/collect/BstMutationResult.java new file mode 100644 index 0000000..68309a8 --- /dev/null +++ b/guava/src/com/google/common/collect/BstMutationResult.java @@ -0,0 +1,156 @@ +/* + * 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.BstModificationResult.ModificationType.IDENTITY; +import static com.google.common.collect.BstModificationResult.ModificationType.REBUILDING_CHANGE; +import static com.google.common.collect.BstModificationResult.ModificationType.REBALANCING_CHANGE; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.BstModificationResult.ModificationType; + +import javax.annotation.Nullable; + +/** + * The result of a mutation operation performed at a single location in a binary search tree. + * + * @author Louis Wasserman + * @param <K> The key type of the nodes in the modified binary search tree. + * @param <N> The type of the nodes in the modified binary search tree. + */ +@GwtCompatible +final class BstMutationResult<K, N extends BstNode<K, N>> { + /** + * Creates a {@code BstMutationResult}. + * + * @param targetKey The key targeted for modification. If {@code originalTarget} or {@code + * changedTarget} are non-null, their keys must compare as equal to {@code targetKey}. + * @param originalRoot The root of the subtree that was modified. + * @param changedRoot The root of the subtree, after the modification and any rebalancing. + * @param modificationResult The result of the local modification to an entry. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutationResult( + @Nullable K targetKey, @Nullable N originalRoot, @Nullable N changedRoot, + BstModificationResult<N> modificationResult) { + return new BstMutationResult<K, N>(targetKey, originalRoot, changedRoot, modificationResult); + } + + private final K targetKey; + + @Nullable + private N originalRoot; + + @Nullable + private N changedRoot; + + private final BstModificationResult<N> modificationResult; + + private BstMutationResult(@Nullable K targetKey, @Nullable N originalRoot, + @Nullable N changedRoot, BstModificationResult<N> modificationResult) { + this.targetKey = targetKey; + this.originalRoot = originalRoot; + this.changedRoot = changedRoot; + this.modificationResult = checkNotNull(modificationResult); + } + + /** + * Returns the key which was the target of this modification. + */ + public K getTargetKey() { + return targetKey; + } + + /** + * Returns the root of the subtree that was modified. + */ + @Nullable + public N getOriginalRoot() { + return originalRoot; + } + + /** + * Returns the root of the subtree, after the modification and any rebalancing was performed. + */ + @Nullable + public N getChangedRoot() { + return changedRoot; + } + + /** + * Returns the entry in the original subtree with key {@code targetKey}, if any. This should not + * be treated as a subtree, but only as an entry, and no guarantees are made about its children + * when viewed as a subtree. + */ + @Nullable + public N getOriginalTarget() { + return modificationResult.getOriginalTarget(); + } + + /** + * Returns the result of the modification to {@link #getOriginalTarget()}. This should not be + * treated as a subtree, but only as an entry, and no guarantees are made about its children when + * viewed as a subtree. + */ + @Nullable + public N getChangedTarget() { + return modificationResult.getChangedTarget(); + } + + ModificationType modificationType() { + return modificationResult.getType(); + } + + /** + * If this mutation was to an immediate child subtree of the specified root on the specified + * side, returns the {@code BstMutationResult} of applying the mutation to the appropriate child + * of the specified root and rebalancing using the specified mutation rule. + */ + public BstMutationResult<K, N> lift(N liftOriginalRoot, BstSide side, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + assert liftOriginalRoot != null & side != null & nodeFactory != null & balancePolicy != null; + switch (modificationType()) { + case IDENTITY: + this.originalRoot = this.changedRoot = liftOriginalRoot; + return this; + case REBUILDING_CHANGE: + case REBALANCING_CHANGE: + this.originalRoot = liftOriginalRoot; + N resultLeft = liftOriginalRoot.childOrNull(LEFT); + N resultRight = liftOriginalRoot.childOrNull(RIGHT); + switch (side) { + case LEFT: + resultLeft = changedRoot; + break; + case RIGHT: + resultRight = changedRoot; + break; + default: + throw new AssertionError(); + } + if (modificationType() == REBUILDING_CHANGE) { + this.changedRoot = nodeFactory.createNode(liftOriginalRoot, resultLeft, resultRight); + } else { + this.changedRoot = + balancePolicy.balance(nodeFactory, liftOriginalRoot, resultLeft, resultRight); + } + return this; + default: + throw new AssertionError(); + } + } +} diff --git a/guava/src/com/google/common/collect/BstMutationRule.java b/guava/src/com/google/common/collect/BstMutationRule.java new file mode 100644 index 0000000..7f90f8c --- /dev/null +++ b/guava/src/com/google/common/collect/BstMutationRule.java @@ -0,0 +1,77 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +/** + * A rule for a local mutation to a binary search tree, that changes at most one entry. In addition + * to specifying how it modifies a particular entry via a {@code BstModifier}, it specifies a + * {@link BstBalancePolicy} for rebalancing the tree after the modification is performed and a + * {@link BstNodeFactory} for constructing newly rebalanced nodes. + * + * @author Louis Wasserman + * @param <K> The key type of the nodes in binary search trees that this rule can modify. + * @param <N> The type of the nodes in binary search trees that this rule can modify. + */ +@GwtCompatible +final class BstMutationRule<K, N extends BstNode<K, N>> { + /** + * Constructs a {@code BstMutationRule} with the specified modifier, balance policy, and node + * factory. + */ + public static <K, N extends BstNode<K, N>> BstMutationRule<K, N> createRule( + BstModifier<K, N> modifier, BstBalancePolicy<N> balancePolicy, + BstNodeFactory<N> nodeFactory) { + return new BstMutationRule<K, N>(modifier, balancePolicy, nodeFactory); + } + + private final BstModifier<K, N> modifier; + private final BstBalancePolicy<N> balancePolicy; + private final BstNodeFactory<N> nodeFactory; + + private BstMutationRule(BstModifier<K, N> modifier, BstBalancePolicy<N> balancePolicy, + BstNodeFactory<N> nodeFactory) { + this.balancePolicy = checkNotNull(balancePolicy); + this.nodeFactory = checkNotNull(nodeFactory); + this.modifier = checkNotNull(modifier); + } + + /** + * Returns the {@link BstModifier} that specifies the change to a targeted entry in a binary + * search tree. + */ + public BstModifier<K, N> getModifier() { + return modifier; + } + + /** + * Returns the policy used to rebalance nodes in the tree after this modification has been + * performed. + */ + public BstBalancePolicy<N> getBalancePolicy() { + return balancePolicy; + } + + /** + * Returns the node factory used to create new nodes in the tree after this modification has been + * performed. + */ + public BstNodeFactory<N> getNodeFactory() { + return nodeFactory; + } +} diff --git a/guava/src/com/google/common/collect/BstNode.java b/guava/src/com/google/common/collect/BstNode.java new file mode 100644 index 0000000..818f85a --- /dev/null +++ b/guava/src/com/google/common/collect/BstNode.java @@ -0,0 +1,125 @@ +/* + * 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.base.Preconditions.checkState; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +/** + * A reusable abstraction for a node in a binary search tree. Null keys are allowed. + * + * <p>The node is considered to be immutable. Any subclass with mutable fields must create a new + * {@code BstNode} object upon any mutation, as the {@code Bst} classes assume that two nodes + * {@code a} and {@code b} represent exactly the same tree if and only if {@code a == b}. + * + * <p>A {@code BstNode} can be considered to be an <i>entry</i>, containing a key and possibly some + * value data, or it can be considered to be a <i>subtree</i>, representative of it and all its + * descendants. + * + * @author Louis Wasserman + * @param <K> The key type associated with this tree. + * @param <N> The type of the nodes in this tree. + */ +@GwtCompatible +class BstNode<K, N extends BstNode<K, N>> { + /** + * The key on which this binary search tree is ordered. All descendants of the left subtree of + * this node must have keys strictly less than {@code this.key}. + */ + private final K key; + + /** + * The left child of this node. A null value indicates that this node has no left child. + */ + @Nullable + private final N left; + + /** + * The right child of this node. A null value indicates that this node has no right child. + */ + @Nullable + private final N right; + + BstNode(@Nullable K key, @Nullable N left, @Nullable N right) { + this.key = key; + this.left = left; + this.right = right; + } + + /** + * Returns the ordered key associated with this node. + */ + @Nullable + public final K getKey() { + return key; + } + + /** + * Returns the child on the specified side, or {@code null} if there is no such child. + */ + @Nullable + public final N childOrNull(BstSide side) { + switch (side) { + case LEFT: + return left; + case RIGHT: + return right; + default: + throw new AssertionError(); + } + } + + /** + * Returns {@code true} if this node has a child on the specified side. + */ + public final boolean hasChild(BstSide side) { + return childOrNull(side) != null; + } + + /** + * Returns this node's child on the specified side. + * + * @throws IllegalStateException if this node has no such child + */ + public final N getChild(BstSide side) { + N child = childOrNull(side); + checkState(child != null); + return child; + } + + /** + * Returns {@code true} if the traditional binary search tree ordering invariant holds with + * respect to the specified {@code comparator}. + */ + protected final boolean orderingInvariantHolds(Comparator<? super K> comparator) { + checkNotNull(comparator); + boolean result = true; + if (hasChild(LEFT)) { + result &= comparator.compare(getChild(LEFT).getKey(), key) < 0; + } + if (hasChild(RIGHT)) { + result &= comparator.compare(getChild(RIGHT).getKey(), key) > 0; + } + return result; + } +} diff --git a/guava/src/com/google/common/collect/BstNodeFactory.java b/guava/src/com/google/common/collect/BstNodeFactory.java new file mode 100644 index 0000000..8e1476c --- /dev/null +++ b/guava/src/com/google/common/collect/BstNodeFactory.java @@ -0,0 +1,46 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A factory for copying nodes in binary search trees with different children. + * + * <p>Typically, nodes will carry more information than the fields in the {@link BstNode} class, + * often some kind of value or some aggregate data for the subtree. This factory is responsible for + * copying this additional data between nodes. + * + * @author Louis Wasserman + * @param <N> The type of the tree nodes constructed with this {@code BstNodeFactory}. + */ +@GwtCompatible +abstract class BstNodeFactory<N extends BstNode<?, N>> { + /** + * Returns a new {@code N} with the key and value data from {@code source}, with left child + * {@code left}, and right child {@code right}. If {@code left} or {@code right} is null, the + * returned node will not have a child on the corresponding side. + */ + public abstract N createNode(N source, @Nullable N left, @Nullable N right); + + /** + * Returns a new {@code N} with the key and value data from {@code source} that is a leaf. + */ + public final N createLeaf(N source) { + return createNode(source, null, null); + } +} diff --git a/guava/src/com/google/common/collect/BstOperations.java b/guava/src/com/google/common/collect/BstOperations.java new file mode 100644 index 0000000..1f933d4 --- /dev/null +++ b/guava/src/com/google/common/collect/BstOperations.java @@ -0,0 +1,228 @@ +/* + * 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.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +/** + * Tools to perform single-key queries and mutations in binary search trees. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstOperations { + private BstOperations() {} + + /** + * Returns the node with key {@code key} in {@code tree}, if any. + */ + @Nullable + public static <K, N extends BstNode<K, N>> N seek( + Comparator<? super K> comparator, @Nullable N tree, @Nullable K key) { + checkNotNull(comparator); + if (tree == null) { + return null; + } + int cmp = comparator.compare(key, tree.getKey()); + if (cmp == 0) { + return tree; + } else { + BstSide side = (cmp < 0) ? LEFT : RIGHT; + return seek(comparator, tree.childOrNull(side), key); + } + } + + /** + * Returns the result of performing the mutation specified by {@code mutationRule} in {@code + * tree} at the location with key {@code key}. + * + * <ul> + * <li>If the returned {@link BstModificationResult} has type {@code IDENTITY}, the exact + * original tree is returned. + * <li>If the returned {@code BstModificationResult} has type {@code REBUILDING_CHANGE}, + * the tree will be rebuilt with the node factory of the mutation rule, but not rebalanced. + * <li>If the returned {@code BstModificationResult} has type {@code REBALANCING_CHANGE}, + * the tree will be rebalanced using the balance policy of the mutation rule. + * </ul> + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate( + Comparator<? super K> comparator, BstMutationRule<K, N> mutationRule, @Nullable N tree, + @Nullable K key) { + checkNotNull(comparator); + checkNotNull(mutationRule); + + if (tree != null) { + int cmp = comparator.compare(key, tree.getKey()); + if (cmp != 0) { + BstSide side = (cmp < 0) ? LEFT : RIGHT; + BstMutationResult<K, N> mutation = + mutate(comparator, mutationRule, tree.childOrNull(side), key); + return mutation.lift( + tree, side, mutationRule.getNodeFactory(), mutationRule.getBalancePolicy()); + } + } + return modify(tree, key, mutationRule); + } + + /** + * Perform the local mutation at the tip of the specified path. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate( + BstInOrderPath<N> path, BstMutationRule<K, N> mutationRule) { + checkNotNull(path); + checkNotNull(mutationRule); + BstBalancePolicy<N> balancePolicy = mutationRule.getBalancePolicy(); + BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory(); + BstModifier<K, N> modifier = mutationRule.getModifier(); + + N target = path.getTip(); + K key = target.getKey(); + BstMutationResult<K, N> result = modify(target, key, mutationRule); + while (path.hasPrefix()) { + BstInOrderPath<N> prefix = path.getPrefix(); + result = result.lift(prefix.getTip(), path.getSideOfExtension(), nodeFactory, balancePolicy); + path = prefix; + } + return result; + } + + /** + * Perform the local mutation right here, at the specified node. + */ + private static <K, N extends BstNode<K, N>> BstMutationResult<K, N> modify( + @Nullable N tree, K key, BstMutationRule<K, N> mutationRule) { + BstBalancePolicy<N> rebalancePolicy = mutationRule.getBalancePolicy(); + BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory(); + BstModifier<K, N> modifier = mutationRule.getModifier(); + + N originalRoot = tree; + N changedRoot; + N originalTarget = (tree == null) ? null : nodeFactory.createLeaf(tree); + BstModificationResult<N> modResult = modifier.modify(key, originalTarget); + N originalLeft = null; + N originalRight = null; + if (tree != null) { + originalLeft = tree.childOrNull(LEFT); + originalRight = tree.childOrNull(RIGHT); + } + switch (modResult.getType()) { + case IDENTITY: + changedRoot = tree; + break; + case REBUILDING_CHANGE: + if (modResult.getChangedTarget() != null) { + changedRoot = + nodeFactory.createNode(modResult.getChangedTarget(), originalLeft, originalRight); + } else if (tree == null) { + changedRoot = null; + } else { + throw new AssertionError( + "Modification result is a REBUILDING_CHANGE, but rebalancing required"); + } + break; + case REBALANCING_CHANGE: + if (modResult.getChangedTarget() != null) { + changedRoot = rebalancePolicy.balance( + nodeFactory, modResult.getChangedTarget(), originalLeft, originalRight); + } else if (tree != null) { + changedRoot = rebalancePolicy.combine(nodeFactory, originalLeft, originalRight); + } else { + changedRoot = null; + } + break; + default: + throw new AssertionError(); + } + return BstMutationResult.mutationResult(key, originalRoot, changedRoot, modResult); + } + + /** + * Returns the result of removing the minimum element from the specified subtree. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMin( + N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(root); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root.hasChild(LEFT)) { + BstMutationResult<K, N> subResult = + extractMin(root.getChild(LEFT), nodeFactory, balancePolicy); + return subResult.lift(root, LEFT, nodeFactory, balancePolicy); + } + return BstMutationResult.mutationResult( + root.getKey(), root, root.childOrNull(RIGHT), + BstModificationResult.rebalancingChange(root, null)); + } + + /** + * Returns the result of removing the maximum element from the specified subtree. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMax( + N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(root); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root.hasChild(RIGHT)) { + BstMutationResult<K, N> subResult = + extractMax(root.getChild(RIGHT), nodeFactory, balancePolicy); + return subResult.lift(root, RIGHT, nodeFactory, balancePolicy); + } + return BstMutationResult.mutationResult(root.getKey(), root, root.childOrNull(LEFT), + BstModificationResult.rebalancingChange(root, null)); + } + + /** + * Inserts the specified entry into the tree as the minimum entry. Assumes that {@code + * entry.getKey()} is less than the key of all nodes in the subtree {@code root}. + */ + public static <N extends BstNode<?, N>> N insertMin(@Nullable N root, N entry, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(entry); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root == null) { + return nodeFactory.createLeaf(entry); + } else { + return balancePolicy.balance(nodeFactory, root, + insertMin(root.childOrNull(LEFT), entry, nodeFactory, balancePolicy), + root.childOrNull(RIGHT)); + } + } + + /** + * Inserts the specified entry into the tree as the maximum entry. Assumes that {@code + * entry.getKey()} is greater than the key of all nodes in the subtree {@code root}. + */ + public static <N extends BstNode<?, N>> N insertMax(@Nullable N root, N entry, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(entry); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root == null) { + return nodeFactory.createLeaf(entry); + } else { + return balancePolicy.balance(nodeFactory, root, root.childOrNull(LEFT), + insertMax(root.childOrNull(RIGHT), entry, nodeFactory, balancePolicy)); + } + } +} diff --git a/guava/src/com/google/common/collect/BstPath.java b/guava/src/com/google/common/collect/BstPath.java new file mode 100644 index 0000000..dd564c7 --- /dev/null +++ b/guava/src/com/google/common/collect/BstPath.java @@ -0,0 +1,74 @@ +/* + * 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.base.Preconditions.checkState; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A path to a node in a binary search tree, originating at the root. + * + * @author Louis Wasserman + * @param <N> The type of nodes in this binary search tree. + * @param <P> This path type, and the path type of all suffix paths. + */ +@GwtCompatible +abstract class BstPath<N extends BstNode<?, N>, P extends BstPath<N, P>> { + private final N tip; + @Nullable + private final P prefix; + + BstPath(N tip, @Nullable P prefix) { + this.tip = checkNotNull(tip); + this.prefix = prefix; + } + + /** + * Return the end of this {@code BstPath}, the deepest node in the path. + */ + public final N getTip() { + return tip; + } + + /** + * Returns {@code true} if this path has a prefix. + */ + public final boolean hasPrefix() { + return prefix != null; + } + + /** + * Returns the prefix of this path, which reaches to the parent of the end of this path. Returns + * {@code null} if this path has no prefix. + */ + @Nullable + public final P prefixOrNull() { + return prefix; + } + + /** + * Returns the prefix of this path, which reaches to the parent of the end of this path. + * + * @throws IllegalStateException if this path has no prefix. + */ + public final P getPrefix() { + checkState(hasPrefix()); + return prefix; + } +} diff --git a/guava/src/com/google/common/collect/BstPathFactory.java b/guava/src/com/google/common/collect/BstPathFactory.java new file mode 100644 index 0000000..92086ae --- /dev/null +++ b/guava/src/com/google/common/collect/BstPathFactory.java @@ -0,0 +1,38 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +/** + * A factory for extending paths in a binary search tree. + * + * @author Louis Wasserman + * @param <N> The type of binary search tree nodes used in the paths generated by this {@code + * BstPathFactory}. + * @param <P> The type of paths constructed by this {@code BstPathFactory}. + */ +@GwtCompatible +interface BstPathFactory<N extends BstNode<?, N>, P extends BstPath<N, P>> { + /** + * Returns this path extended by one node to the specified {@code side}. + */ + P extension(P path, BstSide side); + + /** + * Returns the trivial path that starts at {@code root} and goes no further. + */ + P initialPath(N root); +} diff --git a/guava/src/com/google/common/collect/BstRangeOps.java b/guava/src/com/google/common/collect/BstRangeOps.java new file mode 100644 index 0000000..10d5931 --- /dev/null +++ b/guava/src/com/google/common/collect/BstRangeOps.java @@ -0,0 +1,175 @@ +/* + * 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.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A utility class with operations on binary search trees that operate on some interval. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstRangeOps { + /** + * Returns the total value of the specified aggregation function on the specified tree restricted + * to the specified range. Assumes that the tree satisfies the binary search ordering property + * relative to {@code range.comparator()}. + */ + public static <K, N extends BstNode<K, N>> long totalInRange( + BstAggregate<? super N> aggregate, GeneralRange<K> range, @Nullable N root) { + checkNotNull(aggregate); + checkNotNull(range); + if (root == null || range.isEmpty()) { + return 0; + } + long total = aggregate.treeValue(root); + if (range.hasLowerBound()) { + total -= totalBeyondRangeToSide(aggregate, range, LEFT, root); + } + if (range.hasUpperBound()) { + total -= totalBeyondRangeToSide(aggregate, range, RIGHT, root); + } + return total; + } + + // Returns total value strictly to the specified side of the specified range. + private static <K, N extends BstNode<K, N>> long totalBeyondRangeToSide( + BstAggregate<? super N> aggregate, GeneralRange<K> range, BstSide side, @Nullable N root) { + long accum = 0; + while (root != null) { + if (beyond(range, root.getKey(), side)) { + accum += aggregate.entryValue(root); + accum += aggregate.treeValue(root.childOrNull(side)); + root = root.childOrNull(side.other()); + } else { + root = root.childOrNull(side); + } + } + return accum; + } + + /** + * Returns a balanced tree containing all nodes from the specified tree that were <i>not</i> in + * the specified range, using the specified balance policy. Assumes that the tree satisfies the + * binary search ordering property relative to {@code range.comparator()}. + */ + @Nullable + public static <K, N extends BstNode<K, N>> N minusRange(GeneralRange<K> range, + BstBalancePolicy<N> balancePolicy, BstNodeFactory<N> nodeFactory, @Nullable N root) { + checkNotNull(range); + checkNotNull(balancePolicy); + checkNotNull(nodeFactory); + N higher = range.hasUpperBound() + ? subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, RIGHT, root) + : null; + N lower = range.hasLowerBound() + ? subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, LEFT, root) + : null; + return balancePolicy.combine(nodeFactory, lower, higher); + } + + /* + * Returns a balanced tree containing all nodes in the specified tree that are strictly to the + * specified side of the specified range. + */ + @Nullable + private static <K, N extends BstNode<K, N>> N subTreeBeyondRangeToSide(GeneralRange<K> range, + BstBalancePolicy<N> balancePolicy, BstNodeFactory<N> nodeFactory, BstSide side, + @Nullable N root) { + if (root == null) { + return null; + } + if (beyond(range, root.getKey(), side)) { + N left = root.childOrNull(LEFT); + N right = root.childOrNull(RIGHT); + switch (side) { + case LEFT: + right = subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, LEFT, right); + break; + case RIGHT: + left = subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, RIGHT, left); + break; + default: + throw new AssertionError(); + } + return balancePolicy.balance(nodeFactory, root, left, right); + } else { + return subTreeBeyondRangeToSide( + range, balancePolicy, nodeFactory, side, root.childOrNull(side)); + } + } + + /** + * Returns the furthest path to the specified side in the specified tree that falls into the + * specified range. + */ + @Nullable + public static <K, N extends BstNode<K, N>, P extends BstPath<N, P>> P furthestPath( + GeneralRange<K> range, BstSide side, BstPathFactory<N, P> pathFactory, @Nullable N root) { + checkNotNull(range); + checkNotNull(pathFactory); + checkNotNull(side); + if (root == null) { + return null; + } + P path = pathFactory.initialPath(root); + return furthestPath(range, side, pathFactory, path); + } + + private static <K, N extends BstNode<K, N>, P extends BstPath<N, P>> P furthestPath( + GeneralRange<K> range, BstSide side, BstPathFactory<N, P> pathFactory, P currentPath) { + N tip = currentPath.getTip(); + K tipKey = tip.getKey(); + if (beyond(range, tipKey, side)) { + if (tip.hasChild(side.other())) { + currentPath = pathFactory.extension(currentPath, side.other()); + return furthestPath(range, side, pathFactory, currentPath); + } else { + return null; + } + } else if (tip.hasChild(side)) { + P alphaPath = pathFactory.extension(currentPath, side); + alphaPath = furthestPath(range, side, pathFactory, alphaPath); + if (alphaPath != null) { + return alphaPath; + } + } + return beyond(range, tipKey, side.other()) ? null : currentPath; + } + + /** + * Returns {@code true} if {@code key} is beyond the specified side of the specified range. + */ + public static <K> boolean beyond(GeneralRange<K> range, @Nullable K key, BstSide side) { + checkNotNull(range); + switch (side) { + case LEFT: + return range.tooLow(key); + case RIGHT: + return range.tooHigh(key); + default: + throw new AssertionError(); + } + } + + private BstRangeOps() {} +} diff --git a/guava/src/com/google/common/collect/BstSide.java b/guava/src/com/google/common/collect/BstSide.java new file mode 100644 index 0000000..5dec1bc --- /dev/null +++ b/guava/src/com/google/common/collect/BstSide.java @@ -0,0 +1,40 @@ +/* + * 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 com.google.common.annotations.GwtCompatible; + +/** + * A side of a binary search tree node, used to index its children. + * + * @author Louis Wasserman + */ +@GwtCompatible +enum BstSide { + LEFT { + @Override + public BstSide other() { + return RIGHT; + } + }, + RIGHT { + @Override + public BstSide other() { + return LEFT; + } + }; + + abstract BstSide other(); +} diff --git a/guava/src/com/google/common/collect/CartesianList.java b/guava/src/com/google/common/collect/CartesianList.java deleted file mode 100644 index 62f9227..0000000 --- a/guava/src/com/google/common/collect/CartesianList.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2012 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.checkElementIndex; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.math.IntMath; - -import java.util.AbstractList; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Lists#cartesianProduct(List)}. - * - * @author Louis Wasserman - */ -@GwtCompatible -final class CartesianList<E> extends AbstractList<List<E>> { - - private transient final ImmutableList<List<E>> axes; - private transient final int[] axesSizeProduct; - - static <E> List<List<E>> create(List<? extends List<? extends E>> lists) { - ImmutableList.Builder<List<E>> axesBuilder = - new ImmutableList.Builder<List<E>>(lists.size()); - for (List<? extends E> list : lists) { - List<E> copy = ImmutableList.copyOf(list); - if (copy.isEmpty()) { - return ImmutableList.of(); - } - axesBuilder.add(copy); - } - return new CartesianList<E>(axesBuilder.build()); - } - - CartesianList(ImmutableList<List<E>> axes) { - this.axes = axes; - int[] axesSizeProduct = new int[axes.size() + 1]; - axesSizeProduct[axes.size()] = 1; - try { - for (int i = axes.size() - 1; i >= 0; i--) { - axesSizeProduct[i] = - IntMath.checkedMultiply(axesSizeProduct[i + 1], axes.get(i).size()); - } - } catch (ArithmeticException e) { - throw new IllegalArgumentException( - "Cartesian product too large; must have size at most Integer.MAX_VALUE"); - } - this.axesSizeProduct = axesSizeProduct; - } - - private int getAxisIndexForProductIndex(int index, int axis) { - return (index / axesSizeProduct[axis + 1]) % axes.get(axis).size(); - } - - @Override - public ImmutableList<E> get(final int index) { - checkElementIndex(index, size()); - return new ImmutableList<E>() { - - @Override - public int size() { - return axes.size(); - } - - @Override - public E get(int axis) { - checkElementIndex(axis, size()); - int axisIndex = getAxisIndexForProductIndex(index, axis); - return axes.get(axis).get(axisIndex); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return axesSizeProduct[0]; - } - - @Override - public boolean contains(@Nullable Object o) { - if (!(o instanceof List)) { - return false; - } - List<?> list = (List<?>) o; - if (list.size() != axes.size()) { - return false; - } - ListIterator<?> itr = list.listIterator(); - while (itr.hasNext()) { - int index = itr.nextIndex(); - if (!axes.get(index).contains(itr.next())) { - return false; - } - } - return true; - } - - @Override - public int indexOf(Object o) { - if (!(o instanceof List)) { - return -1; - } - List<?> l = (List<?>) o; - if (l.size() != axes.size()) { - return -1; - } - Iterator<?> lIterator = l.iterator(); - int i = 0; - for (List<E> axis : axes) { - Object lElement = lIterator.next(); - int axisIndex = axis.indexOf(lElement); - if (axisIndex == -1) { - return -1; - } - i = (i * axis.size()) + axisIndex; - } - return i; - } - - @Override - public int lastIndexOf(Object o) { - if (!(o instanceof List)) { - return -1; - } - List<?> l = (List<?>) o; - if (l.size() != axes.size()) { - return -1; - } - Iterator<?> lIterator = l.iterator(); - int i = 0; - for (List<E> axis : axes) { - Object lElement = lIterator.next(); - int axisIndex = axis.lastIndexOf(lElement); - if (axisIndex == -1) { - return -1; - } - i = (i * axis.size()) + axisIndex; - } - return i; - } -} diff --git a/guava/src/com/google/common/collect/ClassToInstanceMap.java b/guava/src/com/google/common/collect/ClassToInstanceMap.java index b3f535c..6b6fb5b 100644 --- a/guava/src/com/google/common/collect/ClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/ClassToInstanceMap.java @@ -31,13 +31,6 @@ import javax.annotation.Nullable; * <p>Like any other {@code Map<Class, Object>}, this map may contain entries * for primitive types, and a primitive type and its corresponding wrapper type * may map to different values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#ClassToInstanceMap"> - * {@code ClassToInstanceMap}</a>. - * - * <p>To map a generic type to an instance of that type, use {@link - * com.google.common.reflect.TypeToInstanceMap} instead. * * @param <B> the common supertype that all entries must share; often this is * simply {@link Object} diff --git a/guava/src/com/google/common/collect/Collections2.java b/guava/src/com/google/common/collect/Collections2.java index 7805e0b..603fa8b 100644 --- a/guava/src/com/google/common/collect/Collections2.java +++ b/guava/src/com/google/common/collect/Collections2.java @@ -18,27 +18,21 @@ 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.math.LongMath.binomial; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; -import com.google.common.math.IntMath; import com.google.common.primitives.Ints; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; - /** * Provides static methods for working with {@code Collection} instances. * @@ -95,33 +89,13 @@ public final class Collections2 { /** * Delegates to {@link Collection#contains}. Returns {@code false} if the - * {@code contains} method throws a {@code ClassCastException} or - * {@code NullPointerException}. + * {@code contains} method throws a {@code ClassCastException}. */ static boolean safeContains(Collection<?> collection, Object object) { - checkNotNull(collection); try { return collection.contains(object); } catch (ClassCastException e) { return false; - } catch (NullPointerException e) { - return false; - } - } - - /** - * Delegates to {@link Collection#remove}. Returns {@code false} if the - * {@code remove} method throws a {@code ClassCastException} or - * {@code NullPointerException}. - */ - static boolean safeRemove(Collection<?> collection, Object object) { - checkNotNull(collection); - try { - return collection.remove(object); - } catch (ClassCastException e) { - return false; - } catch (NullPointerException e) { - return false; } } @@ -163,9 +137,6 @@ public final class Collections2 { @Override public boolean contains(Object element) { try { - // TODO(user): consider doing the predicate after unfiltered.contains, - // which would reduce the risk of CCE here - // unsafe cast can result in a CCE from predicate.apply(), which we // will catch @SuppressWarnings("unchecked") @@ -207,9 +178,6 @@ public final class Collections2 { @Override public boolean remove(Object element) { try { - // TODO(user): consider doing the predicate after unfiltered.contains, - // which would reduce the risk of CCE here - // unsafe cast can result in a CCE from predicate.apply(), which we // will catch @SuppressWarnings("unchecked") @@ -373,357 +341,8 @@ public final class Collections2 { return (Collection<T>) iterable; } - static final Joiner STANDARD_JOINER = Joiner.on(", ").useForNull("null"); - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Iterable}. - * - * <p><i>Notes:</i> This is an implementation of the algorithm for - * Lexicographical Permutations Generation, described in Knuth's "The Art of - * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The - * iteration order follows the lexicographical order. This means that - * the first permutation will be in ascending order, and the last will be in - * descending order. - * - * <p>Duplicate elements are considered equal. For example, the list [1, 1] - * will have only one permutation, instead of two. This is why the elements - * have to implement {@link Comparable}. - * - * <p>An empty iterable has only one permutation, which is an empty list. - * - * <p>This method is equivalent to - * {@code Collections2.orderedPermutations(list, Ordering.natural())}. - * - * @param elements the original iterable whose elements have to be permuted. - * @return an immutable {@link Collection} containing all the different - * permutations of the original iterable. - * @throws NullPointerException if the specified iterable is null or has any - * null elements. - * @since 12.0 - */ - @Beta public static <E extends Comparable<? super E>> - Collection<List<E>> orderedPermutations(Iterable<E> elements) { - return orderedPermutations(elements, Ordering.natural()); - } - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Iterable} using the specified {@link Comparator} for establishing - * the lexicographical ordering. - * - * <p>Examples: <pre> {@code - * - * for (List<String> perm : orderedPermutations(asList("b", "c", "a"))) { - * println(perm); - * } - * // -> ["a", "b", "c"] - * // -> ["a", "c", "b"] - * // -> ["b", "a", "c"] - * // -> ["b", "c", "a"] - * // -> ["c", "a", "b"] - * // -> ["c", "b", "a"] - * - * for (List<Integer> perm : orderedPermutations(asList(1, 2, 2, 1))) { - * println(perm); - * } - * // -> [1, 1, 2, 2] - * // -> [1, 2, 1, 2] - * // -> [1, 2, 2, 1] - * // -> [2, 1, 1, 2] - * // -> [2, 1, 2, 1] - * // -> [2, 2, 1, 1]}</pre> - * - * <p><i>Notes:</i> This is an implementation of the algorithm for - * Lexicographical Permutations Generation, described in Knuth's "The Art of - * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The - * iteration order follows the lexicographical order. This means that - * the first permutation will be in ascending order, and the last will be in - * descending order. - * - * <p>Elements that compare equal are considered equal and no new permutations - * are created by swapping them. - * - * <p>An empty iterable has only one permutation, which is an empty list. - * - * @param elements the original iterable whose elements have to be permuted. - * @param comparator a comparator for the iterable's elements. - * @return an immutable {@link Collection} containing all the different - * permutations of the original iterable. - * @throws NullPointerException If the specified iterable is null, has any - * null elements, or if the specified comparator is null. - * @since 12.0 - */ - @Beta public static <E> Collection<List<E>> orderedPermutations( - Iterable<E> elements, Comparator<? super E> comparator) { - return new OrderedPermutationCollection<E>(elements, comparator); - } - - private static final class OrderedPermutationCollection<E> - extends AbstractCollection<List<E>> { - final ImmutableList<E> inputList; - final Comparator<? super E> comparator; - final int size; - - OrderedPermutationCollection(Iterable<E> input, - Comparator<? super E> comparator) { - this.inputList = Ordering.from(comparator).immutableSortedCopy(input); - this.comparator = comparator; - this.size = calculateSize(inputList, comparator); - } - - /** - * The number of permutations with repeated elements is calculated as - * follows: - * <ul> - * <li>For an empty list, it is 1 (base case).</li> - * <li>When r numbers are added to a list of n-r elements, the number of - * permutations is increased by a factor of (n choose r).</li> - * </ul> - */ - private static <E> int calculateSize( - List<E> sortedInputList, Comparator<? super E> comparator) { - long permutations = 1; - int n = 1; - int r = 1; - while (n < sortedInputList.size()) { - int comparison = comparator.compare( - sortedInputList.get(n - 1), sortedInputList.get(n)); - if (comparison < 0) { - // We move to the next non-repeated element. - permutations *= binomial(n, r); - r = 0; - if (!isPositiveInt(permutations)) { - return Integer.MAX_VALUE; - } - } - n++; - r++; - } - permutations *= binomial(n, r); - if (!isPositiveInt(permutations)) { - return Integer.MAX_VALUE; - } - return (int) permutations; - } - - @Override public int size() { - return size; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Iterator<List<E>> iterator() { - return new OrderedPermutationIterator<E>(inputList, comparator); - } - - @Override public boolean contains(@Nullable Object obj) { - if (obj instanceof List) { - List<?> list = (List<?>) obj; - return isPermutation(inputList, list); - } - return false; - } - - @Override public String toString() { - return "orderedPermutationCollection(" + inputList + ")"; - } - } - - private static final class OrderedPermutationIterator<E> - extends AbstractIterator<List<E>> { - - List<E> nextPermutation; - final Comparator<? super E> comparator; - - OrderedPermutationIterator(List<E> list, - Comparator<? super E> comparator) { - this.nextPermutation = Lists.newArrayList(list); - this.comparator = comparator; - } - - @Override protected List<E> computeNext() { - if (nextPermutation == null) { - return endOfData(); - } - ImmutableList<E> next = ImmutableList.copyOf(nextPermutation); - calculateNextPermutation(); - return next; - } - - void calculateNextPermutation() { - int j = findNextJ(); - if (j == -1) { - nextPermutation = null; - return; - } - - int l = findNextL(j); - Collections.swap(nextPermutation, j, l); - int n = nextPermutation.size(); - Collections.reverse(nextPermutation.subList(j + 1, n)); - } - - int findNextJ() { - for (int k = nextPermutation.size() - 2; k >= 0; k--) { - if (comparator.compare(nextPermutation.get(k), - nextPermutation.get(k + 1)) < 0) { - return k; - } - } - return -1; - } - - int findNextL(int j) { - E ak = nextPermutation.get(j); - for (int l = nextPermutation.size() - 1; l > j; l--) { - if (comparator.compare(ak, nextPermutation.get(l)) < 0) { - return l; - } - } - throw new AssertionError("this statement should be unreachable"); - } - } - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Collection}. - * - * <p><i>Notes:</i> This is an implementation of the Plain Changes algorithm - * for permutations generation, described in Knuth's "The Art of Computer - * Programming", Volume 4, Chapter 7, Section 7.2.1.2. - * - * <p>If the input list contains equal elements, some of the generated - * permutations will be equal. - * - * <p>An empty collection has only one permutation, which is an empty list. - * - * @param elements the original collection whose elements have to be permuted. - * @return an immutable {@link Collection} containing all the different - * permutations of the original collection. - * @throws NullPointerException if the specified collection is null or has any - * null elements. - * @since 12.0 - */ - @Beta public static <E> Collection<List<E>> permutations( - Collection<E> elements) { - return new PermutationCollection<E>(ImmutableList.copyOf(elements)); - } - - private static final class PermutationCollection<E> - extends AbstractCollection<List<E>> { - final ImmutableList<E> inputList; - - PermutationCollection(ImmutableList<E> input) { - this.inputList = input; - } - - @Override public int size() { - return IntMath.factorial(inputList.size()); - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Iterator<List<E>> iterator() { - return new PermutationIterator<E>(inputList); - } - - @Override public boolean contains(@Nullable Object obj) { - if (obj instanceof List) { - List<?> list = (List<?>) obj; - return isPermutation(inputList, list); - } - return false; - } - - @Override public String toString() { - return "permutations(" + inputList + ")"; - } - } - - private static class PermutationIterator<E> - extends AbstractIterator<List<E>> { - final List<E> list; - final int[] c; - final int[] o; - int j; - - PermutationIterator(List<E> list) { - this.list = new ArrayList<E>(list); - int n = list.size(); - c = new int[n]; - o = new int[n]; - for (int i = 0; i < n; i++) { - c[i] = 0; - o[i] = 1; - } - j = Integer.MAX_VALUE; - } - - @Override protected List<E> computeNext() { - if (j <= 0) { - return endOfData(); - } - ImmutableList<E> next = ImmutableList.copyOf(list); - calculateNextPermutation(); - return next; - } + static final Joiner STANDARD_JOINER = Joiner.on(", "); - void calculateNextPermutation() { - j = list.size() - 1; - int s = 0; - - // Handle the special case of an empty list. Skip the calculation of the - // next permutation. - if (j == -1) { - return; - } - - while (true) { - int q = c[j] + o[j]; - if (q < 0) { - switchDirection(); - continue; - } - if (q == j + 1) { - if (j == 0) { - break; - } - s++; - switchDirection(); - continue; - } - - Collections.swap(list, j - c[j] + s, j - q + s); - c[j] = q; - break; - } - } - - void switchDirection() { - o[j] = -o[j]; - j--; - } - } - - /** - * Returns {@code true} if the second list is a permutation of the first. - */ - private static boolean isPermutation(List<?> first, - List<?> second) { - if (first.size() != second.size()) { - return false; - } - Multiset<?> firstSet = HashMultiset.create(first); - Multiset<?> secondSet = HashMultiset.create(second); - return firstSet.equals(secondSet); - } - - private static boolean isPositiveInt(long n) { - return n >= 0 && n <= Integer.MAX_VALUE; - } + // TODO(user): Maybe move the mathematical methods to a separate + // package-permission class. } diff --git a/guava/src/com/google/common/collect/ComparatorOrdering.java b/guava/src/com/google/common/collect/ComparatorOrdering.java index 5eb7612..77fe58d 100644 --- a/guava/src/com/google/common/collect/ComparatorOrdering.java +++ b/guava/src/com/google/common/collect/ComparatorOrdering.java @@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -53,17 +52,6 @@ final class ComparatorOrdering<T> extends Ordering<T> implements Serializable { return list; } - // Override just to remove a level of indirection from inner loops - @Override public <E extends T> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) { - @SuppressWarnings("unchecked") // we'll only ever have E's in here - E[] elements = (E[]) Iterables.toArray(iterable); - for (E e : elements) { - checkNotNull(e); - } - Arrays.sort(elements, comparator); - return ImmutableList.asImmutableList(elements); - } - @Override public boolean equals(@Nullable Object object) { if (object == this) { return true; diff --git a/guava/src/com/google/common/collect/ComparisonChain.java b/guava/src/com/google/common/collect/ComparisonChain.java index 2ed8cc4..cc90357 100644 --- a/guava/src/com/google/common/collect/ComparisonChain.java +++ b/guava/src/com/google/common/collect/ComparisonChain.java @@ -44,10 +44,6 @@ import javax.annotation.Nullable; * * <p>Once any comparison returns a nonzero value, remaining comparisons are * "short-circuited". - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CommonObjectUtilitiesExplained#compare/compareTo"> - * {@code ComparisonChain}</a>. * * @author Mark Davis * @author Kevin Bourrillion @@ -87,10 +83,7 @@ public abstract class ComparisonChain { @Override public ComparisonChain compare(double left, double right) { return classify(Double.compare(left, right)); } - @Override public ComparisonChain compareTrueFirst(boolean left, boolean right) { - return classify(Booleans.compare(right, left)); // reversed - } - @Override public ComparisonChain compareFalseFirst(boolean left, boolean right) { + @Override public ComparisonChain compare(boolean left, boolean right) { return classify(Booleans.compare(left, right)); } ComparisonChain classify(int result) { @@ -131,10 +124,7 @@ public abstract class ComparisonChain { @Override public ComparisonChain compare(double left, double right) { return this; } - @Override public ComparisonChain compareTrueFirst(boolean left, boolean right) { - return this; - } - @Override public ComparisonChain compareFalseFirst(boolean left, boolean right) { + @Override public ComparisonChain compare(boolean left, boolean right) { return this; } @Override public int result() { @@ -186,35 +176,11 @@ public abstract class ComparisonChain { public abstract ComparisonChain compare(double left, double right); /** - * Compares two {@code boolean} values, considering {@code true} to be less - * than {@code false}, <i>if</i> the result of this comparison chain has not + * Compares two {@code boolean} values as specified by {@link + * Booleans#compare}, <i>if</i> the result of this comparison chain has not * already been determined. - * - * @since 12.0 */ - public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); - - /** - * Compares two {@code boolean} values, considering {@code false} to be less - * than {@code true}, <i>if</i> the result of this comparison chain has not - * already been determined. - * - * @since 12.0 (present as {@code compare} since 2.0) - */ - public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); - - /** - * Old name of {@link #compareFalseFirst}. - * - * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed - * are being either negated or reversed, undo the negation or reversal and - * use {@link #compareTrueFirst}. <b>This method is scheduled for deletion - * in September 2013.</b> - */ - @Deprecated - public final ComparisonChain compare(boolean left, boolean right) { - return compareFalseFirst(left, right); - } + public abstract ComparisonChain compare(boolean left, boolean right); /** * Ends this comparison chain and returns its result: a value having the diff --git a/guava/src/com/google/common/collect/CompoundOrdering.java b/guava/src/com/google/common/collect/CompoundOrdering.java index 26ebf54..f669a62 100644 --- a/guava/src/com/google/common/collect/CompoundOrdering.java +++ b/guava/src/com/google/common/collect/CompoundOrdering.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.Comparator; +import java.util.List; /** An ordering that tries several comparators in order. */ @GwtCompatible(serializable = true) @@ -36,11 +37,15 @@ final class CompoundOrdering<T> extends Ordering<T> implements Serializable { this.comparators = ImmutableList.copyOf(comparators); } + CompoundOrdering(List<? extends Comparator<? super T>> comparators, + Comparator<? super T> lastComparator) { + this.comparators = new ImmutableList.Builder<Comparator<? super T>>() + .addAll(comparators).add(lastComparator).build(); + } + @Override public int compare(T left, T right) { - // Avoid using the Iterator to avoid generating garbage (issue 979). - int size = comparators.size(); - for (int i = 0; i < size; i++) { - int result = comparators.get(i).compare(left, right); + for (Comparator<? super T> comparator : comparators) { + int result = comparator.compare(left, right); if (result != 0) { return result; } diff --git a/guava/src/com/google/common/collect/ComputationException.java b/guava/src/com/google/common/collect/ComputationException.java index ac80d6a..5401aff 100644 --- a/guava/src/com/google/common/collect/ComputationException.java +++ b/guava/src/com/google/common/collect/ComputationException.java @@ -18,8 +18,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import javax.annotation.Nullable; - /** * Wraps an exception that occurred during a computation. * @@ -31,7 +29,7 @@ public class ComputationException extends RuntimeException { /** * Creates a new instance with the given cause. */ - public ComputationException(@Nullable Throwable cause) { + public ComputationException(Throwable cause) { super(cause); } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java b/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java index eb7363a..1e37edc 100644 --- a/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java +++ b/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java @@ -223,8 +223,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -263,8 +262,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -305,8 +303,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } diff --git a/guava/src/com/google/common/collect/ConcurrentHashMultiset.java b/guava/src/com/google/common/collect/ConcurrentHashMultiset.java index 1a1331b..46b8d6e 100644 --- a/guava/src/com/google/common/collect/ConcurrentHashMultiset.java +++ b/guava/src/com/google/common/collect/ConcurrentHashMultiset.java @@ -17,7 +17,6 @@ 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.Multisets.checkNonnegative; @@ -31,7 +30,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -46,10 +44,6 @@ import javax.annotation.Nullable; * A multiset that supports concurrent modifications and that provides atomic versions of most * {@code Multiset} operations (exceptions where noted). Null elements are not supported. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. - * * @author Cliff L. Biffle * @author mike nonemacher * @since 2.0 (imported from Google Collections Library) @@ -118,7 +112,7 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * <p>Finally, soft/weak values can be used but are not very useful: the values are created * internally and not exposed externally, so no one else will have a strong reference to the * values. Weak keys on the other hand can be useful in some scenarios. - * + * * @since 7.0 */ @Beta @@ -151,11 +145,26 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @return the nonnegative number of occurrences of the element */ @Override public int count(@Nullable Object element) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); return (existingCounter == null) ? 0 : existingCounter.get(); } /** + * Depending on the type of the underlying map, map.get may throw NullPointerException or + * ClassCastException, if the object is null or of the wrong type. We usually just want to treat + * those cases as if the element isn't in the map, by catching the exceptions and returning null. + */ + private AtomicInteger safeGet(Object element) { + try { + return countMap.get(element); + } catch (NullPointerException e) { + return null; + } catch (ClassCastException e) { + return null; + } + } + + /** * {@inheritDoc} * * <p>If the data in the multiset is modified by any other threads during this method, @@ -209,14 +218,13 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * the resulting amount would exceed {@link Integer#MAX_VALUE} */ @Override public int add(E element, int occurrences) { - checkNotNull(element); if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences)); if (existingCounter == null) { @@ -264,22 +272,13 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @return the count of the element before the operation; possibly zero * @throws IllegalArgumentException if {@code occurrences} is negative */ - /* - * TODO(cpovirk): remove and removeExactly currently accept null inputs only - * if occurrences == 0. This satisfies both NullPointerTester and - * CollectionRemoveTester.testRemove_nullAllowed, but it's not clear that it's - * a good policy, especially because, in order for the test to pass, the - * parameter must be misleadingly annotated as @Nullable. I suspect that - * we'll want to remove @Nullable, add an eager checkNotNull, and loosen up - * testRemove_nullAllowed. - */ @Override public int remove(@Nullable Object element, int occurrences) { if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { return 0; } @@ -318,7 +317,7 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { return false; } @@ -347,10 +346,9 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @throws IllegalArgumentException if {@code count} is negative */ @Override public int setCount(E element, int count) { - checkNotNull(element); checkNonnegative(count, "count"); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { if (count == 0) { return 0; @@ -402,11 +400,10 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative */ @Override public boolean setCount(E element, int expectedOldCount, int newCount) { - checkNotNull(element); checkNonnegative(expectedOldCount, "oldCount"); checkNonnegative(newCount, "newCount"); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { if (expectedOldCount != 0) { return false; @@ -451,23 +448,14 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme @Override protected Set<E> delegate() { return delegate; } - - @Override - public boolean contains(@Nullable Object object) { - return object != null && Collections2.safeContains(delegate, object); - } - - @Override - public boolean containsAll(Collection<?> collection) { - return standardContainsAll(collection); - } - @Override public boolean remove(Object object) { - return object != null && Collections2.safeRemove(delegate, object); - } - - @Override public boolean removeAll(Collection<?> c) { - return standardRemoveAll(c); + try { + return delegate.remove(object); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } } }; } @@ -559,6 +547,21 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme Iterators.addAll(list, iterator()); return list; } + + @Override public boolean remove(Object object) { + if (object instanceof Multiset.Entry) { + Multiset.Entry<?> entry = (Multiset.Entry<?>) object; + Object element = entry.getElement(); + int entryCount = entry.getCount(); + if (entryCount != 0) { + // Safe as long as we never add a new entry, which we won't. + @SuppressWarnings("unchecked") + Multiset<Object> multiset = (Multiset) multiset(); + return multiset.setCount(element, entryCount, 0); + } + } + return false; + } } /** diff --git a/guava/src/com/google/common/collect/ContiguousSet.java b/guava/src/com/google/common/collect/ContiguousSet.java index dc9aa83..439f675 100644 --- a/guava/src/com/google/common/collect/ContiguousSet.java +++ b/guava/src/com/google/common/collect/ContiguousSet.java @@ -19,63 +19,19 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; -import java.util.Collections; import java.util.NoSuchElementException; -import java.util.Set; /** * A sorted set of contiguous values in a given {@link DiscreteDomain}. * - * <p><b>Warning:</b> Be extremely careful what you do with conceptually large instances (such as - * {@code ContiguousSet.create(Range.greaterThan(0), DiscreteDomains.integers()}). Certain - * operations on such a set can be performed efficiently, but others (such as {@link Set#hashCode} - * or {@link Collections#frequency}) can cause major performance problems. - * * @author Gregory Kick * @since 10.0 */ @Beta -@GwtCompatible(emulated = true) -@SuppressWarnings("rawtypes") // allow ungenerified Comparable types +@GwtCompatible +@SuppressWarnings("unchecked") // allow ungenerified Comparable types public abstract class ContiguousSet<C extends Comparable> extends ImmutableSortedSet<C> { - /** - * Returns a {@code ContiguousSet} containing the same values in the given domain - * {@linkplain Range#contains contained} by the range. - * - * @throws IllegalArgumentException if neither range nor the domain has a lower bound, or if - * neither has an upper bound - * - * @since 13.0 - */ - public static <C extends Comparable> ContiguousSet<C> create( - Range<C> range, DiscreteDomain<C> domain) { - checkNotNull(range); - checkNotNull(domain); - Range<C> effectiveRange = range; - try { - if (!range.hasLowerBound()) { - effectiveRange = effectiveRange.intersection(Range.atLeast(domain.minValue())); - } - if (!range.hasUpperBound()) { - effectiveRange = effectiveRange.intersection(Range.atMost(domain.maxValue())); - } - } catch (NoSuchElementException e) { - throw new IllegalArgumentException(e); - } - - // Per class spec, we are allowed to throw CCE if necessary - boolean empty = effectiveRange.isEmpty() - || Range.compareOrThrow( - range.lowerBound.leastValueAbove(domain), - range.upperBound.greatestValueBelow(domain)) > 0; - - return empty - ? new EmptyContiguousSet<C>(domain) - : new RegularContiguousSet<C>(effectiveRange, domain); - } - final DiscreteDomain<C> domain; ContiguousSet(DiscreteDomain<C> domain) { @@ -84,14 +40,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte } @Override public ContiguousSet<C> headSet(C toElement) { - return headSetImpl(checkNotNull(toElement), false); + return headSet(checkNotNull(toElement), false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> headSet(C toElement, boolean inclusive) { + @Override ContiguousSet<C> headSet(C toElement, boolean inclusive) { return headSetImpl(checkNotNull(toElement), inclusive); } @@ -99,14 +51,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte checkNotNull(fromElement); checkNotNull(toElement); checkArgument(comparator().compare(fromElement, toElement) <= 0); - return subSetImpl(fromElement, true, toElement, false); + return subSet(fromElement, true, toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> subSet(C fromElement, boolean fromInclusive, C toElement, + @Override ContiguousSet<C> subSet(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { checkNotNull(fromElement); checkNotNull(toElement); @@ -115,14 +63,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte } @Override public ContiguousSet<C> tailSet(C fromElement) { - return tailSetImpl(checkNotNull(fromElement), true); + return tailSet(checkNotNull(fromElement), true); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> tailSet(C fromElement, boolean inclusive) { + @Override ContiguousSet<C> tailSet(C fromElement, boolean inclusive){ return tailSetImpl(checkNotNull(fromElement), inclusive); } diff --git a/guava/src/com/google/common/collect/Count.java b/guava/src/com/google/common/collect/Count.java index 768e298..a095119 100644 --- a/guava/src/com/google/common/collect/Count.java +++ b/guava/src/com/google/common/collect/Count.java @@ -29,6 +29,10 @@ import javax.annotation.Nullable; final class Count implements Serializable { private int value; + Count() { + this(0); + } + Count(int value) { this.value = value; } diff --git a/guava/src/com/google/common/collect/Cut.java b/guava/src/com/google/common/collect/Cut.java index 44e3450..204ea0c 100644 --- a/guava/src/com/google/common/collect/Cut.java +++ b/guava/src/com/google/common/collect/Cut.java @@ -161,9 +161,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : -1; } - @Override public String toString() { - return "-\u221e"; - } private Object readResolve() { return INSTANCE; } @@ -222,9 +219,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : 1; } - @Override public String toString() { - return "+\u221e"; - } private Object readResolve() { return INSTANCE; } @@ -286,9 +280,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int hashCode() { return endpoint.hashCode(); } - @Override public String toString() { - return "\\" + endpoint + "/"; - } private static final long serialVersionUID = 0; } @@ -351,9 +342,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int hashCode() { return ~endpoint.hashCode(); } - @Override public String toString() { - return "/" + endpoint + "\\"; - } private static final long serialVersionUID = 0; } } diff --git a/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java b/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java index d2d0088..f5630dd 100644 --- a/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -18,14 +18,14 @@ import javax.annotation.Nullable; /** * A descending wrapper around an {@code ImmutableSortedMultiset} - * + * * @author Louis Wasserman */ -@SuppressWarnings("serial") // uses writeReplace, not default serialization final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { private final transient ImmutableSortedMultiset<E> forward; DescendingImmutableSortedMultiset(ImmutableSortedMultiset<E> forward) { + super(forward.reverseComparator()); this.forward = forward; } @@ -50,29 +50,18 @@ final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset } @Override - public ImmutableSortedSet<E> elementSet() { - return forward.elementSet().descendingSet(); + ImmutableSortedSet<E> createElementSet() { + return forward.createDescendingElementSet(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - final ImmutableSet<Entry<E>> forwardEntrySet = forward.entrySet(); - return new EntrySet() { - @Override - public int size() { - return forwardEntrySet.size(); - } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<E>> createAsList() { - return forwardEntrySet.asList().reverse(); - } - }; + ImmutableSortedSet<E> createDescendingElementSet() { + return forward.elementSet(); + } + + @Override + UnmodifiableIterator<Entry<E>> descendingEntryIterator() { + return forward.entryIterator(); } @Override @@ -91,6 +80,16 @@ final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset } @Override + UnmodifiableIterator<Entry<E>> entryIterator() { + return forward.descendingEntryIterator(); + } + + @Override + int distinctElements() { + return forward.distinctElements(); + } + + @Override boolean isPartialView() { return forward.isPartialView(); } diff --git a/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java b/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java deleted file mode 100644 index 340d8b9..0000000 --- a/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtIncompatible; - -import javax.annotation.Nullable; - -/** - * Skeletal implementation of {@link ImmutableSortedSet#descendingSet()}. - * - * @author Louis Wasserman - */ -class DescendingImmutableSortedSet<E> extends ImmutableSortedSet<E> { - private final ImmutableSortedSet<E> forward; - - DescendingImmutableSortedSet(ImmutableSortedSet<E> forward) { - super(Ordering.from(forward.comparator()).reverse()); - this.forward = forward; - } - - @Override - public int size() { - return forward.size(); - } - - @Override - public UnmodifiableIterator<E> iterator() { - return forward.descendingIterator(); - } - - @Override - ImmutableSortedSet<E> headSetImpl(E toElement, boolean inclusive) { - return forward.tailSet(toElement, inclusive).descendingSet(); - } - - @Override - ImmutableSortedSet<E> subSetImpl( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); - } - - @Override - ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive) { - return forward.headSet(fromElement, inclusive).descendingSet(); - } - - @Override - @GwtIncompatible("NavigableSet") - public ImmutableSortedSet<E> descendingSet() { - return forward; - } - - @Override - @GwtIncompatible("NavigableSet") - public UnmodifiableIterator<E> descendingIterator() { - return forward.iterator(); - } - - @Override - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<E> createDescendingSet() { - throw new AssertionError("should never be called"); - } - - @Override - public E lower(E element) { - return forward.higher(element); - } - - @Override - public E floor(E element) { - return forward.ceiling(element); - } - - @Override - public E ceiling(E element) { - return forward.floor(element); - } - - @Override - public E higher(E element) { - return forward.lower(element); - } - - @Override - int indexOf(@Nullable Object target) { - int index = forward.indexOf(target); - if (index == -1) { - return index; - } else { - return size() - 1 - index; - } - } - - @Override - boolean isPartialView() { - return forward.isPartialView(); - } -} diff --git a/guava/src/com/google/common/collect/DescendingMultiset.java b/guava/src/com/google/common/collect/DescendingMultiset.java deleted file mode 100644 index d83f782..0000000 --- a/guava/src/com/google/common/collect/DescendingMultiset.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.NavigableSet; -import java.util.Set; - -/** - * A skeleton implementation of a descending multiset. Only needs - * {@code forwardMultiset()} and {@code entryIterator()}. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -abstract class DescendingMultiset<E> extends ForwardingMultiset<E> - implements SortedMultiset<E> { - abstract SortedMultiset<E> forwardMultiset(); - - private transient Comparator<? super E> comparator; - - @Override public Comparator<? super E> comparator() { - Comparator<? super E> result = comparator; - if (result == null) { - return comparator = - Ordering.from(forwardMultiset().comparator()).<E>reverse(); - } - return result; - } - - private transient NavigableSet<E> elementSet; - - @Override public NavigableSet<E> elementSet() { - NavigableSet<E> result = elementSet; - if (result == null) { - return elementSet = new SortedMultisets.NavigableElementSet<E>(this); - } - return result; - } - - @Override public Entry<E> pollFirstEntry() { - return forwardMultiset().pollLastEntry(); - } - - @Override public Entry<E> pollLastEntry() { - return forwardMultiset().pollFirstEntry(); - } - - @Override public SortedMultiset<E> headMultiset(E toElement, - BoundType boundType) { - return forwardMultiset().tailMultiset(toElement, boundType) - .descendingMultiset(); - } - - @Override public SortedMultiset<E> subMultiset(E fromElement, - BoundType fromBoundType, E toElement, BoundType toBoundType) { - return forwardMultiset().subMultiset(toElement, toBoundType, fromElement, - fromBoundType).descendingMultiset(); - } - - @Override public SortedMultiset<E> tailMultiset(E fromElement, - BoundType boundType) { - return forwardMultiset().headMultiset(fromElement, boundType) - .descendingMultiset(); - } - - @Override protected Multiset<E> delegate() { - return forwardMultiset(); - } - - @Override public SortedMultiset<E> descendingMultiset() { - return forwardMultiset(); - } - - @Override public Entry<E> firstEntry() { - return forwardMultiset().lastEntry(); - } - - @Override public Entry<E> lastEntry() { - return forwardMultiset().firstEntry(); - } - - abstract Iterator<Entry<E>> entryIterator(); - - private transient Set<Entry<E>> entrySet; - - @Override public Set<Entry<E>> entrySet() { - Set<Entry<E>> result = entrySet; - return (result == null) ? entrySet = createEntrySet() : result; - } - - Set<Entry<E>> createEntrySet() { - return new Multisets.EntrySet<E>() { - @Override Multiset<E> multiset() { - return DescendingMultiset.this; - } - - @Override public Iterator<Entry<E>> iterator() { - return entryIterator(); - } - - @Override public int size() { - return forwardMultiset().entrySet().size(); - } - }; - } - - @Override public Iterator<E> iterator() { - return Multisets.iteratorImpl(this); - } - - @Override public Object[] toArray() { - return standardToArray(); - } - - @Override public <T> T[] toArray(T[] array) { - return standardToArray(array); - } - - @Override public String toString() { - return entrySet().toString(); - } -}
\ No newline at end of file diff --git a/guava/src/com/google/common/collect/DiscreteDomain.java b/guava/src/com/google/common/collect/DiscreteDomain.java index f2a2f9a..893bbbb 100644 --- a/guava/src/com/google/common/collect/DiscreteDomain.java +++ b/guava/src/com/google/common/collect/DiscreteDomain.java @@ -19,13 +19,11 @@ package com.google.common.collect; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import java.io.Serializable; -import java.math.BigInteger; import java.util.NoSuchElementException; /** * A descriptor for a <i>discrete</i> {@code Comparable} domain such as all - * {@link Integer} instances. A discrete domain is one that supports the three basic + * {@link Integer}s. A discrete domain is one that supports the three basic * operations: {@link #next}, {@link #previous} and {@link #distance}, according * to their specifications. The methods {@link #minValue} and {@link #maxValue} * should also be overridden for bounded types. @@ -34,159 +32,13 @@ import java.util.NoSuchElementException; * type; it cannot represent partial domains such as "prime integers" or * "strings of length 5." * - * <p>See the Guava User Guide section on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained#Discrete_Domains"> - * {@code DiscreteDomain}</a>. - * * @author Kevin Bourrillion * @since 10.0 + * @see DiscreteDomains */ @GwtCompatible @Beta public abstract class DiscreteDomain<C extends Comparable> { - - /** - * Returns the discrete domain for values of type {@code Integer}. - * - * @since 14.0 (since 10.0 as {@code DiscreteDomains.integers()}) - */ - public static DiscreteDomain<Integer> integers() { - return IntegerDomain.INSTANCE; - } - - private static final class IntegerDomain extends DiscreteDomain<Integer> - implements Serializable { - private static final IntegerDomain INSTANCE = new IntegerDomain(); - - @Override public Integer next(Integer value) { - int i = value; - return (i == Integer.MAX_VALUE) ? null : i + 1; - } - - @Override public Integer previous(Integer value) { - int i = value; - return (i == Integer.MIN_VALUE) ? null : i - 1; - } - - @Override public long distance(Integer start, Integer end) { - return (long) end - start; - } - - @Override public Integer minValue() { - return Integer.MIN_VALUE; - } - - @Override public Integer maxValue() { - return Integer.MAX_VALUE; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.integers()"; - } - - private static final long serialVersionUID = 0; - } - - /** - * Returns the discrete domain for values of type {@code Long}. - * - * @since 14.0 (since 10.0 as {@code DiscreteDomains.longs()}) - */ - public static DiscreteDomain<Long> longs() { - return LongDomain.INSTANCE; - } - - private static final class LongDomain extends DiscreteDomain<Long> - implements Serializable { - private static final LongDomain INSTANCE = new LongDomain(); - - @Override public Long next(Long value) { - long l = value; - return (l == Long.MAX_VALUE) ? null : l + 1; - } - - @Override public Long previous(Long value) { - long l = value; - return (l == Long.MIN_VALUE) ? null : l - 1; - } - - @Override public long distance(Long start, Long end) { - long result = end - start; - if (end > start && result < 0) { // overflow - return Long.MAX_VALUE; - } - if (end < start && result > 0) { // underflow - return Long.MIN_VALUE; - } - return result; - } - - @Override public Long minValue() { - return Long.MIN_VALUE; - } - - @Override public Long maxValue() { - return Long.MAX_VALUE; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.longs()"; - } - - private static final long serialVersionUID = 0; - } - - /** - * Returns the discrete domain for values of type {@code BigInteger}. - */ - // TODO(kevinb): make sure it's tested, and make it public - static DiscreteDomain<BigInteger> bigIntegers() { - return BigIntegerDomain.INSTANCE; - } - - private static final class BigIntegerDomain extends DiscreteDomain<BigInteger> - implements Serializable { - private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); - - private static final BigInteger MIN_LONG = - BigInteger.valueOf(Long.MIN_VALUE); - private static final BigInteger MAX_LONG = - BigInteger.valueOf(Long.MAX_VALUE); - - @Override public BigInteger next(BigInteger value) { - return value.add(BigInteger.ONE); - } - - @Override public BigInteger previous(BigInteger value) { - return value.subtract(BigInteger.ONE); - } - - @Override public long distance(BigInteger start, BigInteger end) { - return end.subtract(start).max(MIN_LONG).min(MAX_LONG).longValue(); - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.bigIntegers()"; - } - - private static final long serialVersionUID = 0; - } - /** Constructor for use by subclasses. */ protected DiscreteDomain() {} @@ -224,7 +76,7 @@ public abstract class DiscreteDomain<C extends Comparable> { * type. * * @return the distance as described above, or {@link Long#MIN_VALUE} or - * {@link Long#MAX_VALUE} if the distance is too small or too large, + * {@link Long#MIN_VALUE} if the distance is too small or too large, * respectively. */ public abstract long distance(C start, C end); @@ -258,5 +110,4 @@ public abstract class DiscreteDomain<C extends Comparable> { public C maxValue() { throw new NoSuchElementException(); } - } diff --git a/guava/src/com/google/common/collect/DiscreteDomains.java b/guava/src/com/google/common/collect/DiscreteDomains.java index dac4628..8cb2ae7 100644 --- a/guava/src/com/google/common/collect/DiscreteDomains.java +++ b/guava/src/com/google/common/collect/DiscreteDomains.java @@ -16,22 +16,20 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.math.BigInteger; + /** * Factories for common {@link DiscreteDomain} instances. * - * <p>See the Guava User Guide section on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained#Discrete_Domains"> - * {@code DiscreteDomain}</a>. - * * @author Gregory Kick * @since 10.0 - * @deprecated Merged into {@link DiscreteDomain}. This class is scheduled for deletion in release - * 15.0. */ @GwtCompatible -@Deprecated +@Beta public final class DiscreteDomains { private DiscreteDomains() {} @@ -39,13 +37,122 @@ public final class DiscreteDomains { * Returns the discrete domain for values of type {@code Integer}. */ public static DiscreteDomain<Integer> integers() { - return DiscreteDomain.integers(); + return IntegerDomain.INSTANCE; + } + + private static final class IntegerDomain extends DiscreteDomain<Integer> + implements Serializable { + private static final IntegerDomain INSTANCE = new IntegerDomain(); + + @Override public Integer next(Integer value) { + int i = value; + return (i == Integer.MAX_VALUE) ? null : i + 1; + } + + @Override public Integer previous(Integer value) { + int i = value; + return (i == Integer.MIN_VALUE) ? null : i - 1; + } + + @Override public long distance(Integer start, Integer end) { + return (long) end - start; + } + + @Override public Integer minValue() { + return Integer.MIN_VALUE; + } + + @Override public Integer maxValue() { + return Integer.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; } /** * Returns the discrete domain for values of type {@code Long}. */ public static DiscreteDomain<Long> longs() { - return DiscreteDomain.longs(); + return LongDomain.INSTANCE; + } + + private static final class LongDomain extends DiscreteDomain<Long> + implements Serializable { + private static final LongDomain INSTANCE = new LongDomain(); + + @Override public Long next(Long value) { + long l = value; + return (l == Long.MAX_VALUE) ? null : l + 1; + } + + @Override public Long previous(Long value) { + long l = value; + return (l == Long.MIN_VALUE) ? null : l - 1; + } + + @Override public long distance(Long start, Long end) { + long result = end - start; + if (end > start && result < 0) { // overflow + return Long.MAX_VALUE; + } + if (end < start && result > 0) { // underflow + return Long.MIN_VALUE; + } + return result; + } + + @Override public Long minValue() { + return Long.MIN_VALUE; + } + + @Override public Long maxValue() { + return Long.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code BigInteger}. + */ + // TODO(kevinb): make sure it's tested, and make it public + static DiscreteDomain<BigInteger> bigIntegers() { + return BigIntegerDomain.INSTANCE; + } + + private static final class BigIntegerDomain extends DiscreteDomain<BigInteger> + implements Serializable { + private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); + + private static final BigInteger MIN_LONG = + BigInteger.valueOf(Long.MIN_VALUE); + private static final BigInteger MAX_LONG = + BigInteger.valueOf(Long.MAX_VALUE); + + @Override public BigInteger next(BigInteger value) { + return value.add(BigInteger.ONE); + } + + @Override public BigInteger previous(BigInteger value) { + return value.subtract(BigInteger.ONE); + } + + @Override public long distance(BigInteger start, BigInteger end) { + return start.subtract(end).max(MIN_LONG).min(MAX_LONG).longValue(); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; } } diff --git a/guava/src/com/google/common/collect/EmptyContiguousSet.java b/guava/src/com/google/common/collect/EmptyContiguousSet.java index 4546349..2bec7bd 100644 --- a/guava/src/com/google/common/collect/EmptyContiguousSet.java +++ b/guava/src/com/google/common/collect/EmptyContiguousSet.java @@ -71,8 +71,8 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { return this; } - @GwtIncompatible("not used by GWT emulation") - @Override int indexOf(Object target) { + //Abstract method doesn't exist in GWT emulation + /* @Override */ int indexOf(Object target) { return -1; } @@ -80,11 +80,6 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { return Iterators.emptyIterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<C> descendingIterator() { - return Iterators.emptyIterator(); - } - @Override boolean isPartialView() { return false; } @@ -133,9 +128,4 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { Object writeReplace() { return new SerializedForm<C>(domain); } - - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<C> createDescendingSet() { - return new EmptyImmutableSortedSet<C>(Ordering.natural().reverse()); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableBiMap.java b/guava/src/com/google/common/collect/EmptyImmutableBiMap.java deleted file mode 100644 index 5b862b3..0000000 --- a/guava/src/com/google/common/collect/EmptyImmutableBiMap.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; - -import javax.annotation.Nullable; - -/** - * Bimap with no mappings. - * - * @author Jared Levy - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace(), not default serialization -final class EmptyImmutableBiMap extends ImmutableBiMap<Object, Object> { - static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap(); - - private EmptyImmutableBiMap() {} - - @Override public ImmutableBiMap<Object, Object> inverse() { - return this; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Object get(@Nullable Object key) { - return null; - } - - @Override - public ImmutableSet<Entry<Object, Object>> entrySet() { - return ImmutableSet.of(); - } - - @Override - ImmutableSet<Entry<Object, Object>> createEntrySet() { - throw new AssertionError("should never be called"); - } - - @Override - public ImmutableSet<Object> keySet() { - return ImmutableSet.of(); - } - - @Override - boolean isPartialView() { - return false; - } - - Object readResolve() { - return INSTANCE; // preserve singleton property - } -} diff --git a/guava/src/com/google/common/collect/EmptyImmutableList.java b/guava/src/com/google/common/collect/EmptyImmutableList.java index b854d2b..ec685dd 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableList.java +++ b/guava/src/com/google/common/collect/EmptyImmutableList.java @@ -24,17 +24,45 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.List; +import java.util.NoSuchElementException; import javax.annotation.Nullable; /** * An empty immutable list. - * + * * @author Kevin Bourrillion */ @GwtCompatible(serializable = true, emulated = true) final class EmptyImmutableList extends ImmutableList<Object> { static final EmptyImmutableList INSTANCE = new EmptyImmutableList(); + static final UnmodifiableListIterator<Object> ITERATOR = + new UnmodifiableListIterator<Object>() { + + @Override public boolean hasNext() { + return false; + } + + @Override public boolean hasPrevious() { + return false; + } + + @Override public Object next() { + throw new NoSuchElementException(); + } + + @Override public int nextIndex() { + return 0; + } + + @Override public Object previous() { + throw new NoSuchElementException(); + } + + @Override public int previousIndex() { + return -1; + } + }; private EmptyImmutableList() {} @@ -51,20 +79,18 @@ final class EmptyImmutableList extends ImmutableList<Object> { return false; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<Object> iterator() { - return listIterator(); + return Iterators.emptyIterator(); } + private static final Object[] EMPTY_ARRAY = new Object[0]; + @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { @@ -98,13 +124,17 @@ final class EmptyImmutableList extends ImmutableList<Object> { return this; } - @Override public UnmodifiableListIterator<Object> listIterator() { - return Iterators.EMPTY_LIST_ITERATOR; + @Override public UnmodifiableListIterator<Object> listIterator(){ + return ITERATOR; } @Override public UnmodifiableListIterator<Object> listIterator(int start) { checkPositionIndex(start, 0); - return Iterators.EMPTY_LIST_ITERATOR; + return ITERATOR; + } + + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { diff --git a/guava/src/com/google/common/collect/EmptyImmutableMap.java b/guava/src/com/google/common/collect/EmptyImmutableMap.java new file mode 100644 index 0000000..8d58021 --- /dev/null +++ b/guava/src/com/google/common/collect/EmptyImmutableMap.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * An empty immutable map. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +final class EmptyImmutableMap extends ImmutableMap<Object, Object> { + static final EmptyImmutableMap INSTANCE = new EmptyImmutableMap(); + + private EmptyImmutableMap() {} + + @Override public Object get(@Nullable Object key) { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override public boolean isEmpty() { + return true; + } + + @Override public boolean containsKey(@Nullable Object key) { + return false; + } + + @Override public boolean containsValue(@Nullable Object value) { + return false; + } + + @Override public ImmutableSet<Entry<Object, Object>> entrySet() { + return ImmutableSet.of(); + } + + @Override public ImmutableSet<Object> keySet() { + return ImmutableSet.of(); + } + + @Override public ImmutableCollection<Object> values() { + return ImmutableCollection.EMPTY_IMMUTABLE_COLLECTION; + } + + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + return that.isEmpty(); + } + return false; + } + + @Override boolean isPartialView() { + return false; + } + + @Override public int hashCode() { + return 0; + } + + @Override public String toString() { + return "{}"; + } + + Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/guava/src/com/google/common/collect/EmptyImmutableMultiset.java b/guava/src/com/google/common/collect/EmptyImmutableMultiset.java index 1931342..2a72a2b 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableMultiset.java +++ b/guava/src/com/google/common/collect/EmptyImmutableMultiset.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2008 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 @@ -18,13 +18,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.util.Collection; - import javax.annotation.Nullable; /** * An empty immutable multiset. - * + * * @author Jared Levy * @author Louis Wasserman */ @@ -38,51 +36,22 @@ final class EmptyImmutableMultiset extends ImmutableMultiset<Object> { } @Override - public boolean contains(@Nullable Object object) { - return false; - } - - @Override - public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - - @Override - public UnmodifiableIterator<Object> iterator() { - return Iterators.emptyIterator(); - } - - @Override - public boolean equals(@Nullable Object object) { - if (object instanceof Multiset) { - Multiset<?> other = (Multiset<?>) object; - return other.isEmpty(); - } - return false; - } - - @Override - public int hashCode() { - return 0; - } - - @Override public ImmutableSet<Object> elementSet() { return ImmutableSet.of(); } @Override - public ImmutableSet<Entry<Object>> entrySet() { - return ImmutableSet.of(); + public int size() { + return 0; } @Override - ImmutableSet<Entry<Object>> createEntrySet() { - throw new AssertionError("should never be called"); + UnmodifiableIterator<Entry<Object>> entryIterator() { + return Iterators.emptyIterator(); } @Override - public int size() { + int distinctElements() { return 0; } @@ -92,22 +61,8 @@ final class EmptyImmutableMultiset extends ImmutableMultiset<Object> { } @Override - public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; - } - - @Override - public <T> T[] toArray(T[] other) { - return asList().toArray(other); - } - - @Override - public ImmutableList<Object> asList() { - return ImmutableList.of(); - } - - Object readResolve() { - return INSTANCE; // preserve singleton property + ImmutableSet<Entry<Object>> createEntrySet() { + return ImmutableSet.of(); } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/EmptyImmutableSet.java b/guava/src/com/google/common/collect/EmptyImmutableSet.java index e70b051..8722bdf 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSet.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSet.java @@ -25,7 +25,7 @@ import javax.annotation.Nullable; /** * An empty immutable set. - * + * * @author Kevin Bourrillion */ @GwtCompatible(serializable = true, emulated = true) @@ -43,14 +43,10 @@ final class EmptyImmutableSet extends ImmutableSet<Object> { return true; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<Object> iterator() { return Iterators.emptyIterator(); } @@ -59,17 +55,21 @@ final class EmptyImmutableSet extends ImmutableSet<Object> { return false; } + private static final Object[] EMPTY_ARRAY = new Object[0]; + @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { - return asList().toArray(a); + if (a.length > 0) { + a[0] = null; + } + return a; } - @Override - public ImmutableList<Object> asList() { - return ImmutableList.of(); + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java b/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java deleted file mode 100644 index 0a1e854..0000000 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.util.Comparator; - -import javax.annotation.Nullable; - -/** - * An empty immutable sorted map. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace, not default serialization -final class EmptyImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> { - private final transient ImmutableSortedSet<K> keySet; - - EmptyImmutableSortedMap(Comparator<? super K> comparator) { - this.keySet = ImmutableSortedSet.emptySet(comparator); - } - - EmptyImmutableSortedMap( - Comparator<? super K> comparator, ImmutableSortedMap<K, V> descendingMap) { - super(descendingMap); - this.keySet = ImmutableSortedSet.emptySet(comparator); - } - - @Override - public V get(@Nullable Object key) { - return null; - } - - @Override - public ImmutableSortedSet<K> keySet() { - return keySet; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public ImmutableCollection<V> values() { - return ImmutableList.of(); - } - - @Override - public String toString() { - return "{}"; - } - - @Override - boolean isPartialView() { - return false; - } - - @Override - public ImmutableSet<Entry<K, V>> entrySet() { - return ImmutableSet.of(); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - throw new AssertionError("should never be called"); - } - - @Override - public ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) { - checkNotNull(toKey); - return this; - } - - @Override - public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { - checkNotNull(fromKey); - return this; - } - - @Override - ImmutableSortedMap<K, V> createDescendingMap() { - return new EmptyImmutableSortedMap<K, V>(Ordering.from(comparator()).reverse(), this); - } -} diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java b/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java index a7ddf28..623050c 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -16,7 +16,6 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Collection; import java.util.Comparator; import javax.annotation.Nullable; @@ -26,12 +25,9 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@SuppressWarnings("serial") // Uses writeReplace, not default serialization final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { - private final ImmutableSortedSet<E> elementSet; - EmptyImmutableSortedMultiset(Comparator<? super E> comparator) { - this.elementSet = ImmutableSortedSet.emptySet(comparator); + super(comparator); } @Override @@ -50,33 +46,28 @@ final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { } @Override - public boolean contains(@Nullable Object object) { - return false; - } - - @Override - public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); + public int size() { + return 0; } @Override - public int size() { - return 0; + ImmutableSortedSet<E> createElementSet() { + return ImmutableSortedSet.emptySet(comparator()); } @Override - public ImmutableSortedSet<E> elementSet() { - return elementSet; + ImmutableSortedSet<E> createDescendingElementSet() { + return ImmutableSortedSet.emptySet(reverseComparator()); } @Override - public ImmutableSet<Entry<E>> entrySet() { - return ImmutableSet.of(); + UnmodifiableIterator<Entry<E>> descendingEntryIterator() { + return Iterators.emptyIterator(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - throw new AssertionError("should never be called"); + UnmodifiableIterator<Entry<E>> entryIterator() { + return Iterators.emptyIterator(); } @Override @@ -94,46 +85,12 @@ final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { } @Override - public UnmodifiableIterator<E> iterator() { - return Iterators.emptyIterator(); - } - - @Override - public boolean equals(@Nullable Object object) { - if (object instanceof Multiset) { - Multiset<?> other = (Multiset<?>) object; - return other.isEmpty(); - } - return false; - } - - @Override - public int hashCode() { + int distinctElements() { return 0; } @Override - public String toString() { - return "[]"; - } - - @Override boolean isPartialView() { return false; } - - @Override - public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; - } - - @Override - public <T> T[] toArray(T[] other) { - return asList().toArray(other); - } - - @Override - public ImmutableList<E> asList() { - return ImmutableList.of(); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java b/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java index 9f5d522..e406163 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java @@ -17,7 +17,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.util.Collection; import java.util.Comparator; @@ -47,37 +46,33 @@ class EmptyImmutableSortedSet<E> extends ImmutableSortedSet<E> { return true; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<E> iterator() { return Iterators.emptyIterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<E> descendingIterator() { - return Iterators.emptyIterator(); - } - @Override boolean isPartialView() { return false; } - @Override public ImmutableList<E> asList() { - return ImmutableList.of(); - } + private static final Object[] EMPTY_ARRAY = new Object[0]; @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { - return asList().toArray(a); + if (a.length > 0) { + a[0] = null; + } + return a; + } + + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { @@ -125,9 +120,4 @@ class EmptyImmutableSortedSet<E> extends ImmutableSortedSet<E> { @Override int indexOf(@Nullable Object target) { return -1; } - - @Override - ImmutableSortedSet<E> createDescendingSet() { - return new EmptyImmutableSortedSet<E>(Ordering.from(comparator).reverse()); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableTable.java b/guava/src/com/google/common/collect/EmptyImmutableTable.java index 65b8042..61949ca 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableTable.java +++ b/guava/src/com/google/common/collect/EmptyImmutableTable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Guava Authors + * 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. @@ -28,7 +28,7 @@ import javax.annotation.concurrent.Immutable; /** * An empty implementation of {@link ImmutableTable}. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible @Immutable @@ -53,7 +53,7 @@ final class EmptyImmutableTable extends ImmutableTable<Object, Object, Object> { @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; return that.isEmpty(); } else { diff --git a/guava/src/com/google/common/collect/EnumBiMap.java b/guava/src/com/google/common/collect/EnumBiMap.java index 05d84ed..9a94ddd 100644 --- a/guava/src/com/google/common/collect/EnumBiMap.java +++ b/guava/src/com/google/common/collect/EnumBiMap.java @@ -17,7 +17,6 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -32,10 +31,6 @@ import java.util.Map; * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values * are not permitted. An {@code EnumBiMap} and its inverse are both * serializable. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap"> - * {@code BiMap}</a>. * * @author Mike Bostock * @since 2.0 (imported from Google Collections Library) @@ -111,16 +106,6 @@ public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>> return valueType; } - @Override - K checkKey(K key) { - return checkNotNull(key); - } - - @Override - V checkValue(V value) { - return checkNotNull(value); - } - /** * @serialData the key class, value class, number of entries, first key, first * value, second key, second value, and so on. diff --git a/guava/src/com/google/common/collect/EnumHashBiMap.java b/guava/src/com/google/common/collect/EnumHashBiMap.java index c43daf0..8f32515 100644 --- a/guava/src/com/google/common/collect/EnumHashBiMap.java +++ b/guava/src/com/google/common/collect/EnumHashBiMap.java @@ -16,8 +16,6 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -35,10 +33,6 @@ import javax.annotation.Nullable; * a {@code HashMap} instance for values-to-keys. Null keys are not permitted, * but null values are. An {@code EnumHashBiMap} and its inverse are both * serializable. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap"> - * {@code BiMap}</a>. * * @author Mike Bostock * @since 2.0 (imported from Google Collections Library) @@ -83,12 +77,7 @@ public final class EnumHashBiMap<K extends Enum<K>, V> this.keyType = keyType; } - // Overriding these 3 methods to show that values may be null (but not keys) - - @Override - K checkKey(K key) { - return checkNotNull(key); - } + // Overriding these two methods to show that values may be null (but not keys) @Override public V put(K key, @Nullable V value) { return super.put(key, value); diff --git a/guava/src/com/google/common/collect/EnumMultiset.java b/guava/src/com/google/common/collect/EnumMultiset.java index 2bb121c..560bf7c 100644 --- a/guava/src/com/google/common/collect/EnumMultiset.java +++ b/guava/src/com/google/common/collect/EnumMultiset.java @@ -27,10 +27,6 @@ import java.util.Iterator; /** * Multiset implementation backed by an {@link EnumMap}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) @@ -58,19 +54,6 @@ public final class EnumMultiset<E extends Enum<E>> extends AbstractMapBasedMulti Iterables.addAll(multiset, elements); return multiset; } - - /** - * Returns a new {@code EnumMultiset} instance containing the given elements. Unlike - * {@link EnumMultiset#create(Iterable)}, this method does not produce an exception on an empty - * iterable. - * - * @since 14.0 - */ - public static <E extends Enum<E>> EnumMultiset<E> create(Iterable<E> elements, Class<E> type) { - EnumMultiset<E> result = create(type); - Iterables.addAll(result, elements); - return result; - } private transient Class<E> type; diff --git a/guava/src/com/google/common/collect/FilteredEntryMultimap.java b/guava/src/com/google/common/collect/FilteredEntryMultimap.java deleted file mode 100644 index 9120893..0000000 --- a/guava/src/com/google/common/collect/FilteredEntryMultimap.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (C) 2012 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.base.Predicates.compose; -import static com.google.common.base.Predicates.in; -import static com.google.common.base.Predicates.not; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; - -import java.util.AbstractMap; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}. - * - * @author Jared Levy - * @author Louis Wasserman - */ -@GwtCompatible -class FilteredEntryMultimap<K, V> extends FilteredMultimap<K, V> { - final Predicate<? super Entry<K, V>> predicate; - - FilteredEntryMultimap(Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) { - super(unfiltered); - this.predicate = checkNotNull(predicate); - } - - @Override - Predicate<? super Entry<K, V>> entryPredicate() { - return predicate; - } - - @Override - public int size() { - return entries().size(); - } - - private boolean satisfies(K key, V value) { - return predicate.apply(Maps.immutableEntry(key, value)); - } - - - final class ValuePredicate implements Predicate<V> { - private final K key; - - ValuePredicate(K key) { - this.key = key; - } - - @Override - public boolean apply(@Nullable V value) { - return satisfies(key, value); - } - } - - static <E> Collection<E> filterCollection( - Collection<E> collection, Predicate<? super E> predicate) { - if (collection instanceof Set) { - return Sets.filter((Set<E>) collection, predicate); - } else { - return Collections2.filter(collection, predicate); - } - } - - @Override - public boolean containsKey(@Nullable Object key) { - return asMap().get(key) != null; - } - - @Override - public Collection<V> removeAll(@Nullable Object key) { - return Objects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection()); - } - - Collection<V> unmodifiableEmptyCollection() { - // These return false, rather than throwing a UOE, on remove calls. - return (unfiltered instanceof SetMultimap) - ? Collections.<V>emptySet() - : Collections.<V>emptyList(); - } - - @Override - public void clear() { - entries().clear(); - } - - @Override - public Collection<V> get(final K key) { - return filterCollection(unfiltered.get(key), new ValuePredicate(key)); - } - - @Override - Collection<Entry<K, V>> createEntries() { - return filterCollection(unfiltered.entries(), predicate); - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - throw new AssertionError("should never be called"); - } - - @Override - Map<K, Collection<V>> createAsMap() { - return new AsMap(); - } - - @Override - public Set<K> keySet() { - return asMap().keySet(); - } - - boolean removeIf(Predicate<? super Entry<K, Collection<V>>> predicate) { - Iterator<Entry<K, Collection<V>>> entryIterator = unfiltered.asMap().entrySet().iterator(); - boolean changed = false; - while (entryIterator.hasNext()) { - Entry<K, Collection<V>> entry = entryIterator.next(); - K key = entry.getKey(); - Collection<V> collection = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { - if (collection.size() == entry.getValue().size()) { - entryIterator.remove(); - } else { - collection.clear(); - } - changed = true; - } - } - return changed; - } - - class AsMap extends AbstractMap<K, Collection<V>> { - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public void clear() { - FilteredEntryMultimap.this.clear(); - } - - @Override - public Collection<V> get(@Nullable Object key) { - Collection<V> result = unfiltered.asMap().get(key); - if (result == null) { - return null; - } - @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself - K k = (K) key; - result = filterCollection(result, new ValuePredicate(k)); - return result.isEmpty() ? null : result; - } - - @Override - public Collection<V> remove(@Nullable Object key) { - Collection<V> collection = unfiltered.asMap().get(key); - if (collection == null) { - return null; - } - @SuppressWarnings("unchecked") // it's definitely equal to a K - K k = (K) key; - List<V> result = Lists.newArrayList(); - Iterator<V> itr = collection.iterator(); - while (itr.hasNext()) { - V v = itr.next(); - if (satisfies(k, v)) { - itr.remove(); - result.add(v); - } - } - if (result.isEmpty()) { - return null; - } else if (unfiltered instanceof SetMultimap) { - return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); - } else { - return Collections.unmodifiableList(result); - } - } - - private Set<K> keySet; - - @Override - public Set<K> keySet() { - Set<K> result = keySet; - if (result == null) { - return keySet = new Maps.KeySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<K>keyFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<K>keyFunction())); - } - - @Override - public boolean remove(@Nullable Object o) { - return AsMap.this.remove(o) != null; - } - }; - } - return result; - } - - @Override - public Set<Entry<K, Collection<V>>> entrySet() { - return new Maps.EntrySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public Iterator<Entry<K, Collection<V>>> iterator() { - return new AbstractIterator<Entry<K, Collection<V>>>() { - final Iterator<Entry<K, Collection<V>>> backingIterator - = unfiltered.asMap().entrySet().iterator(); - - @Override - protected Entry<K, Collection<V>> computeNext() { - while (backingIterator.hasNext()) { - Entry<K, Collection<V>> entry = backingIterator.next(); - K key = entry.getKey(); - Collection<V> collection - = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty()) { - return Maps.immutableEntry(key, collection); - } - } - return endOfData(); - } - }; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(in(c)); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - - @Override - public int size() { - return Iterators.size(iterator()); - } - }; - } - - @Override - public Collection<Collection<V>> values() { - return new Maps.Values<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public boolean remove(@Nullable Object o) { - if (o instanceof Collection) { - Collection<?> c = (Collection<?>) o; - Iterator<Entry<K, Collection<V>>> entryIterator - = unfiltered.asMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, Collection<V>> entry = entryIterator.next(); - K key = entry.getKey(); - Collection<V> collection - = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty() && c.equals(collection)) { - if (collection.size() == entry.getValue().size()) { - entryIterator.remove(); - } else { - collection.clear(); - } - return true; - } - } - } - return false; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<Collection<V>>valueFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<Collection<V>>valueFunction())); - } - }; - } - } - - @Override - Multiset<K> createKeys() { - return new Keys(); - } - - class Keys extends Multimaps.Keys<K, V> { - Keys() { - super(FilteredEntryMultimap.this); - } - - @Override - public int remove(@Nullable Object key, int occurrences) { - Multisets.checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(key); - } - Collection<V> collection = unfiltered.asMap().get(key); - if (collection == null) { - return 0; - } - @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself - K k = (K) key; - int oldCount = 0; - Iterator<V> itr = collection.iterator(); - while (itr.hasNext()) { - V v = itr.next(); - if (satisfies(k, v)) { - oldCount++; - if (oldCount <= occurrences) { - itr.remove(); - } - } - } - return oldCount; - } - - @Override - public Set<Multiset.Entry<K>> entrySet() { - return new Multisets.EntrySet<K>() { - - @Override - Multiset<K> multiset() { - return Keys.this; - } - - @Override - public Iterator<Multiset.Entry<K>> iterator() { - return Keys.this.entryIterator(); - } - - @Override - public int size() { - return FilteredEntryMultimap.this.keySet().size(); - } - - private boolean removeIf(final Predicate<? super Multiset.Entry<K>> predicate) { - return FilteredEntryMultimap.this.removeIf(new Predicate<Map.Entry<K, Collection<V>>>() { - @Override - public boolean apply(Map.Entry<K, Collection<V>> entry) { - return predicate.apply( - Multisets.immutableEntry(entry.getKey(), entry.getValue().size())); - } - }); - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(in(c)); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - }; - } - } -} diff --git a/guava/src/com/google/common/collect/FilteredKeyMultimap.java b/guava/src/com/google/common/collect/FilteredKeyMultimap.java deleted file mode 100644 index 0a07333..0000000 --- a/guava/src/com/google/common/collect/FilteredKeyMultimap.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2012 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.base.Preconditions.checkPositionIndex; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. - * - * @author Louis Wasserman - */ -@GwtCompatible -class FilteredKeyMultimap<K, V> extends FilteredMultimap<K, V> { - final Predicate<? super K> keyPredicate; - - FilteredKeyMultimap(Multimap<K, V> unfiltered, Predicate<? super K> keyPredicate) { - super(unfiltered); - this.keyPredicate = checkNotNull(keyPredicate); - } - - @Override - Predicate<? super Entry<K, V>> entryPredicate() { - return Predicates.compose(keyPredicate, Maps.<K>keyFunction()); - } - - @Override - public int size() { - int size = 0; - for (Collection<V> collection : asMap().values()) { - size += collection.size(); - } - return size; - } - - @Override - public boolean containsKey(@Nullable Object key) { - if (unfiltered.containsKey(key)) { - @SuppressWarnings("unchecked") // k is equal to a K, if not one itself - K k = (K) key; - return keyPredicate.apply(k); - } - return false; - } - - @Override - public Collection<V> removeAll(Object key) { - return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); - } - - Collection<V> unmodifiableEmptyCollection() { - if (unfiltered instanceof SetMultimap) { - return ImmutableSet.of(); - } else { - return ImmutableList.of(); - } - } - - @Override - public void clear() { - keySet().clear(); - } - - @Override - Set<K> createKeySet() { - return Sets.filter(unfiltered.keySet(), keyPredicate); - } - - @Override - public Collection<V> get(K key) { - if (keyPredicate.apply(key)) { - return unfiltered.get(key); - } else if (unfiltered instanceof SetMultimap) { - return new AddRejectingSet<K, V>(key); - } else { - return new AddRejectingList<K, V>(key); - } - } - - static class AddRejectingSet<K, V> extends ForwardingSet<V> { - final K key; - - AddRejectingSet(K key) { - this.key = key; - } - - @Override - public boolean add(V element) { - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - public boolean addAll(Collection<? extends V> collection) { - checkNotNull(collection); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - protected Set<V> delegate() { - return Collections.emptySet(); - } - } - - static class AddRejectingList<K, V> extends ForwardingList<V> { - final K key; - - AddRejectingList(K key) { - this.key = key; - } - - @Override - public boolean add(V v) { - add(0, v); - return true; - } - - @Override - public boolean addAll(Collection<? extends V> collection) { - addAll(0, collection); - return true; - } - - @Override - public void add(int index, V element) { - checkPositionIndex(index, 0); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - public boolean addAll(int index, Collection<? extends V> elements) { - checkNotNull(elements); - checkPositionIndex(index, 0); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - protected List<V> delegate() { - return Collections.emptyList(); - } - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return Iterators.filter( - unfiltered.entries().iterator(), Predicates.compose(keyPredicate, Maps.<K>keyFunction())); - } - - @Override - Collection<Entry<K, V>> createEntries() { - return new Multimaps.Entries<K, V>() { - @Override - Multimap<K, V> multimap() { - return FilteredKeyMultimap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - - @Override - @SuppressWarnings("unchecked") - public boolean remove(@Nullable Object o) { - if (o instanceof Entry) { - Entry<?, ?> entry = (Entry<?, ?>) o; - if (unfiltered.containsEntry(entry.getKey(), entry.getValue()) - && keyPredicate.apply((K) entry.getKey())) { - return unfiltered.remove(entry.getKey(), entry.getValue()); - } - } - return false; - } - - @Override - public boolean removeAll(Collection<?> c) { - Predicate<Entry<K, ?>> combinedPredicate = Predicates.and( - Predicates.compose(keyPredicate, Maps.<K>keyFunction()), Predicates.in(c)); - return Iterators.removeIf(unfiltered.entries().iterator(), combinedPredicate); - } - - @Override - public boolean retainAll(Collection<?> c) { - Predicate<Entry<K, ?>> combinedPredicate = Predicates.and( - Predicates.compose(keyPredicate, Maps.<K>keyFunction()), - Predicates.not(Predicates.in(c))); - return Iterators.removeIf(unfiltered.entries().iterator(), combinedPredicate); - } - }; - } - - @Override - Map<K, Collection<V>> createAsMap() { - return Maps.filterKeys(unfiltered.asMap(), keyPredicate); - } - - @Override - Multiset<K> createKeys() { - return Multisets.filter(unfiltered.keys(), keyPredicate); - } -} diff --git a/guava/src/com/google/common/collect/FilteredMultimap.java b/guava/src/com/google/common/collect/FilteredMultimap.java deleted file mode 100644 index d7d2b3b..0000000 --- a/guava/src/com/google/common/collect/FilteredMultimap.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; -import com.google.common.base.Predicate; - -import java.util.Map.Entry; - -/** - * A superclass of all filtered multimap types. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class FilteredMultimap<K, V> extends AbstractMultimap<K, V> { - final Multimap<K, V> unfiltered; - - FilteredMultimap(Multimap<K, V> unfiltered) { - this.unfiltered = checkNotNull(unfiltered); - } - - abstract Predicate<? super Entry<K, V>> entryPredicate(); -} diff --git a/guava/src/com/google/common/collect/FluentIterable.java b/guava/src/com/google/common/collect/FluentIterable.java deleted file mode 100644 index 2469180..0000000 --- a/guava/src/com/google/common/collect/FluentIterable.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.SortedSet; - -import javax.annotation.Nullable; - -/** - * {@code FluentIterable} provides a rich interface for manipulating {@code Iterable} instances in a - * chained fashion. A {@code FluentIterable} can be created from an {@code Iterable}, or from a set - * of elements. The following types of methods are provided on {@code FluentIterable}: - * <ul> - * <li>chained methods which return a new {@code FluentIterable} based in some way on the contents - * of the current one (for example {@link #transform}) - * <li>conversion methods which copy the {@code FluentIterable}'s contents into a new collection or - * array (for example {@link #toList}) - * <li>element extraction methods which facilitate the retrieval of certain elements (for example - * {@link #last}) - * <li>query methods which answer questions about the {@code FluentIterable}'s contents (for example - * {@link #anyMatch}) - * </ul> - * - * <p>Here is an example that merges the lists returned by two separate database calls, transforms - * it by invoking {@code toString()} on each element, and returns the first 10 elements as an - * {@code ImmutableList}: <pre> {@code - * - * FluentIterable - * .from(database.getClientList()) - * .filter(activeInLastMonth()) - * .transform(Functions.toStringFunction()) - * .limit(10) - * .toList();}</pre> - * - * Anything which can be done using {@code FluentIterable} could be done in a different fashion - * (often with {@link Iterables}), however the use of {@code FluentIterable} makes many sets of - * operations significantly more concise. - * - * @author Marcin Mikosik - * @since 12.0 - */ -@GwtCompatible(emulated = true) -public abstract class FluentIterable<E> implements Iterable<E> { - // We store 'iterable' and use it instead of 'this' to allow Iterables to perform instanceof - // checks on the _original_ iterable when FluentIterable.from is used. - private final Iterable<E> iterable; - - /** Constructor for use by subclasses. */ - protected FluentIterable() { - this.iterable = this; - } - - FluentIterable(Iterable<E> iterable) { - this.iterable = checkNotNull(iterable); - } - - /** - * Returns a fluent iterable that wraps {@code iterable}, or {@code iterable} itself if it - * is already a {@code FluentIterable}. - */ - public static <E> FluentIterable<E> from(final Iterable<E> iterable) { - return (iterable instanceof FluentIterable) ? (FluentIterable<E>) iterable - : new FluentIterable<E>(iterable) { - @Override - public Iterator<E> iterator() { - return iterable.iterator(); - } - }; - } - - /** - * Construct a fluent iterable from another fluent iterable. This is obviously never necessary, - * but is intended to help call out cases where one migration from {@code Iterable} to - * {@code FluentIterable} has obviated the need to explicitly convert to a {@code FluentIterable}. - * - * @deprecated instances of {@code FluentIterable} don't need to be converted to - * {@code FluentIterable} - */ - @Deprecated - public static <E> FluentIterable<E> from(FluentIterable<E> iterable) { - return checkNotNull(iterable); - } - - /** - * Returns a string representation of this fluent iterable, with the format - * {@code [e1, e2, ..., en]}. - */ - @Override - public String toString() { - return Iterables.toString(iterable); - } - - /** - * Returns the number of elements in this fluent iterable. - */ - public final int size() { - return Iterables.size(iterable); - } - - /** - * Returns {@code true} if this fluent iterable contains any object for which - * {@code equals(element)} is true. - */ - public final boolean contains(@Nullable Object element) { - return Iterables.contains(iterable, element); - } - - /** - * Returns a fluent iterable whose {@code Iterator} cycles indefinitely over the elements of - * this fluent iterable. - * - * <p>That iterator supports {@code remove()} if {@code iterable.iterator()} does. After - * {@code remove()} is called, subsequent cycles omit the removed element, which is no longer in - * this fluent iterable. The iterator's {@code hasNext()} method returns {@code true} until - * this fluent iterable is empty. - * - * <p><b>Warning:</b> Typical uses of the resulting iterator may produce an infinite loop. You - * should use an explicit {@code break} or be certain that you will eventually remove all the - * elements. - */ - public final FluentIterable<E> cycle() { - return from(Iterables.cycle(iterable)); - } - - /** - * Returns the elements from this fluent iterable that satisfy a predicate. The - * resulting fluent iterable's iterator does not support {@code remove()}. - */ - public final FluentIterable<E> filter(Predicate<? super E> predicate) { - return from(Iterables.filter(iterable, predicate)); - } - - /** - * Returns the elements from this fluent iterable that are instances of class {@code type}. - * - * @param type the type of elements desired - */ - @GwtIncompatible("Class.isInstance") - public final <T> FluentIterable<T> filter(Class<T> type) { - return from(Iterables.filter(iterable, type)); - } - - /** - * Returns {@code true} if any element in this fluent iterable satisfies the predicate. - */ - public final boolean anyMatch(Predicate<? super E> predicate) { - return Iterables.any(iterable, predicate); - } - - /** - * Returns {@code true} if every element in this fluent iterable satisfies the predicate. - * If this fluent iterable is empty, {@code true} is returned. - */ - public final boolean allMatch(Predicate<? super E> predicate) { - return Iterables.all(iterable, predicate); - } - - /** - * Returns an {@link Optional} containing the first element in this fluent iterable that - * satisfies the given predicate, if such an element exists. - * - * <p><b>Warning:</b> avoid using a {@code predicate} that matches {@code null}. If {@code null} - * is matched in this fluent iterable, a {@link NullPointerException} will be thrown. - */ - public final Optional<E> firstMatch(Predicate<? super E> predicate) { - return Iterables.tryFind(iterable, predicate); - } - - /** - * Returns a fluent iterable that applies {@code function} to each element of this - * fluent iterable. - * - * <p>The returned fluent iterable's iterator supports {@code remove()} if this iterable's - * iterator does. After a successful {@code remove()} call, this fluent iterable no longer - * contains the corresponding element. - */ - public final <T> FluentIterable<T> transform(Function<? super E, T> function) { - return from(Iterables.transform(iterable, function)); - } - - /** - * Applies {@code function} to each element of this fluent iterable and returns - * a fluent iterable with the concatenated combination of results. {@code function} - * returns an Iterable of results. - * - * <p>The returned fluent iterable's iterator supports {@code remove()} if this - * function-returned iterables' iterator does. After a successful {@code remove()} call, - * the returned fluent iterable no longer contains the corresponding element. - * - * @since 13.0 (required {@code Function<E, Iterable<T>>} until 14.0) - */ - public <T> FluentIterable<T> transformAndConcat( - Function<? super E, ? extends Iterable<? extends T>> function) { - return from(Iterables.concat(transform(function))); - } - - /** - * Returns an {@link Optional} containing the first element in this fluent iterable. - * If the iterable is empty, {@code Optional.absent()} is returned. - * - * @throws NullPointerException if the first element is null; if this is a possibility, use - * {@code iterator().next()} or {@link Iterables#getFirst} instead. - */ - public final Optional<E> first() { - Iterator<E> iterator = iterable.iterator(); - return iterator.hasNext() - ? Optional.of(iterator.next()) - : Optional.<E>absent(); - } - - /** - * Returns an {@link Optional} containing the last element in this fluent iterable. - * If the iterable is empty, {@code Optional.absent()} is returned. - * - * @throws NullPointerException if the last element is null; if this is a possibility, use - * {@link Iterables#getLast} instead. - */ - public final Optional<E> last() { - // Iterables#getLast was inlined here so we don't have to throw/catch a NSEE - - // TODO(kevinb): Support a concurrently modified collection? - if (iterable instanceof List) { - List<E> list = (List<E>) iterable; - if (list.isEmpty()) { - return Optional.absent(); - } - return Optional.of(list.get(list.size() - 1)); - } - Iterator<E> iterator = iterable.iterator(); - if (!iterator.hasNext()) { - return Optional.absent(); - } - - /* - * TODO(kevinb): consider whether this "optimization" is worthwhile. Users - * with SortedSets tend to know they are SortedSets and probably would not - * call this method. - */ - if (iterable instanceof SortedSet) { - SortedSet<E> sortedSet = (SortedSet<E>) iterable; - return Optional.of(sortedSet.last()); - } - - while (true) { - E current = iterator.next(); - if (!iterator.hasNext()) { - return Optional.of(current); - } - } - } - - /** - * Returns a view of this fluent iterable that skips its first {@code numberToSkip} - * elements. If this fluent iterable contains fewer than {@code numberToSkip} elements, - * the returned fluent iterable skips all of its elements. - * - * <p>Modifications to this fluent iterable before a call to {@code iterator()} are - * reflected in the returned fluent iterable. That is, the its iterator skips the first - * {@code numberToSkip} elements that exist when the iterator is created, not when {@code skip()} - * is called. - * - * <p>The returned fluent iterable's iterator supports {@code remove()} if the - * {@code Iterator} of this fluent iterable supports it. Note that it is <i>not</i> - * possible to delete the last skipped element by immediately calling {@code remove()} on the - * returned fluent iterable's iterator, as the {@code Iterator} contract states that a call - * to {@code * remove()} before a call to {@code next()} will throw an - * {@link IllegalStateException}. - */ - public final FluentIterable<E> skip(int numberToSkip) { - return from(Iterables.skip(iterable, numberToSkip)); - } - - /** - * Creates a fluent iterable with the first {@code size} elements of this - * fluent iterable. If this fluent iterable does not contain that many elements, - * the returned fluent iterable will have the same behavior as this fluent iterable. - * The returned fluent iterable's iterator supports {@code remove()} if this - * fluent iterable's iterator does. - * - * @param size the maximum number of elements in the returned fluent iterable - * @throws IllegalArgumentException if {@code size} is negative - */ - public final FluentIterable<E> limit(int size) { - return from(Iterables.limit(iterable, size)); - } - - /** - * Determines whether this fluent iterable is empty. - */ - public final boolean isEmpty() { - return !iterable.iterator().hasNext(); - } - - /** - * Returns an {@code ImmutableList} containing all of the elements from this fluent iterable in - * proper sequence. - * - * @since 14.0 (since 12.0 as {@code toImmutableList()}). - */ - public final ImmutableList<E> toList() { - return ImmutableList.copyOf(iterable); - } - - /** - * Returns an {@code ImmutableList} containing all of the elements from this {@code - * FluentIterable} in the order specified by {@code comparator}. To produce an {@code - * ImmutableList} sorted by its natural ordering, use {@code toSortedList(Ordering.natural())}. - * - * @param comparator the function by which to sort list elements - * @throws NullPointerException if any element is null - * @since 14.0 (since 13.0 as {@code toSortedImmutableList()}). - */ - @Beta - public final ImmutableList<E> toSortedList(Comparator<? super E> comparator) { - return Ordering.from(comparator).immutableSortedCopy(iterable); - } - - /** - * Returns an {@code ImmutableSet} containing all of the elements from this fluent iterable with - * duplicates removed. - * - * @since 14.0 (since 12.0 as {@code toImmutableSet()}). - */ - public final ImmutableSet<E> toSet() { - return ImmutableSet.copyOf(iterable); - } - - /** - * Returns an {@code ImmutableSortedSet} containing all of the elements from this {@code - * FluentIterable} in the order specified by {@code comparator}, with duplicates (determined by - * {@code comparator.compare(x, y) == 0}) removed. To produce an {@code ImmutableSortedSet} sorted - * by its natural ordering, use {@code toSortedSet(Ordering.natural())}. - * - * @param comparator the function by which to sort set elements - * @throws NullPointerException if any element is null - * @since 14.0 (since 12.0 as {@code toImmutableSortedSet()}). - */ - public final ImmutableSortedSet<E> toSortedSet(Comparator<? super E> comparator) { - return ImmutableSortedSet.copyOf(comparator, iterable); - } - - /** - * Returns an immutable map for which the elements of this {@code FluentIterable} are the keys in - * the same order, mapped to values by the given function. If this iterable contains duplicate - * elements, the returned map will contain each distinct element once in the order it first - * appears. - * - * @throws NullPointerException if any element of this iterable is {@code null}, or if {@code - * valueFunction} produces {@code null} for any key - * @since 14.0 - */ - public final <V> ImmutableMap<E, V> toMap(Function<? super E, V> valueFunction) { - return Maps.toMap(iterable, valueFunction); - } - - /** - * Creates an index {@code ImmutableListMultimap} that contains the results of applying a - * specified function to each item in this {@code FluentIterable} of values. Each element of this - * iterable will be stored as a value in the resulting multimap, yielding a multimap with the same - * size as this iterable. The key used to store that value in the multimap will be the result of - * calling the function on that value. The resulting multimap is created as an immutable snapshot. - * In the returned multimap, keys appear in the order they are first encountered, and the values - * corresponding to each key appear in the same order as they are encountered. - * - * @param keyFunction the function used to produce the key for each value - * @throws NullPointerException if any of the following cases is true: - * <ul> - * <li>{@code keyFunction} is null - * <li>An element in this fluent iterable is null - * <li>{@code keyFunction} returns {@code null} for any element of this iterable - * </ul> - * @since 14.0 - */ - public final <K> ImmutableListMultimap<K, E> index(Function<? super E, K> keyFunction) { - return Multimaps.index(iterable, keyFunction); - } - - /** - * Returns an immutable map for which the {@link java.util.Map#values} are the elements of this - * {@code FluentIterable} in the given order, and each key is the product of invoking a supplied - * function on its corresponding value. - * - * @param keyFunction the function used to produce the key for each value - * @throws IllegalArgumentException if {@code keyFunction} produces the same key for more than one - * value in this fluent iterable - * @throws NullPointerException if any element of this fluent iterable is null, or if - * {@code keyFunction} produces {@code null} for any value - * @since 14.0 - */ - public final <K> ImmutableMap<K, E> uniqueIndex(Function<? super E, K> keyFunction) { - return Maps.uniqueIndex(iterable, keyFunction); - } - - /** - * Returns an {@code ImmutableList} containing all of the elements from this - * fluent iterable in proper sequence. - * - * @deprecated Use {@link #toList()} instead. This method is scheduled for removal in Guava 15.0. - */ - @Deprecated - public final ImmutableList<E> toImmutableList() { - return toList(); - } - - /** - * Returns an {@code ImmutableList} containing all of the elements from this - * {@code FluentIterable} in the order specified by {@code comparator}. To produce an - * {@code ImmutableList} sorted by its natural ordering, use - * {@code toSortedImmutableList(Ordering.natural())}. - * - * @param comparator the function by which to sort list elements - * @throws NullPointerException if any element is null - * @since 13.0 - * @deprecated Use {@link #toSortedList(Comparator)} instead. This method is scheduled for removal - * in Guava 15.0. - */ - @Deprecated - public final ImmutableList<E> toSortedImmutableList(Comparator<? super E> comparator) { - return toSortedList(comparator); - } - - /** - * Returns an {@code ImmutableSet} containing all of the elements from this - * fluent iterable with duplicates removed. - * - * @deprecated Use {@link #toSet()} instead. This method is scheduled for removal in Guava 15.0. - */ - @Deprecated - public final ImmutableSet<E> toImmutableSet() { - return toSet(); - } - - /** - * Returns an {@code ImmutableSortedSet} containing all of the elements from this - * {@code FluentIterable} in the order specified by {@code comparator}, with duplicates - * (determined by {@code comparator.compare(x, y) == 0}) removed. To produce an - * {@code ImmutableSortedSet} sorted by its natural ordering, use - * {@code toImmutableSortedSet(Ordering.natural())}. - * - * @param comparator the function by which to sort set elements - * @throws NullPointerException if any element is null - * @deprecated Use {@link #toSortedSet(Comparator)} instead. This method is scheduled for removal - * in Guava 15.0. - */ - @Deprecated - public final ImmutableSortedSet<E> toImmutableSortedSet(Comparator<? super E> comparator) { - return toSortedSet(comparator); - } - - /** - * Returns an array containing all of the elements from this fluent iterable in iteration order. - * - * @param type the type of the elements - * @return a newly-allocated array into which all the elements of this fluent iterable have - * been copied - */ - @GwtIncompatible("Array.newArray(Class, int)") - public final E[] toArray(Class<E> type) { - return Iterables.toArray(iterable, type); - } - - /** - * Copies all the elements from this fluent iterable to {@code collection}. This is equivalent to - * calling {@code Iterables.addAll(collection, this)}. - * - * @param collection the collection to copy elements to - * @return {@code collection}, for convenience - * @since 14.0 - */ - public final <C extends Collection<? super E>> C copyInto(C collection) { - checkNotNull(collection); - if (iterable instanceof Collection) { - collection.addAll(Collections2.cast(iterable)); - } else { - for (E item : iterable) { - collection.add(item); - } - } - return collection; - } - - /** - * Returns the element at the specified position in this fluent iterable. - * - * @param position position of the element to return - * @return the element at the specified position in this fluent iterable - * @throws IndexOutOfBoundsException if {@code position} is negative or greater than or equal to - * the size of this fluent iterable - */ - public final E get(int position) { - return Iterables.get(iterable, position); - } - - /** - * Function that transforms {@code Iterable<E>} into a fluent iterable. - */ - private static class FromIterableFunction<E> - implements Function<Iterable<E>, FluentIterable<E>> { - @Override - public FluentIterable<E> apply(Iterable<E> fromObject) { - return FluentIterable.from(fromObject); - } - } -} diff --git a/guava/src/com/google/common/collect/ForwardingBlockingDeque.java b/guava/src/com/google/common/collect/ForwardingBlockingDeque.java deleted file mode 100644 index a6fb43d..0000000 --- a/guava/src/com/google/common/collect/ForwardingBlockingDeque.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.Collection; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. - * Subclasses should override one or more methods to modify the behavior of the backing deque as - * desired per the <a href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><b>Warning:</b> The methods of {@code ForwardingBlockingDeque} forward - * <b>indiscriminately</b> to the methods of the delegate. For example, overriding {@link #add} - * alone <b>will not</b> change the behaviour of {@link #offer} which can lead to unexpected - * behaviour. In this case, you should override {@code offer} as well, either providing your own - * implementation, or delegating to the provided {@code standardOffer} method. - * - * <p> - * The {@code standard} methods are not guaranteed to be thread-safe, even when all of the methods - * that they depend on are thread-safe. - * - * @author Emily Soldal - * @since 14.0 - */ -public abstract class ForwardingBlockingDeque<E> - extends ForwardingDeque<E> implements BlockingDeque<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingBlockingDeque() {} - - @Override protected abstract BlockingDeque<E> delegate(); - - @Override - public int remainingCapacity() { - return delegate().remainingCapacity(); - } - - @Override - public void putFirst(E e) throws InterruptedException { - delegate().putFirst(e); - } - - @Override - public void putLast(E e) throws InterruptedException { - delegate().putLast(e); - } - - @Override - public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offerFirst(e, timeout, unit); - } - - @Override - public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offerLast(e, timeout, unit); - } - - @Override - public E takeFirst() throws InterruptedException { - return delegate().takeFirst(); - } - - @Override - public E takeLast() throws InterruptedException { - return delegate().takeLast(); - } - - @Override - public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().pollFirst(timeout, unit); - } - - @Override - public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().pollLast(timeout, unit); - } - - @Override - public void put(E e) throws InterruptedException { - delegate().put(e); - } - - @Override - public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offer(e, timeout, unit); - } - - @Override - public E take() throws InterruptedException { - return delegate().take(); - } - - @Override - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().poll(timeout, unit); - } - - @Override - public int drainTo(Collection<? super E> c) { - return delegate().drainTo(c); - } - - @Override - public int drainTo(Collection<? super E> c, int maxElements) { - return delegate().drainTo(c, maxElements); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingCollection.java b/guava/src/com/google/common/collect/ForwardingCollection.java index 79d7860..a6a46f0 100644 --- a/guava/src/com/google/common/collect/ForwardingCollection.java +++ b/guava/src/com/google/common/collect/ForwardingCollection.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; @@ -126,7 +127,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContains(@Nullable Object object) { + @Beta protected boolean standardContains(@Nullable Object object) { return Iterators.contains(iterator(), object); } @@ -137,7 +138,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContainsAll(Collection<?> collection) { + @Beta protected boolean standardContainsAll(Collection<?> collection) { for (Object o : collection) { if (!contains(o)) { return false; @@ -153,7 +154,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardAddAll(Collection<? extends E> collection) { + @Beta protected boolean standardAddAll(Collection<? extends E> collection) { return Iterators.addAll(this, collection.iterator()); } @@ -165,7 +166,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRemove(@Nullable Object object) { + @Beta protected boolean standardRemove(@Nullable Object object) { Iterator<E> iterator = iterator(); while (iterator.hasNext()) { if (Objects.equal(iterator.next(), object)) { @@ -184,7 +185,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRemoveAll(Collection<?> collection) { + @Beta protected boolean standardRemoveAll(Collection<?> collection) { return Iterators.removeAll(iterator(), collection); } @@ -196,7 +197,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRetainAll(Collection<?> collection) { + @Beta protected boolean standardRetainAll(Collection<?> collection) { return Iterators.retainAll(iterator(), collection); } @@ -208,8 +209,12 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected void standardClear() { - Iterators.clear(iterator()); + @Beta protected void standardClear() { + Iterator<E> iterator = iterator(); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } } /** @@ -220,7 +225,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardIsEmpty() { + @Beta protected boolean standardIsEmpty() { return !iterator().hasNext(); } @@ -231,7 +236,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected String standardToString() { + @Beta protected String standardToString() { return Collections2.toStringImpl(this); } @@ -242,7 +247,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected Object[] standardToArray() { + @Beta protected Object[] standardToArray() { Object[] newArray = new Object[size()]; return toArray(newArray); } @@ -254,7 +259,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected <T> T[] standardToArray(T[] array) { + @Beta protected <T> T[] standardToArray(T[] array) { return ObjectArrays.toArrayImpl(this, array); } } diff --git a/guava/src/com/google/common/collect/ForwardingDeque.java b/guava/src/com/google/common/collect/ForwardingDeque.java deleted file mode 100644 index 070f622..0000000 --- a/guava/src/com/google/common/collect/ForwardingDeque.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.Deque; -import java.util.Iterator; - -/** - * A deque which forwards all its method calls to another deque. Subclasses - * should override one or more methods to modify the behavior of the backing - * deque as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><b>Warning:</b> The methods of {@code ForwardingDeque} forward - * <b>indiscriminately</b> to the methods of the delegate. For example, - * overriding {@link #add} alone <b>will not</b> change the behavior of {@link - * #offer} which can lead to unexpected behavior. In this case, you should - * override {@code offer} as well. - * - * @author Kurt Alfred Kluever - * @since 12.0 - */ -public abstract class ForwardingDeque<E> extends ForwardingQueue<E> - implements Deque<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingDeque() {} - - @Override protected abstract Deque<E> delegate(); - - @Override - public void addFirst(E e) { - delegate().addFirst(e); - } - - @Override - public void addLast(E e) { - delegate().addLast(e); - } - - @Override - public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); - } - - @Override - public E getFirst() { - return delegate().getFirst(); - } - - @Override - public E getLast() { - return delegate().getLast(); - } - - @Override - public boolean offerFirst(E e) { - return delegate().offerFirst(e); - } - - @Override - public boolean offerLast(E e) { - return delegate().offerLast(e); - } - - @Override - public E peekFirst() { - return delegate().peekFirst(); - } - - @Override - public E peekLast() { - return delegate().peekLast(); - } - - @Override - public E pollFirst() { - return delegate().pollFirst(); - } - - @Override - public E pollLast() { - return delegate().pollLast(); - } - - @Override - public E pop() { - return delegate().pop(); - } - - @Override - public void push(E e) { - delegate().push(e); - } - - @Override - public E removeFirst() { - return delegate().removeFirst(); - } - - @Override - public E removeLast() { - return delegate().removeLast(); - } - - @Override - public boolean removeFirstOccurrence(Object o) { - return delegate().removeFirstOccurrence(o); - } - - @Override - public boolean removeLastOccurrence(Object o) { - return delegate().removeLastOccurrence(o); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableCollection.java b/guava/src/com/google/common/collect/ForwardingImmutableCollection.java deleted file mode 100644 index 90489a4..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableCollection.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010 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 com.google.common.annotations.GwtCompatible; - -/** - * Dummy class that makes the GWT serialization policy happy. It isn't used - * on the server-side. - * - * @author Hayward Chan - */ -@GwtCompatible(emulated = true) -class ForwardingImmutableCollection { - private ForwardingImmutableCollection() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableList.java b/guava/src/com/google/common/collect/ForwardingImmutableList.java deleted file mode 100644 index 2b9092e..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableList.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableList<E> { - private ForwardingImmutableList() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableMap.java b/guava/src/com/google/common/collect/ForwardingImmutableMap.java deleted file mode 100644 index a367157..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableMap.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableMap<K, V> { - private ForwardingImmutableMap() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableSet.java b/guava/src/com/google/common/collect/ForwardingImmutableSet.java deleted file mode 100644 index c7d7bf6..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableSet.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableSet<E> { - private ForwardingImmutableSet() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingList.java b/guava/src/com/google/common/collect/ForwardingList.java index 9f3cf1c..e59e662 100644 --- a/guava/src/com/google/common/collect/ForwardingList.java +++ b/guava/src/com/google/common/collect/ForwardingList.java @@ -127,7 +127,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAdd(E element){ + @Beta protected boolean standardAdd(E element){ add(size(), element); return true; } @@ -140,7 +140,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAddAll( + @Beta protected boolean standardAddAll( int index, Iterable<? extends E> elements) { return Lists.addAllImpl(this, index, elements); } @@ -152,7 +152,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardIndexOf(@Nullable Object element) { + @Beta protected int standardIndexOf(@Nullable Object element) { return Lists.indexOfImpl(this, element); } @@ -164,7 +164,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardLastIndexOf(@Nullable Object element) { + @Beta protected int standardLastIndexOf(@Nullable Object element) { return Lists.lastIndexOfImpl(this, element); } @@ -175,7 +175,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected Iterator<E> standardIterator() { + @Beta protected Iterator<E> standardIterator() { return listIterator(); } @@ -187,15 +187,14 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected ListIterator<E> standardListIterator() { + @Beta protected ListIterator<E> standardListIterator(){ return listIterator(0); } /** * A sensible default implementation of {@link #listIterator(int)}, in terms - * of {@link #size}, {@link #get(int)}, {@link #set(int, Object)}, {@link - * #add(int, Object)}, and {@link #remove(int)}. If you override any of these - * methods, you may wish to override {@link #listIterator(int)} to forward to + * of {@link #size} and {@link #get(int)}. If you override either of these + * methods you may wish to override {@link #listIterator(int)} to forward to * this implementation. * * @since 7.0 diff --git a/guava/src/com/google/common/collect/ForwardingMap.java b/guava/src/com/google/common/collect/ForwardingMap.java index be22230..9b3a489 100644 --- a/guava/src/com/google/common/collect/ForwardingMap.java +++ b/guava/src/com/google/common/collect/ForwardingMap.java @@ -86,17 +86,17 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject } @Override - public boolean containsKey(@Nullable Object key) { + public boolean containsKey(Object key) { return delegate().containsKey(key); } @Override - public boolean containsValue(@Nullable Object value) { + public boolean containsValue(Object value) { return delegate().containsValue(value); } @Override - public V get(@Nullable Object key) { + public V get(Object key) { return delegate().get(key); } @@ -141,7 +141,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected void standardPutAll(Map<? extends K, ? extends V> map) { + @Beta protected void standardPutAll(Map<? extends K, ? extends V> map) { Maps.putAllImpl(this, map); } @@ -177,8 +177,12 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected void standardClear() { - Iterators.clear(entrySet().iterator()); + @Beta protected void standardClear() { + Iterator<Entry<K, V>> entryIterator = entrySet().iterator(); + while (entryIterator.hasNext()) { + entryIterator.next(); + entryIterator.remove(); + } } /** @@ -194,7 +198,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected class StandardKeySet extends Maps.KeySet<K, V> { - /** Constructor for use by subclasses. */ public StandardKeySet() {} @Override @@ -227,7 +230,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected class StandardValues extends Maps.Values<K, V> { - /** Constructor for use by subclasses. */ public StandardValues() {} @Override @@ -244,7 +246,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContainsValue(@Nullable Object value) { + @Beta protected boolean standardContainsValue(@Nullable Object value) { return Maps.containsValueImpl(this, value); } @@ -260,7 +262,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected abstract class StandardEntrySet extends Maps.EntrySet<K, V> { - /** Constructor for use by subclasses. */ public StandardEntrySet() {} @Override @@ -276,7 +277,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardIsEmpty() { + @Beta protected boolean standardIsEmpty() { return !entrySet().iterator().hasNext(); } @@ -287,7 +288,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Maps.equalsImpl(this, object); } @@ -298,7 +299,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return Sets.hashCodeImpl(entrySet()); } @@ -309,7 +310,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected String standardToString() { + @Beta protected String standardToString() { return Maps.toStringImpl(this); } } diff --git a/guava/src/com/google/common/collect/ForwardingMapEntry.java b/guava/src/com/google/common/collect/ForwardingMapEntry.java index 4d63757..ff201a5 100644 --- a/guava/src/com/google/common/collect/ForwardingMapEntry.java +++ b/guava/src/com/google/common/collect/ForwardingMapEntry.java @@ -92,7 +92,7 @@ public abstract class ForwardingMapEntry<K, V> * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { if (object instanceof Entry) { Entry<?, ?> that = (Entry<?, ?>) object; return Objects.equal(this.getKey(), that.getKey()) @@ -108,7 +108,7 @@ public abstract class ForwardingMapEntry<K, V> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { K k = getKey(); V v = getValue(); return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); diff --git a/guava/src/com/google/common/collect/ForwardingMultiset.java b/guava/src/com/google/common/collect/ForwardingMultiset.java index 9834751..4e1ceed 100644 --- a/guava/src/com/google/common/collect/ForwardingMultiset.java +++ b/guava/src/com/google/common/collect/ForwardingMultiset.java @@ -107,7 +107,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardContains(@Nullable Object object) { + @Override @Beta protected boolean standardContains(@Nullable Object object) { return count(object) > 0; } @@ -118,8 +118,12 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected void standardClear() { - Iterators.clear(entrySet().iterator()); + @Override @Beta protected void standardClear() { + Iterator<Entry<E>> entryIterator = entrySet().iterator(); + while (entryIterator.hasNext()) { + entryIterator.next(); + entryIterator.remove(); + } } /** @@ -145,7 +149,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAdd(E element) { + @Beta protected boolean standardAdd(E element) { add(element, 1); return true; } @@ -171,7 +175,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRemove(Object element) { + @Beta @Override protected boolean standardRemove(Object element) { return remove(element, 1) > 0; } @@ -183,7 +187,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRemoveAll( + @Beta @Override protected boolean standardRemoveAll( Collection<?> elementsToRemove) { return Multisets.removeAllImpl(this, elementsToRemove); } @@ -196,7 +200,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRetainAll( + @Beta @Override protected boolean standardRetainAll( Collection<?> elementsToRetain) { return Multisets.retainAllImpl(this, elementsToRetain); } @@ -210,7 +214,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardSetCount(E element, int count) { + @Beta protected int standardSetCount(E element, int count) { return Multisets.setCountImpl(this, element, count); } @@ -222,7 +226,8 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardSetCount(E element, int oldCount, int newCount) { + @Beta protected boolean standardSetCount( + E element, int oldCount, int newCount) { return Multisets.setCountImpl(this, element, oldCount, newCount); } @@ -241,7 +246,6 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> */ @Beta protected class StandardElementSet extends Multisets.ElementSet<E> { - /** Constructor for use by subclasses. */ public StandardElementSet() {} @Override @@ -257,7 +261,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected Iterator<E> standardIterator() { + @Beta protected Iterator<E> standardIterator() { return Multisets.iteratorImpl(this); } @@ -268,7 +272,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardSize() { + @Beta protected int standardSize() { return Multisets.sizeImpl(this); } @@ -280,7 +284,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Multisets.equalsImpl(this, object); } @@ -291,7 +295,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return entrySet().hashCode(); } @@ -302,7 +306,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected String standardToString() { + @Beta @Override protected String standardToString() { return entrySet().toString(); } } diff --git a/guava/src/com/google/common/collect/ForwardingNavigableMap.java b/guava/src/com/google/common/collect/ForwardingNavigableMap.java deleted file mode 100644 index 8f371ff..0000000 --- a/guava/src/com/google/common/collect/ForwardingNavigableMap.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2012 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.collect.Maps.keyOrNull; - -import com.google.common.annotations.Beta; - -import java.util.Iterator; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.SortedMap; - -/** - * A navigable map which forwards all its method calls to another navigable map. Subclasses should - * override one or more methods to modify the behavior of the backing map as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><i>Warning:</i> The methods of {@code ForwardingNavigableMap} forward <i>indiscriminately</i> - * to the methods of the delegate. For example, overriding {@link #put} alone <i>will not</i> - * change the behavior of {@link #putAll}, which can lead to unexpected behavior. In this case, you - * should override {@code putAll} as well, either providing your own implementation, or delegating - * to the provided {@code standardPutAll} method. - * - * <p>Each of the {@code standard} methods uses the map's comparator (or the natural ordering of - * the elements, if there is no comparator) to test element equality. As a result, if the comparator - * is not consistent with equals, some of the standard implementations may violate the {@code Map} - * contract. - * - * <p>The {@code standard} methods and the collection views they return are not guaranteed to be - * thread-safe, even when all of the methods that they depend on are thread-safe. - * - * @author Louis Wasserman - * @since 12.0 - */ -public abstract class ForwardingNavigableMap<K, V> - extends ForwardingSortedMap<K, V> implements NavigableMap<K, V> { - - /** Constructor for use by subclasses. */ - protected ForwardingNavigableMap() {} - - @Override - protected abstract NavigableMap<K, V> delegate(); - - @Override - public Entry<K, V> lowerEntry(K key) { - return delegate().lowerEntry(key); - } - - /** - * A sensible definition of {@link #lowerEntry} in terms of the {@code lastEntry()} of - * {@link #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override - * {@code lowerEntry} to forward to this implementation. - */ - protected Entry<K, V> standardLowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return delegate().lowerKey(key); - } - - /** - * A sensible definition of {@link #lowerKey} in terms of {@code lowerEntry}. If you override - * {@link #lowerEntry}, you may wish to override {@code lowerKey} to forward to this - * implementation. - */ - protected K standardLowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return delegate().floorEntry(key); - } - - /** - * A sensible definition of {@link #floorEntry} in terms of the {@code lastEntry()} of - * {@link #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override - * {@code floorEntry} to forward to this implementation. - */ - protected Entry<K, V> standardFloorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return delegate().floorKey(key); - } - - /** - * A sensible definition of {@link #floorKey} in terms of {@code floorEntry}. If you override - * {@code floorEntry}, you may wish to override {@code floorKey} to forward to this - * implementation. - */ - protected K standardFloorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return delegate().ceilingEntry(key); - } - - /** - * A sensible definition of {@link #ceilingEntry} in terms of the {@code firstEntry()} of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override - * {@code ceilingEntry} to forward to this implementation. - */ - protected Entry<K, V> standardCeilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return delegate().ceilingKey(key); - } - - /** - * A sensible definition of {@link #ceilingKey} in terms of {@code ceilingEntry}. If you override - * {@code ceilingEntry}, you may wish to override {@code ceilingKey} to forward to this - * implementation. - */ - protected K standardCeilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return delegate().higherEntry(key); - } - - /** - * A sensible definition of {@link #higherEntry} in terms of the {@code firstEntry()} of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override - * {@code higherEntry} to forward to this implementation. - */ - protected Entry<K, V> standardHigherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return delegate().higherKey(key); - } - - /** - * A sensible definition of {@link #higherKey} in terms of {@code higherEntry}. If you override - * {@code higherEntry}, you may wish to override {@code higherKey} to forward to this - * implementation. - */ - protected K standardHigherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return delegate().firstEntry(); - } - - /** - * A sensible definition of {@link #firstEntry} in terms of the {@code iterator()} of - * {@link #entrySet}. If you override {@code entrySet}, you may wish to override - * {@code firstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardFirstEntry() { - return Iterables.getFirst(entrySet(), null); - } - - /** - * A sensible definition of {@link #firstKey} in terms of {@code firstEntry}. If you override - * {@code firstEntry}, you may wish to override {@code firstKey} to forward to this - * implementation. - */ - protected K standardFirstKey() { - Entry<K, V> entry = firstEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public Entry<K, V> lastEntry() { - return delegate().lastEntry(); - } - - /** - * A sensible definition of {@link #lastEntry} in terms of the {@code iterator()} of the - * {@link #entrySet} of {@link #descendingMap}. If you override {@code descendingMap}, you may - * wish to override {@code lastEntry} to forward to this implementation. - */ - protected Entry<K, V> standardLastEntry() { - return Iterables.getFirst(descendingMap().entrySet(), null); - } - - /** - * A sensible definition of {@link #lastKey} in terms of {@code lastEntry}. If you override - * {@code lastEntry}, you may wish to override {@code lastKey} to forward to this implementation. - */ - protected K standardLastKey() { - Entry<K, V> entry = lastEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public Entry<K, V> pollFirstEntry() { - return delegate().pollFirstEntry(); - } - - /** - * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of - * {@code entrySet}. If you override {@code entrySet}, you may wish to override - * {@code pollFirstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardPollFirstEntry() { - return Iterators.pollNext(entrySet().iterator()); - } - - @Override - public Entry<K, V> pollLastEntry() { - return delegate().pollLastEntry(); - } - - /** - * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of the - * {@code entrySet} of {@code descendingMap}. If you override {@code descendingMap}, you may wish - * to override {@code pollFirstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardPollLastEntry() { - return Iterators.pollNext(descendingMap().entrySet().iterator()); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return delegate().descendingMap(); - } - - /** - * A sensible implementation of {@link NavigableMap#descendingMap} in terms of the methods of - * this {@code NavigableMap}. In many cases, you may wish to override - * {@link ForwardingNavigableMap#descendingMap} to forward to this implementation or a subclass - * thereof. - * - * <p>In particular, this map iterates over entries with repeated calls to - * {@link NavigableMap#lowerEntry}. If a more efficient means of iteration is available, you may - * wish to override the {@code entryIterator()} method of this class. - * - * @since 12.0 - */ - @Beta - protected class StandardDescendingMap extends Maps.DescendingMap<K, V> { - /** Constructor for use by subclasses. */ - public StandardDescendingMap() {} - - @Override - NavigableMap<K, V> forward() { - return ForwardingNavigableMap.this; - } - - @Override - protected Iterator<Entry<K, V>> entryIterator() { - return new Iterator<Entry<K, V>>() { - private Entry<K, V> toRemove = null; - private Entry<K, V> nextOrNull = forward().lastEntry(); - - @Override - public boolean hasNext() { - return nextOrNull != null; - } - - @Override - public java.util.Map.Entry<K, V> next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - try { - return nextOrNull; - } finally { - toRemove = nextOrNull; - nextOrNull = forward().lowerEntry(nextOrNull.getKey()); - } - } - - @Override - public void remove() { - Iterators.checkRemove(toRemove != null); - forward().remove(toRemove.getKey()); - toRemove = null; - } - }; - } - } - - @Override - public NavigableSet<K> navigableKeySet() { - return delegate().navigableKeySet(); - } - - /** - * A sensible implementation of {@link NavigableMap#navigableKeySet} in terms of the methods of - * this {@code NavigableMap}. In many cases, you may wish to override - * {@link ForwardingNavigableMap#navigableKeySet} to forward to this implementation or a subclass - * thereof. - * - * @since 12.0 - */ - @Beta - protected class StandardNavigableKeySet extends Maps.NavigableKeySet<K, V> { - /** Constructor for use by subclasses. */ - public StandardNavigableKeySet() { - super(ForwardingNavigableMap.this); - } - } - - @Override - public NavigableSet<K> descendingKeySet() { - return delegate().descendingKeySet(); - } - - /** - * A sensible definition of {@link #descendingKeySet} as the {@code navigableKeySet} of - * {@link #descendingMap}. (The {@link StandardDescendingMap} implementation implements - * {@code navigableKeySet} on its own, so as not to cause an infinite loop.) If you override - * {@code descendingMap}, you may wish to override {@code descendingKeySet} to forward to this - * implementation. - */ - @Beta - protected NavigableSet<K> standardDescendingKeySet() { - return descendingMap().navigableKeySet(); - } - - /** - * A sensible definition of {@link #subMap(Object, Object)} in terms of - * {@link #subMap(Object, boolean, Object, boolean)}. If you override - * {@code subMap(K, boolean, K, boolean)}, you may wish to override {@code subMap} to forward to - * this implementation. - */ - @Override - protected SortedMap<K, V> standardSubMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return delegate().subMap(fromKey, fromInclusive, toKey, toInclusive); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return delegate().headMap(toKey, inclusive); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return delegate().tailMap(fromKey, inclusive); - } - - /** - * A sensible definition of {@link #headMap(Object)} in terms of - * {@link #headMap(Object, boolean)}. If you override {@code headMap(K, boolean)}, you may wish - * to override {@code headMap} to forward to this implementation. - */ - protected SortedMap<K, V> standardHeadMap(K toKey) { - return headMap(toKey, false); - } - - /** - * A sensible definition of {@link #tailMap(Object)} in terms of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap(K, boolean)}, you may wish - * to override {@code tailMap} to forward to this implementation. - */ - protected SortedMap<K, V> standardTailMap(K fromKey) { - return tailMap(fromKey, true); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingNavigableSet.java b/guava/src/com/google/common/collect/ForwardingNavigableSet.java deleted file mode 100644 index dff5ea0..0000000 --- a/guava/src/com/google/common/collect/ForwardingNavigableSet.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.Beta; - -import java.util.Iterator; -import java.util.NavigableSet; -import java.util.SortedSet; - -/** - * A navigable set which forwards all its method calls to another navigable set. Subclasses should - * override one or more methods to modify the behavior of the backing set as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><i>Warning:</i> The methods of {@code ForwardingNavigableSet} forward <i>indiscriminately</i> - * to the methods of the delegate. For example, overriding {@link #add} alone <i>will not</i> - * change the behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you - * should override {@code addAll} as well, either providing your own implementation, or delegating - * to the provided {@code standardAddAll} method. - * - * <p>Each of the {@code standard} methods uses the set's comparator (or the natural ordering of - * the elements, if there is no comparator) to test element equality. As a result, if the - * comparator is not consistent with equals, some of the standard implementations may violate the - * {@code Set} contract. - * - * <p>The {@code standard} methods and the collection views they return are not guaranteed to be - * thread-safe, even when all of the methods that they depend on are thread-safe. - * - * @author Louis Wasserman - * @since 12.0 - */ -public abstract class ForwardingNavigableSet<E> - extends ForwardingSortedSet<E> implements NavigableSet<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingNavigableSet() {} - - @Override - protected abstract NavigableSet<E> delegate(); - - @Override - public E lower(E e) { - return delegate().lower(e); - } - - /** - * A sensible definition of {@link #lower} in terms of the {@code descendingIterator} method of - * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may - * wish to override {@link #lower} to forward to this implementation. - */ - protected E standardLower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - @Override - public E floor(E e) { - return delegate().floor(e); - } - - /** - * A sensible definition of {@link #floor} in terms of the {@code descendingIterator} method of - * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may - * wish to override {@link #floor} to forward to this implementation. - */ - protected E standardFloor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - @Override - public E ceiling(E e) { - return delegate().ceiling(e); - } - - /** - * A sensible definition of {@link #ceiling} in terms of the {@code iterator} method of - * {@link #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may - * wish to override {@link #ceiling} to forward to this implementation. - */ - protected E standardCeiling(E e) { - return Iterators.getNext(tailSet(e, true).iterator(), null); - } - - @Override - public E higher(E e) { - return delegate().higher(e); - } - - /** - * A sensible definition of {@link #higher} in terms of the {@code iterator} method of - * {@link #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may - * wish to override {@link #higher} to forward to this implementation. - */ - protected E standardHigher(E e) { - return Iterators.getNext(tailSet(e, false).iterator(), null); - } - - @Override - public E pollFirst() { - return delegate().pollFirst(); - } - - /** - * A sensible definition of {@link #pollFirst} in terms of the {@code iterator} method. If you - * override {@link #iterator} you may wish to override {@link #pollFirst} to forward to this - * implementation. - */ - protected E standardPollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public E pollLast() { - return delegate().pollLast(); - } - - /** - * A sensible definition of {@link #pollLast} in terms of the {@code descendingIterator} method. - * If you override {@link #descendingIterator} you may wish to override {@link #pollLast} to - * forward to this implementation. - */ - protected E standardPollLast() { - return Iterators.pollNext(descendingIterator()); - } - - protected E standardFirst() { - return iterator().next(); - } - - protected E standardLast() { - return descendingIterator().next(); - } - - @Override - public NavigableSet<E> descendingSet() { - return delegate().descendingSet(); - } - - /** - * A sensible implementation of {@link NavigableSet#descendingSet} in terms of the other methods - * of {@link NavigableSet}, notably including {@link NavigableSet#descendingIterator}. - * - * <p>In many cases, you may wish to override {@link ForwardingNavigableSet#descendingSet} to - * forward to this implementation or a subclass thereof. - * - * @since 12.0 - */ - @Beta - protected class StandardDescendingSet extends Sets.DescendingSet<E> { - /** Constructor for use by subclasses. */ - public StandardDescendingSet() { - super(ForwardingNavigableSet.this); - } - } - - @Override - public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return delegate().subSet(fromElement, fromInclusive, toElement, toInclusive); - } - - /** - * A sensible definition of {@link #subSet(Object, boolean, Object, boolean)} in terms of the - * {@code headSet} and {@code tailSet} methods. In many cases, you may wish to override - * {@link #subSet(Object, boolean, Object, boolean)} to forward to this implementation. - */ - @Beta - protected NavigableSet<E> standardSubSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive); - } - - /** - * A sensible definition of {@link #subSet(Object, Object)} in terms of the - * {@link #subSet(Object, boolean, Object, boolean)} method. If you override - * {@link #subSet(Object, boolean, Object, boolean)}, you may wish to override - * {@link #subSet(Object, Object)} to forward to this implementation. - */ - @Override - protected SortedSet<E> standardSubSet(E fromElement, E toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return delegate().headSet(toElement, inclusive); - } - - /** - * A sensible definition of {@link #headSet(Object)} in terms of the - * {@link #headSet(Object, boolean)} method. If you override - * {@link #headSet(Object, boolean)}, you may wish to override - * {@link #headSet(Object)} to forward to this implementation. - */ - protected SortedSet<E> standardHeadSet(E toElement) { - return headSet(toElement, false); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return delegate().tailSet(fromElement, inclusive); - } - - /** - * A sensible definition of {@link #tailSet(Object)} in terms of the - * {@link #tailSet(Object, boolean)} method. If you override - * {@link #tailSet(Object, boolean)}, you may wish to override - * {@link #tailSet(Object)} to forward to this implementation. - */ - protected SortedSet<E> standardTailSet(E fromElement) { - return tailSet(fromElement, true); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingQueue.java b/guava/src/com/google/common/collect/ForwardingQueue.java index 569880a..3d30aaf 100644 --- a/guava/src/com/google/common/collect/ForwardingQueue.java +++ b/guava/src/com/google/common/collect/ForwardingQueue.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.NoSuchElementException; @@ -82,7 +83,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardOffer(E e) { + @Beta protected boolean standardOffer(E e) { try { return add(e); } catch (IllegalStateException caught) { @@ -97,7 +98,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected E standardPeek() { + @Beta protected E standardPeek() { try { return element(); } catch (NoSuchElementException caught) { @@ -112,7 +113,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected E standardPoll() { + @Beta protected E standardPoll() { try { return remove(); } catch (NoSuchElementException caught) { diff --git a/guava/src/com/google/common/collect/ForwardingSet.java b/guava/src/com/google/common/collect/ForwardingSet.java index e1a4485..4c3dccd 100644 --- a/guava/src/com/google/common/collect/ForwardingSet.java +++ b/guava/src/com/google/common/collect/ForwardingSet.java @@ -16,11 +16,9 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import java.util.Collection; import java.util.Set; import javax.annotation.Nullable; @@ -64,26 +62,13 @@ public abstract class ForwardingSet<E> extends ForwardingCollection<E> } /** - * A sensible definition of {@link #removeAll} in terms of {@link #iterator} - * and {@link #remove}. If you override {@code iterator} or {@code remove}, - * you may wish to override {@link #removeAll} to forward to this - * implementation. - * - * @since 7.0 (this version overrides the {@code ForwardingCollection} version as of 12.0) - */ - @Override - protected boolean standardRemoveAll(Collection<?> collection) { - return Sets.removeAllImpl(this, checkNotNull(collection)); // for GWT - } - - /** * A sensible definition of {@link #equals} in terms of {@link #size} and * {@link #containsAll}. If you override either of those methods, you may wish * to override {@link #equals} to forward to this implementation. * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Sets.equalsImpl(this, object); } @@ -94,7 +79,7 @@ public abstract class ForwardingSet<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return Sets.hashCodeImpl(this); } } diff --git a/guava/src/com/google/common/collect/ForwardingTable.java b/guava/src/com/google/common/collect/ForwardingTable.java index 92cc876..2c59705 100644 --- a/guava/src/com/google/common/collect/ForwardingTable.java +++ b/guava/src/com/google/common/collect/ForwardingTable.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Collection; @@ -31,6 +32,7 @@ import java.util.Set; * @author Gregory Kick * @since 7.0 */ +@Beta @GwtCompatible public abstract class ForwardingTable<R, C, V> extends ForwardingObject implements Table<R, C, V> { diff --git a/guava/src/com/google/common/collect/GeneralRange.java b/guava/src/com/google/common/collect/GeneralRange.java index cb3b2c8..5a3980f 100644 --- a/guava/src/com/google/common/collect/GeneralRange.java +++ b/guava/src/com/google/common/collect/GeneralRange.java @@ -1,11 +1,11 @@ /* * 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 @@ -19,21 +19,21 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.BoundType.CLOSED; import static com.google.common.collect.BoundType.OPEN; -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; - import java.io.Serializable; import java.util.Comparator; import javax.annotation.Nullable; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + /** * A generalized interval on any ordering, for internal use. Supports {@code null}. Unlike * {@link Range}, this allows the use of an arbitrary comparator. This is designed for use in the * implementation of subcollections of sorted collection types. - * + * * <p>Whenever possible, use {@code Range} instead, which is better supported. - * + * * @author Louis Wasserman */ @GwtCompatible(serializable = true) @@ -138,26 +138,26 @@ final class GeneralRange<T> implements Serializable { } boolean isEmpty() { - return (hasUpperBound() && tooLow(getUpperEndpoint())) - || (hasLowerBound() && tooHigh(getLowerEndpoint())); + return (hasUpperBound() && tooLow(upperEndpoint)) + || (hasLowerBound() && tooHigh(lowerEndpoint)); } boolean tooLow(@Nullable T t) { if (!hasLowerBound()) { return false; } - T lbound = getLowerEndpoint(); + T lbound = lowerEndpoint; int cmp = comparator.compare(t, lbound); - return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN); + return cmp < 0 | (cmp == 0 & lowerBoundType == OPEN); } boolean tooHigh(@Nullable T t) { if (!hasUpperBound()) { return false; } - T ubound = getUpperEndpoint(); + T ubound = upperEndpoint; int cmp = comparator.compare(t, ubound); - return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN); + return cmp > 0 | (cmp == 0 & upperBoundType == OPEN); } boolean contains(@Nullable T t) { @@ -173,33 +173,33 @@ final class GeneralRange<T> implements Serializable { boolean hasLowBound = this.hasLowerBound; @Nullable - T lowEnd = getLowerEndpoint(); - BoundType lowType = getLowerBoundType(); + T lowEnd = lowerEndpoint; + BoundType lowType = lowerBoundType; if (!hasLowerBound()) { hasLowBound = other.hasLowerBound; - lowEnd = other.getLowerEndpoint(); - lowType = other.getLowerBoundType(); + lowEnd = other.lowerEndpoint; + lowType = other.lowerBoundType; } 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(); + int cmp = comparator.compare(lowerEndpoint, other.lowerEndpoint); + if (cmp < 0 || (cmp == 0 && other.lowerBoundType == OPEN)) { + lowEnd = other.lowerEndpoint; + lowType = other.lowerBoundType; } } boolean hasUpBound = this.hasUpperBound; @Nullable - T upEnd = getUpperEndpoint(); - BoundType upType = getUpperBoundType(); + T upEnd = upperEndpoint; + BoundType upType = upperBoundType; if (!hasUpperBound()) { hasUpBound = other.hasUpperBound; - upEnd = other.getUpperEndpoint(); - upType = other.getUpperBoundType(); + upEnd = other.upperEndpoint; + upType = other.upperBoundType; } 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(); + int cmp = comparator.compare(upperEndpoint, other.upperEndpoint); + if (cmp > 0 || (cmp == 0 && other.upperBoundType == OPEN)) { + upEnd = other.upperEndpoint; + upType = other.upperBoundType; } } @@ -221,18 +221,18 @@ final class GeneralRange<T> implements Serializable { 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()); + && hasUpperBound == r.hasUpperBound && lowerBoundType.equals(r.lowerBoundType) + && upperBoundType.equals(r.upperBoundType) + && Objects.equal(lowerEndpoint, r.lowerEndpoint) + && Objects.equal(upperEndpoint, r.upperEndpoint); } return false; } @Override public int hashCode() { - return Objects.hashCode(comparator, getLowerEndpoint(), getLowerBoundType(), getUpperEndpoint(), - getUpperBoundType()); + return Objects.hashCode(comparator, lowerEndpoint, lowerBoundType, upperEndpoint, + upperBoundType); } private transient GeneralRange<T> reverse; @@ -240,12 +240,12 @@ final class GeneralRange<T> implements Serializable { /** * Returns the same range relative to the reversed comparator. */ - GeneralRange<T> reverse() { + public 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 = + new GeneralRange<T>(Ordering.from(comparator).reverse(), hasUpperBound, upperEndpoint, + upperBoundType, hasLowerBound, lowerEndpoint, lowerBoundType); result.reverse = this; return this.reverse = result; } @@ -254,30 +254,35 @@ final class GeneralRange<T> implements Serializable { @Override public String toString() { - return new StringBuilder() - .append(comparator) - .append(":") - .append(lowerBoundType == CLOSED ? '[' : '(') - .append(hasLowerBound ? lowerEndpoint : "-\u221e") - .append(',') - .append(hasUpperBound ? upperEndpoint : "\u221e") - .append(upperBoundType == CLOSED ? ']' : ')') - .toString(); - } - - T getLowerEndpoint() { - return lowerEndpoint; - } - - BoundType getLowerBoundType() { - return lowerBoundType; - } - - T getUpperEndpoint() { - return upperEndpoint; - } - - BoundType getUpperBoundType() { - return upperBoundType; + StringBuilder builder = new StringBuilder(); + builder.append(comparator).append(":"); + switch (lowerBoundType) { + case CLOSED: + builder.append('['); + break; + case OPEN: + builder.append('('); + break; + } + if (hasLowerBound()) { + builder.append(lowerEndpoint); + } else { + builder.append("-\u221e"); + } + builder.append(','); + if (hasUpperBound()) { + builder.append(upperEndpoint); + } else { + builder.append("\u221e"); + } + switch (upperBoundType) { + case CLOSED: + builder.append(']'); + break; + case OPEN: + builder.append(')'); + break; + } + return builder.toString(); } } diff --git a/guava/src/com/google/common/collect/GenericMapMaker.java b/guava/src/com/google/common/collect/GenericMapMaker.java index 0b0a290..8c5bbeb 100644 --- a/guava/src/com/google/common/collect/GenericMapMaker.java +++ b/guava/src/com/google/common/collect/GenericMapMaker.java @@ -62,6 +62,12 @@ public abstract class GenericMapMaker<K0, V0> { abstract GenericMapMaker<K0, V0> keyEquivalence(Equivalence<Object> equivalence); /** + * See {@link MapMaker#valueEquivalence}. + */ + @GwtIncompatible("To be supported") + abstract GenericMapMaker<K0, V0> valueEquivalence(Equivalence<Object> equivalence); + + /** * See {@link MapMaker#initialCapacity}. */ public abstract GenericMapMaker<K0, V0> initialCapacity(int initialCapacity); @@ -72,6 +78,11 @@ public abstract class GenericMapMaker<K0, V0> { abstract GenericMapMaker<K0, V0> maximumSize(int maximumSize); /** + * See {@link MapMaker#strongKeys}. + */ + abstract GenericMapMaker<K0, V0> strongKeys(); + + /** * See {@link MapMaker#concurrencyLevel}. */ public abstract GenericMapMaker<K0, V0> concurrencyLevel(int concurrencyLevel); @@ -83,6 +94,18 @@ public abstract class GenericMapMaker<K0, V0> { public abstract GenericMapMaker<K0, V0> weakKeys(); /** + * See {@link MapMaker#strongValues}. + */ + abstract GenericMapMaker<K0, V0> strongValues(); + + /** + * See {@link MapMaker#softKeys}. + */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + public abstract GenericMapMaker<K0, V0> softKeys(); + + /** * See {@link MapMaker#weakValues}. */ @GwtIncompatible("java.lang.ref.WeakReference") @@ -95,6 +118,13 @@ public abstract class GenericMapMaker<K0, V0> { public abstract GenericMapMaker<K0, V0> softValues(); /** + * See {@link MapMaker#expiration}. + */ + @Deprecated + public + abstract GenericMapMaker<K0, V0> expiration(long duration, TimeUnit unit); + + /** * See {@link MapMaker#expireAfterWrite}. */ abstract GenericMapMaker<K0, V0> expireAfterWrite(long duration, TimeUnit unit); diff --git a/guava/src/com/google/common/collect/HashBasedTable.java b/guava/src/com/google/common/collect/HashBasedTable.java index 4944174..8c92ec1 100644 --- a/guava/src/com/google/common/collect/HashBasedTable.java +++ b/guava/src/com/google/common/collect/HashBasedTable.java @@ -18,6 +18,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Supplier; @@ -44,15 +45,12 @@ import javax.annotation.Nullable; * <p>Note that this implementation is not synchronized. If multiple threads * access this table concurrently and one of the threads modifies the table, it * must be synchronized externally. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table"> - * {@code Table}</a>. * * @author Jared Levy * @since 7.0 */ @GwtCompatible(serializable = true) +@Beta public class HashBasedTable<R, C, V> extends StandardTable<R, C, V> { private static class Factory<C, V> implements Supplier<Map<C, V>>, Serializable { diff --git a/guava/src/com/google/common/collect/HashBiMap.java b/guava/src/com/google/common/collect/HashBiMap.java index 0620f45..26d11e1 100644 --- a/guava/src/com/google/common/collect/HashBiMap.java +++ b/guava/src/com/google/common/collect/HashBiMap.java @@ -1,657 +1,97 @@ /* * 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 + * 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. + * 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.checkState; - import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Objects; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.Arrays; -import java.util.ConcurrentModificationException; -import java.util.Iterator; +import java.util.HashMap; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; import javax.annotation.Nullable; /** - * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A - * {@code HashBiMap} and its inverse are both serializable. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap"> {@code BiMap} - * </a>. + * A {@link BiMap} backed by two {@link HashMap} instances. This implementation + * allows null keys and values. A {@code HashBiMap} and its inverse are both + * serializable. * - * @author Louis Wasserman * @author Mike Bostock * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(emulated = true) -public final class HashBiMap<K, V> extends AbstractMap<K, V> implements BiMap<K, V>, Serializable { +public final class HashBiMap<K, V> extends AbstractBiMap<K, V> { /** - * Returns a new, empty {@code HashBiMap} with the default initial capacity (16). + * Returns a new, empty {@code HashBiMap} with the default initial capacity + * (16). */ public static <K, V> HashBiMap<K, V> create() { - return create(16); + return new HashBiMap<K, V>(); } /** * Constructs a new, empty bimap with the specified expected size. * * @param expectedSize the expected number of entries - * @throws IllegalArgumentException if the specified expected size is negative + * @throws IllegalArgumentException if the specified expected size is + * negative */ public static <K, V> HashBiMap<K, V> create(int expectedSize) { return new HashBiMap<K, V>(expectedSize); } /** - * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an - * initial capacity sufficient to hold the mappings in the specified map. + * Constructs a new bimap containing initial values from {@code map}. The + * bimap is created with an initial capacity sufficient to hold the mappings + * in the specified map. */ - public static <K, V> HashBiMap<K, V> create(Map<? extends K, ? extends V> map) { + public static <K, V> HashBiMap<K, V> create( + Map<? extends K, ? extends V> map) { HashBiMap<K, V> bimap = create(map.size()); bimap.putAll(map); return bimap; } - private static final class BiEntry<K, V> { - final K key; - final int keyHash; - - final V value; - final int valueHash; - - @Nullable - BiEntry<K, V> nextInKToVBucket; - - @Nullable - BiEntry<K, V> nextInVToKBucket; - - BiEntry(K key, int keyHash, V value, int valueHash) { - this.key = key; - this.keyHash = keyHash; - this.value = value; - this.valueHash = valueHash; - } + private HashBiMap() { + super(new HashMap<K, V>(), new HashMap<V, K>()); } - private static final double LOAD_FACTOR = 1.0; - - private transient BiEntry<K, V>[] hashTableKToV; - private transient BiEntry<K, V>[] hashTableVToK; - private transient int size; - private transient int mask; - private transient int modCount; - private HashBiMap(int expectedSize) { - init(expectedSize); - } - - private void init(int expectedSize) { - checkArgument(expectedSize >= 0, "expectedSize must be >= 0 but was %s", expectedSize); - int tableSize = Hashing.closedTableSize(expectedSize, LOAD_FACTOR); - this.hashTableKToV = createTable(tableSize); - this.hashTableVToK = createTable(tableSize); - this.mask = tableSize - 1; - this.modCount = 0; - this.size = 0; - } - - /** - * Finds and removes {@code entry} from the bucket linked lists in both the - * key-to-value direction and the value-to-key direction. - */ - private void delete(BiEntry<K, V> entry) { - int keyBucket = entry.keyHash & mask; - BiEntry<K, V> prevBucketEntry = null; - for (BiEntry<K, V> bucketEntry = hashTableKToV[keyBucket]; true; - bucketEntry = bucketEntry.nextInKToVBucket) { - if (bucketEntry == entry) { - if (prevBucketEntry == null) { - hashTableKToV[keyBucket] = entry.nextInKToVBucket; - } else { - prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket; - } - break; - } - prevBucketEntry = bucketEntry; - } - - int valueBucket = entry.valueHash & mask; - prevBucketEntry = null; - for (BiEntry<K, V> bucketEntry = hashTableVToK[valueBucket];; - bucketEntry = bucketEntry.nextInVToKBucket) { - if (bucketEntry == entry) { - if (prevBucketEntry == null) { - hashTableVToK[valueBucket] = entry.nextInVToKBucket; - } else { - prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket; - } - break; - } - prevBucketEntry = bucketEntry; - } - - size--; - modCount++; - } - - private void insert(BiEntry<K, V> entry) { - int keyBucket = entry.keyHash & mask; - entry.nextInKToVBucket = hashTableKToV[keyBucket]; - hashTableKToV[keyBucket] = entry; - - int valueBucket = entry.valueHash & mask; - entry.nextInVToKBucket = hashTableVToK[valueBucket]; - hashTableVToK[valueBucket] = entry; - - size++; - modCount++; - } - - private static int hash(@Nullable Object o) { - return Hashing.smear((o == null) ? 0 : o.hashCode()); + super( + Maps.<K, V>newHashMapWithExpectedSize(expectedSize), + Maps.<V, K>newHashMapWithExpectedSize(expectedSize)); } - private BiEntry<K, V> seekByKey(@Nullable Object key, int keyHash) { - for (BiEntry<K, V> entry = hashTableKToV[keyHash & mask]; entry != null; - entry = entry.nextInKToVBucket) { - if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { - return entry; - } - } - return null; - } - - private BiEntry<K, V> seekByValue(@Nullable Object value, int valueHash) { - for (BiEntry<K, V> entry = hashTableVToK[valueHash & mask]; entry != null; - entry = entry.nextInVToKBucket) { - if (valueHash == entry.valueHash && Objects.equal(value, entry.value)) { - return entry; - } - } - return null; - } - - @Override - public boolean containsKey(@Nullable Object key) { - return seekByKey(key, hash(key)) != null; - } - - @Override - public boolean containsValue(@Nullable Object value) { - return seekByValue(value, hash(value)) != null; - } + // Override these two methods to show that keys and values may be null - @Nullable - @Override - public V get(@Nullable Object key) { - BiEntry<K, V> entry = seekByKey(key, hash(key)); - return (entry == null) ? null : entry.value; + @Override public V put(@Nullable K key, @Nullable V value) { + return super.put(key, value); } - @Override - public V put(@Nullable K key, @Nullable V value) { - return put(key, value, false); - } - - @Override - public V forcePut(@Nullable K key, @Nullable V value) { - return put(key, value, true); - } - - private V put(@Nullable K key, @Nullable V value, boolean force) { - int keyHash = hash(key); - int valueHash = hash(value); - - BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash); - if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash - && Objects.equal(value, oldEntryForKey.value)) { - return value; - } - - BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash); - if (oldEntryForValue != null) { - if (force) { - delete(oldEntryForValue); - } else { - throw new IllegalArgumentException("value already present: " + value); - } - } - - if (oldEntryForKey != null) { - delete(oldEntryForKey); - } - BiEntry<K, V> newEntry = new BiEntry<K, V>(key, keyHash, value, valueHash); - insert(newEntry); - rehashIfNecessary(); - return (oldEntryForKey == null) ? null : oldEntryForKey.value; - } - - @Nullable - private K putInverse(@Nullable V value, @Nullable K key, boolean force) { - int valueHash = hash(value); - int keyHash = hash(key); - - BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash); - if (oldEntryForValue != null && keyHash == oldEntryForValue.keyHash - && Objects.equal(key, oldEntryForValue.key)) { - return key; - } - - BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash); - if (oldEntryForKey != null) { - if (force) { - delete(oldEntryForKey); - } else { - throw new IllegalArgumentException("value already present: " + key); - } - } - - if (oldEntryForValue != null) { - delete(oldEntryForValue); - } - BiEntry<K, V> newEntry = new BiEntry<K, V>(key, keyHash, value, valueHash); - insert(newEntry); - rehashIfNecessary(); - return (oldEntryForValue == null) ? null : oldEntryForValue.key; - } - - private void rehashIfNecessary() { - BiEntry<K, V>[] oldKToV = hashTableKToV; - if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { - int newTableSize = oldKToV.length * 2; - - this.hashTableKToV = createTable(newTableSize); - this.hashTableVToK = createTable(newTableSize); - this.mask = newTableSize - 1; - this.size = 0; - - for (int bucket = 0; bucket < oldKToV.length; bucket++) { - BiEntry<K, V> entry = oldKToV[bucket]; - while (entry != null) { - BiEntry<K, V> nextEntry = entry.nextInKToVBucket; - insert(entry); - entry = nextEntry; - } - } - this.modCount++; - } - } - - @SuppressWarnings("unchecked") - private BiEntry<K, V>[] createTable(int length) { - return new BiEntry[length]; - } - - @Override - public V remove(@Nullable Object key) { - BiEntry<K, V> entry = seekByKey(key, hash(key)); - if (entry == null) { - return null; - } else { - delete(entry); - return entry.value; - } - } - - @Override - public void clear() { - size = 0; - Arrays.fill(hashTableKToV, null); - Arrays.fill(hashTableVToK, null); - modCount++; - } - - @Override - public int size() { - return size; - } - - abstract class Itr<T> implements Iterator<T> { - int nextBucket = 0; - BiEntry<K, V> next = null; - BiEntry<K, V> toRemove = null; - int expectedModCount = modCount; - - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForConcurrentModification(); - if (next != null) { - return true; - } - while (nextBucket < hashTableKToV.length) { - if (hashTableKToV[nextBucket] != null) { - next = hashTableKToV[nextBucket++]; - return true; - } - nextBucket++; - } - return false; - } - - @Override - public T next() { - checkForConcurrentModification(); - if (!hasNext()) { - throw new NoSuchElementException(); - } - - BiEntry<K, V> entry = next; - next = entry.nextInKToVBucket; - toRemove = entry; - return output(entry); - } - - @Override - public void remove() { - checkForConcurrentModification(); - checkState(toRemove != null, "Only one remove() call allowed per call to next"); - delete(toRemove); - expectedModCount = modCount; - toRemove = null; - } - - abstract T output(BiEntry<K, V> entry); - } - - @Override - public Set<K> keySet() { - return new KeySet(); - } - - private final class KeySet extends Maps.KeySet<K, V> { - @Override - Map<K, V> map() { - return HashBiMap.this; - } - - @Override - public Iterator<K> iterator() { - return new Itr<K>() { - @Override - K output(BiEntry<K, V> entry) { - return entry.key; - } - }; - } - - @Override - public boolean remove(@Nullable Object o) { - BiEntry<K, V> entry = seekByKey(o, hash(o)); - if (entry == null) { - return false; - } else { - delete(entry); - return true; - } - } - } - - @Override - public Set<V> values() { - return inverse().keySet(); - } - - @Override - public Set<Entry<K, V>> entrySet() { - return new EntrySet(); - } - - private final class EntrySet extends Maps.EntrySet<K, V> { - @Override - Map<K, V> map() { - return HashBiMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return new Itr<Entry<K, V>>() { - @Override - Entry<K, V> output(BiEntry<K, V> entry) { - return new MapEntry(entry); - } - - class MapEntry extends AbstractMapEntry<K, V> { - BiEntry<K, V> delegate; - - MapEntry(BiEntry<K, V> entry) { - this.delegate = entry; - } - - @Override public K getKey() { - return delegate.key; - } - - @Override public V getValue() { - return delegate.value; - } - - @Override public V setValue(V value) { - V oldValue = delegate.value; - int valueHash = hash(value); - if (valueHash == delegate.valueHash && Objects.equal(value, oldValue)) { - return value; - } - checkArgument( - seekByValue(value, valueHash) == null, "value already present: %s", value); - delete(delegate); - BiEntry<K, V> newEntry = - new BiEntry<K, V>(delegate.key, delegate.keyHash, value, valueHash); - insert(newEntry); - expectedModCount = modCount; - if (toRemove == delegate) { - toRemove = newEntry; - } - delegate = newEntry; - return oldValue; - } - } - }; - } - } - - private transient BiMap<V, K> inverse; - - @Override - public BiMap<V, K> inverse() { - return (inverse == null) ? inverse = new Inverse() : inverse; - } - - private final class Inverse extends AbstractMap<V, K> implements BiMap<V, K>, Serializable { - BiMap<K, V> forward() { - return HashBiMap.this; - } - - @Override - public int size() { - return size; - } - - @Override - public void clear() { - forward().clear(); - } - - @Override - public boolean containsKey(@Nullable Object value) { - return forward().containsValue(value); - } - - @Override - public K get(@Nullable Object value) { - BiEntry<K, V> entry = seekByValue(value, hash(value)); - return (entry == null) ? null : entry.key; - } - - @Override - public K put(@Nullable V value, @Nullable K key) { - return putInverse(value, key, false); - } - - @Override - public K forcePut(@Nullable V value, @Nullable K key) { - return putInverse(value, key, true); - } - - @Override - public K remove(@Nullable Object value) { - BiEntry<K, V> entry = seekByValue(value, hash(value)); - if (entry == null) { - return null; - } else { - delete(entry); - return entry.key; - } - } - - @Override - public BiMap<K, V> inverse() { - return forward(); - } - - @Override - public Set<V> keySet() { - return new InverseKeySet(); - } - - private final class InverseKeySet extends Maps.KeySet<V, K> { - @Override - Map<V, K> map() { - return Inverse.this; - } - - @Override - public boolean remove(@Nullable Object o) { - BiEntry<K, V> entry = seekByValue(o, hash(o)); - if (entry == null) { - return false; - } else { - delete(entry); - return true; - } - } - - @Override - public Iterator<V> iterator() { - return new Itr<V>() { - @Override V output(BiEntry<K, V> entry) { - return entry.value; - } - }; - } - } - - @Override - public Set<K> values() { - return forward().keySet(); - } - - @Override - public Set<Entry<V, K>> entrySet() { - return new Maps.EntrySet<V, K>() { - - @Override - Map<V, K> map() { - return Inverse.this; - } - - @Override - public Iterator<Entry<V, K>> iterator() { - return new Itr<Entry<V, K>>() { - @Override - Entry<V, K> output(BiEntry<K, V> entry) { - return new InverseEntry(entry); - } - - class InverseEntry extends AbstractMapEntry<V, K> { - BiEntry<K, V> delegate; - - InverseEntry(BiEntry<K, V> entry) { - this.delegate = entry; - } - - @Override - public V getKey() { - return delegate.value; - } - - @Override - public K getValue() { - return delegate.key; - } - - @Override - public K setValue(K key) { - K oldKey = delegate.key; - int keyHash = hash(key); - if (keyHash == delegate.keyHash && Objects.equal(key, oldKey)) { - return key; - } - checkArgument(seekByKey(key, keyHash) == null, "value already present: %s", key); - delete(delegate); - BiEntry<K, V> newEntry = - new BiEntry<K, V>(key, keyHash, delegate.value, delegate.valueHash); - insert(newEntry); - expectedModCount = modCount; - // This is safe because entries can only get bumped up to earlier in the iteration, - // so they can't get revisited. - return oldKey; - } - } - }; - } - }; - } - - Object writeReplace() { - return new InverseSerializedForm<K, V>(HashBiMap.this); - } - } - - private static final class InverseSerializedForm<K, V> implements Serializable { - private final HashBiMap<K, V> bimap; - - InverseSerializedForm(HashBiMap<K, V> bimap) { - this.bimap = bimap; - } - - Object readResolve() { - return bimap.inverse(); - } + @Override public V forcePut(@Nullable K key, @Nullable V value) { + return super.forcePut(key, value); } /** - * @serialData the number of entries, first key, first value, second key, second value, and so on. + * @serialData the number of entries, first key, first value, second key, + * second value, and so on. */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { @@ -660,10 +100,12 @@ public final class HashBiMap<K, V> extends AbstractMap<K, V> implements BiMap<K, } @GwtIncompatible("java.io.ObjectInputStream") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { stream.defaultReadObject(); int size = Serialization.readCount(stream); - init(size); + setDelegates(Maps.<K, V>newHashMapWithExpectedSize(size), + Maps.<V, K>newHashMapWithExpectedSize(size)); Serialization.populateMap(this, stream, size); } diff --git a/guava/src/com/google/common/collect/HashMultimap.java b/guava/src/com/google/common/collect/HashMultimap.java index bab2a05..d347ff1 100644 --- a/guava/src/com/google/common/collect/HashMultimap.java +++ b/guava/src/com/google/common/collect/HashMultimap.java @@ -48,7 +48,7 @@ import java.util.Set; */ @GwtCompatible(serializable = true, emulated = true) public final class HashMultimap<K, V> extends AbstractSetMultimap<K, V> { - private static final int DEFAULT_VALUES_PER_KEY = 2; + private static final int DEFAULT_VALUES_PER_KEY = 8; @VisibleForTesting transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; diff --git a/guava/src/com/google/common/collect/Hashing.java b/guava/src/com/google/common/collect/Hashing.java index b13eb7c..9c5f6bc 100644 --- a/guava/src/com/google/common/collect/Hashing.java +++ b/guava/src/com/google/common/collect/Hashing.java @@ -17,50 +17,27 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.primitives.Ints; /** * Static methods for implementing hash-based collections. * * @author Kevin Bourrillion * @author Jesse Wilson - * @author Austin Appleby */ @GwtCompatible final class Hashing { private Hashing() {} - private static final int C1 = 0xcc9e2d51; - private static final int C2 = 0x1b873593; - /* - * This method was rewritten in Java from an intermediate step of the Murmur hash function in - * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the - * following header: - * - * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author - * hereby disclaims copyright to this source code. + * This method was written by Doug Lea with assistance from members of JCP + * JSR-166 Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + * + * As of 2010/06/11, this method is identical to the (package private) hash + * method in OpenJDK 7's java.util.HashMap class. */ static int smear(int hashCode) { - return C2 * Integer.rotateLeft(hashCode * C1, 15); - } - - static int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; - - static int closedTableSize(int expectedEntries, double loadFactor) { - // Get the recommended table size. - // Round down to the nearest power of 2. - expectedEntries = Math.max(expectedEntries, 2); - int tableSize = Integer.highestOneBit(expectedEntries); - // Check to make sure that we will not exceed the maximum load factor. - if ((double) expectedEntries / tableSize > loadFactor) { - tableSize <<= 1; - return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE; - } - return tableSize; - } - - static boolean needsResizing(int size, int tableSize, double loadFactor) { - return size > loadFactor * tableSize && tableSize < MAX_TABLE_SIZE; + hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12); + return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4); } } diff --git a/guava/src/com/google/common/collect/ImmutableAsList.java b/guava/src/com/google/common/collect/ImmutableAsList.java index 249abee..9eb87de 100644 --- a/guava/src/com/google/common/collect/ImmutableAsList.java +++ b/guava/src/com/google/common/collect/ImmutableAsList.java @@ -17,49 +17,36 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; /** - * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks - * to the backing collection. + * List returned by {@link ImmutableCollection#asList} when the collection isn't + * an {@link ImmutableList} or an {@link ImmutableSortedSet}. * * @author Jared Levy - * @author Louis Wasserman */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") -abstract class ImmutableAsList<E> extends ImmutableList<E> { - abstract ImmutableCollection<E> delegateCollection(); +final class ImmutableAsList<E> extends RegularImmutableList<E> { + private final transient ImmutableCollection<E> collection; - @Override public boolean contains(Object target) { - // The collection's contains() is at least as fast as ImmutableList's - // and is often faster. - return delegateCollection().contains(target); - } - - @Override - public int size() { - return delegateCollection().size(); + ImmutableAsList(Object[] array, ImmutableCollection<E> collection) { + super(array, 0, array.length); + this.collection = collection; } - @Override - public boolean isEmpty() { - return delegateCollection().isEmpty(); - } - - @Override - boolean isPartialView() { - return delegateCollection().isPartialView(); + @Override public boolean contains(Object target) { + // The collection's contains() is at least as fast as RegularImmutableList's + // and is often faster. + return collection.contains(target); } /** * Serialized form that leads to the same performance as the original list. */ - @GwtIncompatible("serialization") static class SerializedForm implements Serializable { final ImmutableCollection<?> collection; SerializedForm(ImmutableCollection<?> collection) { @@ -71,14 +58,12 @@ abstract class ImmutableAsList<E> extends ImmutableList<E> { private static final long serialVersionUID = 0; } - @GwtIncompatible("serialization") private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Use SerializedForm"); } - @GwtIncompatible("serialization") @Override Object writeReplace() { - return new SerializedForm(delegateCollection()); + return new SerializedForm(collection); } } diff --git a/guava/src/com/google/common/collect/ImmutableBiMap.java b/guava/src/com/google/common/collect/ImmutableBiMap.java index d7968b2..9d8e144 100644 --- a/guava/src/com/google/common/collect/ImmutableBiMap.java +++ b/guava/src/com/google/common/collect/ImmutableBiMap.java @@ -16,13 +16,12 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; -import java.util.Collection; import java.util.Map; +import javax.annotation.Nullable; + /** * An immutable {@link BiMap} with reliable user-specified iteration order. Does * not permit null keys or values. An {@code ImmutableBiMap} and its inverse @@ -44,22 +43,23 @@ import java.util.Map; public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> implements BiMap<K, V> { + private static final ImmutableBiMap<Object, Object> EMPTY_IMMUTABLE_BIMAP + = new EmptyBiMap(); + /** * Returns the empty bimap. */ // Casting to any type is safe because the set will never hold any elements. @SuppressWarnings("unchecked") public static <K, V> ImmutableBiMap<K, V> of() { - return (ImmutableBiMap<K, V>) EmptyImmutableBiMap.INSTANCE; + return (ImmutableBiMap<K, V>) EMPTY_IMMUTABLE_BIMAP; } /** * Returns an immutable bimap containing a single entry. */ public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1) { - checkNotNull(k1, "null key in entry: null=%s", v1); - checkNotNull(v1, "null value in entry: %s=null", k1); - return new SingletonImmutableBiMap<K, V>(k1, v1); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1)); } /** @@ -68,10 +68,7 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * @throws IllegalArgumentException if duplicate keys or values are added */ public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1, K k2, V v2) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1, k2, v2)); } /** @@ -81,11 +78,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3)); } /** @@ -95,12 +89,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .put(k4, v4) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3, k4, v4)); } /** @@ -110,13 +100,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .put(k4, v4) - .put(k5, v5) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3, k4, v4, k5, v5)); } // looking for of() with > 5 entries? Use the builder instead. @@ -184,7 +169,11 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * @throws IllegalArgumentException if duplicate keys or values were added */ @Override public ImmutableBiMap<K, V> build() { - return fromEntries(entries); + ImmutableMap<K, V> map = super.build(); + if (map.isEmpty()) { + return of(); + } + return new RegularImmutableBiMap<K, V>(map); } } @@ -213,25 +202,18 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> } } - return fromEntries(ImmutableList.copyOf(map.entrySet())); - } - - static <K, V> ImmutableBiMap<K, V> fromEntries( - Collection<? extends Entry<? extends K, ? extends V>> entries) { - switch (entries.size()) { - case 0: - return of(); - case 1: { - Entry<? extends K, ? extends V> entry = Iterables.getOnlyElement(entries); - return new SingletonImmutableBiMap<K, V>(entry.getKey(), entry.getValue()); - } - default: - return new RegularImmutableBiMap<K, V>(entries); + if (map.isEmpty()) { + return of(); } + + ImmutableMap<K, V> immutableMap = ImmutableMap.copyOf(map); + return new RegularImmutableBiMap<K, V>(immutableMap); } ImmutableBiMap() {} + abstract ImmutableMap<K, V> delegate(); + /** * {@inheritDoc} * @@ -241,6 +223,26 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> @Override public abstract ImmutableBiMap<V, K> inverse(); + @Override public boolean containsKey(@Nullable Object key) { + return delegate().containsKey(key); + } + + @Override public boolean containsValue(@Nullable Object value) { + return inverse().containsKey(value); + } + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + return delegate().entrySet(); + } + + @Override public V get(@Nullable Object key) { + return delegate().get(key); + } + + @Override public ImmutableSet<K> keySet() { + return delegate().keySet(); + } + /** * Returns an immutable set of the values in this map. The values are in the * same order as the parameters used to build this map. @@ -253,14 +255,50 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * Guaranteed to throw an exception and leave the bimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public V forcePut(K key, V value) { throw new UnsupportedOperationException(); } + @Override public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override public boolean equals(@Nullable Object object) { + return object == this || delegate().equals(object); + } + + @Override public int hashCode() { + return delegate().hashCode(); + } + + @Override public String toString() { + return delegate().toString(); + } + + /** Bimap with no mappings. */ + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + static class EmptyBiMap extends ImmutableBiMap<Object, Object> { + @Override ImmutableMap<Object, Object> delegate() { + return ImmutableMap.of(); + } + @Override public ImmutableBiMap<Object, Object> inverse() { + return this; + } + @Override boolean isPartialView() { + return false; + } + Object readResolve() { + return EMPTY_IMMUTABLE_BIMAP; // preserve singleton property + } + } + /** * Serialized type for all ImmutableBiMap instances. It captures the logical * contents and they are reconstructed using public factory methods. This diff --git a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java index 50f93c3..1c596e2 100644 --- a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java @@ -16,14 +16,10 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.primitives.Primitives; import java.util.Map; -import javax.annotation.Nullable; - /** * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link * MutableClassToInstanceMap}. @@ -142,18 +138,15 @@ public final class ImmutableClassToInstanceMap<B> extends @Override @SuppressWarnings("unchecked") // value could not get in if not a T - @Nullable public <T extends B> T getInstance(Class<T> type) { - return (T) delegate.get(checkNotNull(type)); + return (T) delegate.get(type); } /** * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public <T extends B> T putInstance(Class<T> type, T value) { throw new UnsupportedOperationException(); diff --git a/guava/src/com/google/common/collect/ImmutableCollection.java b/guava/src/com/google/common/collect/ImmutableCollection.java index 2aeca97..5fca2aa 100644 --- a/guava/src/com/google/common/collect/ImmutableCollection.java +++ b/guava/src/com/google/common/collect/ImmutableCollection.java @@ -17,7 +17,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.VisibleForTesting; import java.io.Serializable; import java.util.Collection; @@ -86,9 +85,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean add(E e) { throw new UnsupportedOperationException(); @@ -98,9 +95,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean remove(Object object) { throw new UnsupportedOperationException(); @@ -110,9 +105,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean addAll(Collection<? extends E> newElements) { throw new UnsupportedOperationException(); @@ -122,9 +115,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean removeAll(Collection<?> oldElements) { throw new UnsupportedOperationException(); @@ -134,9 +125,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean retainAll(Collection<?> elementsToKeep) { throw new UnsupportedOperationException(); @@ -146,9 +135,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { throw new UnsupportedOperationException(); @@ -177,7 +164,7 @@ public abstract class ImmutableCollection<E> case 1: return ImmutableList.of(iterator().next()); default: - return new RegularImmutableAsList<E>(this, toArray()); + return new ImmutableAsList<E>(toArray(), this); } } @@ -199,7 +186,7 @@ public abstract class ImmutableCollection<E> } @Override public UnmodifiableIterator<Object> iterator() { - return Iterators.EMPTY_LIST_ITERATOR; + return Iterators.EMPTY_ITERATOR; } private static final Object[] EMPTY_ARRAY = new Object[0]; @@ -285,24 +272,6 @@ public abstract class ImmutableCollection<E> * @since 10.0 */ public abstract static class Builder<E> { - static final int DEFAULT_INITIAL_CAPACITY = 4; - - @VisibleForTesting - static int expandedCapacity(int oldCapacity, int minCapacity) { - if (minCapacity < 0) { - throw new AssertionError("cannot store more than MAX_VALUE elements"); - } - // careful of overflow! - int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; - if (newCapacity < minCapacity) { - newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; - } - if (newCapacity < 0) { - newCapacity = Integer.MAX_VALUE; - // guaranteed to be >= newCapacity - } - return newCapacity; - } Builder() { } diff --git a/guava/src/com/google/common/collect/ImmutableEnumMap.java b/guava/src/com/google/common/collect/ImmutableEnumMap.java deleted file mode 100644 index 6738685..0000000 --- a/guava/src/com/google/common/collect/ImmutableEnumMap.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.EnumMap; -import java.util.Iterator; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link ImmutableMap} backed by a non-empty {@link - * java.util.EnumMap}. - * - * @author Louis Wasserman - */ -@GwtCompatible(serializable = true, emulated = true) -@SuppressWarnings("serial") // we're overriding default serialization -final class ImmutableEnumMap<K extends Enum<K>, V> extends ImmutableMap<K, V> { - static <K extends Enum<K>, V> ImmutableMap<K, V> asImmutable(EnumMap<K, V> map) { - switch (map.size()) { - case 0: - return ImmutableMap.of(); - case 1: { - Entry<K, V> entry = Iterables.getOnlyElement(map.entrySet()); - return ImmutableMap.of(entry.getKey(), entry.getValue()); - } - default: - return new ImmutableEnumMap<K, V>(map); - } - } - - private transient final EnumMap<K, V> delegate; - - private ImmutableEnumMap(EnumMap<K, V> delegate) { - this.delegate = delegate; - checkArgument(!delegate.isEmpty()); - } - - @Override - ImmutableSet<K> createKeySet() { - return new ImmutableSet<K>() { - - @Override - public boolean contains(Object object) { - return delegate.containsKey(object); - } - - @Override - public int size() { - return ImmutableEnumMap.this.size(); - } - - @Override - public UnmodifiableIterator<K> iterator() { - return Iterators.unmodifiableIterator(delegate.keySet().iterator()); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return delegate.containsKey(key); - } - - @Override - public V get(Object key) { - return delegate.get(key); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new ImmutableMapEntrySet<K, V>() { - - @Override - ImmutableMap<K, V> map() { - return ImmutableEnumMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new UnmodifiableIterator<Entry<K, V>>() { - private final Iterator<Entry<K, V>> backingIterator = delegate.entrySet().iterator(); - - @Override - public boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override - public Entry<K, V> next() { - Entry<K, V> entry = backingIterator.next(); - return Maps.immutableEntry(entry.getKey(), entry.getValue()); - } - }; - } - }; - } - - @Override - boolean isPartialView() { - return false; - } - - // All callers of the constructor are restricted to <K extends Enum<K>>. - @Override Object writeReplace() { - return new EnumSerializedForm<K, V>(delegate); - } - - /* - * This class is used to serialize ImmutableEnumSet instances. - */ - private static class EnumSerializedForm<K extends Enum<K>, V> - implements Serializable { - final EnumMap<K, V> delegate; - EnumSerializedForm(EnumMap<K, V> delegate) { - this.delegate = delegate; - } - Object readResolve() { - return new ImmutableEnumMap<K, V>(delegate); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableEnumSet.java b/guava/src/com/google/common/collect/ImmutableEnumSet.java index d187b5c..ac6dd0e 100644 --- a/guava/src/com/google/common/collect/ImmutableEnumSet.java +++ b/guava/src/com/google/common/collect/ImmutableEnumSet.java @@ -31,17 +31,6 @@ import java.util.EnumSet; @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization final class ImmutableEnumSet<E extends Enum<E>> extends ImmutableSet<E> { - static <E extends Enum<E>> ImmutableSet<E> asImmutable(EnumSet<E> set) { - switch (set.size()) { - case 0: - return ImmutableSet.of(); - case 1: - return ImmutableSet.of(Iterables.getOnlyElement(set)); - default: - return new ImmutableEnumSet<E>(set); - } - } - /* * Notes on EnumSet and <E extends Enum<E>>: * @@ -52,7 +41,7 @@ final class ImmutableEnumSet<E extends Enum<E>> extends ImmutableSet<E> { */ private final transient EnumSet<E> delegate; - private ImmutableEnumSet(EnumSet<E> delegate) { + ImmutableEnumSet(EnumSet<E> delegate) { this.delegate = delegate; } diff --git a/guava/src/com/google/common/collect/ImmutableList.java b/guava/src/com/google/common/collect/ImmutableList.java index a01f4bc..cd8235a 100644 --- a/guava/src/com/google/common/collect/ImmutableList.java +++ b/guava/src/com/google/common/collect/ImmutableList.java @@ -16,17 +16,15 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkPositionIndex; -import static com.google.common.base.Preconditions.checkPositionIndexes; -import static com.google.common.collect.ObjectArrays.checkElementNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -50,10 +48,6 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this type are * guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @see ImmutableMap * @see ImmutableSet * @author Kevin Bourrillion @@ -259,19 +253,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @throws NullPointerException if any of {@code elements} is null */ public static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements) { - // We special-case for 0 or 1 elements, but going further is madness. - if (!elements.hasNext()) { - return of(); - } - E first = elements.next(); - if (!elements.hasNext()) { - return of(first); - } else { - return new ImmutableList.Builder<E>() - .add(first) - .addAll(elements) - .build(); - } + return copyFromCollection(Lists.newArrayList(elements)); } /** @@ -291,12 +273,9 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } } - /** - * Views the array as an immutable list. The array must have only non-null {@code E} elements. - * - * <p>The array must be internally created. - */ - static <E> ImmutableList<E> asImmutableList(Object[] elements) { + private static <E> ImmutableList<E> copyFromCollection( + Collection<? extends E> collection) { + Object[] elements = collection.toArray(); switch (elements.length) { case 0: return of(); @@ -305,23 +284,29 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> ImmutableList<E> list = new SingletonImmutableList<E>((E) elements[0]); return list; default: + // safe to use the array without copying it + // as specified by Collection.toArray(). return construct(elements); } } - - private static <E> ImmutableList<E> copyFromCollection( - Collection<? extends E> collection) { - return asImmutableList(collection.toArray()); - } - + /** {@code elements} has to be internally created array. */ private static <E> ImmutableList<E> construct(Object... elements) { for (int i = 0; i < elements.length; i++) { - ObjectArrays.checkElementNotNull(elements[i], i); + checkElementNotNull(elements[i], i); } return new RegularImmutableList<E>(elements); } + // We do this instead of Preconditions.checkNotNull to save boxing and array + // creation cost. + private static Object checkElementNotNull(Object element, int index) { + if (element == null) { + throw new NullPointerException("at index " + index); + } + return element; + } + ImmutableList() {} // This declaration is needed to make List.iterator() and @@ -334,29 +319,15 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> return listIterator(0); } - @Override public UnmodifiableListIterator<E> listIterator(int index) { - return new AbstractIndexedListIterator<E>(size(), index) { - @Override - protected E get(int index) { - return ImmutableList.this.get(index); - } - }; - } + @Override public abstract UnmodifiableListIterator<E> listIterator(int index); - @Override - public int indexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.indexOfImpl(this, object); - } + // Mark these two methods with @Nullable @Override - public int lastIndexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); - } + public abstract int indexOf(@Nullable Object object); @Override - public boolean contains(@Nullable Object object) { - return indexOf(object) >= 0; - } + public abstract int lastIndexOf(@Nullable Object object); // constrain the return type to ImmutableList<E> @@ -367,67 +338,13 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * returned.) */ @Override - public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); - int length = toIndex - fromIndex; - switch (length) { - case 0: - return of(); - case 1: - return of(get(fromIndex)); - default: - return subListUnchecked(fromIndex, toIndex); - } - } - - /** - * Called by the default implementation of {@link #subList} when {@code - * toIndex - fromIndex > 1}, after index validation has already been - * performed. - */ - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new SubList(fromIndex, toIndex - fromIndex); - } - - class SubList extends ImmutableList<E> { - transient final int offset; - transient final int length; - - SubList(int offset, int length) { - this.offset = offset; - this.length = length; - } - - @Override - public int size() { - return length; - } - - @Override - public E get(int index) { - checkElementIndex(index, length); - return ImmutableList.this.get(index + offset); - } - - @Override - public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, length); - return ImmutableList.this.subList(fromIndex + offset, toIndex + offset); - } - - @Override - boolean isPartialView() { - return true; - } - } + public abstract ImmutableList<E> subList(int fromIndex, int toIndex); /** * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean addAll(int index, Collection<? extends E> newElements) { throw new UnsupportedOperationException(); @@ -437,9 +354,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final E set(int index, E element) { throw new UnsupportedOperationException(); @@ -449,9 +364,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void add(int index, E element) { throw new UnsupportedOperationException(); @@ -461,9 +374,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final E remove(int index) { throw new UnsupportedOperationException(); @@ -491,8 +402,8 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } private static class ReverseImmutableList<E> extends ImmutableList<E> { - private final transient ImmutableList<E> forwardList; - private final transient int size; + private transient final ImmutableList<E> forwardList; + private transient final int size; ReverseImmutableList(ImmutableList<E> backingList) { this.forwardList = backingList; @@ -530,18 +441,18 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); return forwardList.subList( reversePosition(toIndex), reversePosition(fromIndex)).reverse(); } @Override public E get(int index) { - checkElementIndex(index, size); + Preconditions.checkElementIndex(index, size); return forwardList.get(reverseIndex(index)); } @Override public UnmodifiableListIterator<E> listIterator(int index) { - checkPositionIndex(index, size); + Preconditions.checkPositionIndex(index, size); final UnmodifiableListIterator<E> forward = forwardList.listIterator(reversePosition(index)); return new UnmodifiableListIterator<E>() { @@ -583,7 +494,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> return forwardList.isPartialView(); } } - + @Override public boolean equals(Object obj) { return Lists.equalsImpl(this, obj); } @@ -641,34 +552,13 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @since 2.0 (imported from Google Collections Library) */ public static final class Builder<E> extends ImmutableCollection.Builder<E> { - private Object[] contents; - private int size; + private final ArrayList<E> contents = Lists.newArrayList(); /** * Creates a new builder. The returned builder is equivalent to the builder * generated by {@link ImmutableList#builder}. */ - public Builder() { - this(DEFAULT_INITIAL_CAPACITY); - } - - // TODO(user): consider exposing this - Builder(int capacity) { - this.contents = new Object[capacity]; - this.size = 0; - } - - /** - * Expand the absolute capacity of the builder so it can accept at least - * the specified number of elements without being resized. - */ - Builder<E> ensureCapacity(int minCapacity) { - if (contents.length < minCapacity) { - this.contents = ObjectArrays.arraysCopyOf( - this.contents, expandedCapacity(contents.length, minCapacity)); - } - return this; - } + public Builder() {} /** * Adds {@code element} to the {@code ImmutableList}. @@ -678,9 +568,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @throws NullPointerException if {@code element} is null */ @Override public Builder<E> add(E element) { - checkNotNull(element); - ensureCapacity(size + 1); - contents[size++] = element; + contents.add(checkNotNull(element)); return this; } @@ -695,7 +583,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> @Override public Builder<E> addAll(Iterable<? extends E> elements) { if (elements instanceof Collection) { Collection<?> collection = (Collection<?>) elements; - ensureCapacity(size + collection.size()); + contents.ensureCapacity(contents.size() + collection.size()); } super.addAll(elements); return this; @@ -710,12 +598,8 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * null element */ @Override public Builder<E> add(E... elements) { - for (int i = 0; i < elements.length; i++) { - checkElementNotNull(elements[i], i); - } - ensureCapacity(size + elements.length); - System.arraycopy(elements, 0, contents, size, elements.length); - size += elements.length; + contents.ensureCapacity(contents.size() + elements.length); + super.add(elements); return this; } @@ -737,21 +621,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * the {@code Builder}. */ @Override public ImmutableList<E> build() { - switch (size) { - case 0: - return of(); - case 1: - @SuppressWarnings("unchecked") // guaranteed to be an E - E singleElement = (E) contents[0]; - return of(singleElement); - default: - if (size == contents.length) { - // no need to copy; any further add operations on the builder will copy the buffer - return new RegularImmutableList<E>(contents); - } else { - return new RegularImmutableList<E>(ObjectArrays.arraysCopyOf(contents, size)); - } - } + return copyOf(contents); } } } diff --git a/guava/src/com/google/common/collect/ImmutableListMultimap.java b/guava/src/com/google/common/collect/ImmutableListMultimap.java index 865fa6f..3071075 100644 --- a/guava/src/com/google/common/collect/ImmutableListMultimap.java +++ b/guava/src/com/google/common/collect/ImmutableListMultimap.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -45,10 +46,6 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this class * are guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) */ @@ -200,7 +197,7 @@ public class ImmutableListMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { super.orderKeysBy(keyComparator); return this; @@ -211,7 +208,7 @@ public class ImmutableListMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { super.orderValuesBy(valueComparator); return this; @@ -291,14 +288,13 @@ public class ImmutableListMultimap<K, V> /** * {@inheritDoc} * - * <p>Because an inverse of a list multimap can contain multiple pairs with - * the same key and value, this method returns an {@code - * ImmutableListMultimap} rather than the {@code ImmutableMultimap} specified - * in the {@code ImmutableMultimap} class. + * <p>Because an inverse of a list multimap can contain multiple pairs with the same key and + * value, this method returns an {@code ImmutableListMultimap} rather than the + * {@code ImmutableMultimap} specified in the {@code ImmutableMultimap} class. * - * @since 11.0 + * @since 11 */ - @Override + @Beta public ImmutableListMultimap<V, K> inverse() { ImmutableListMultimap<V, K> result = inverse; return (result == null) ? (inverse = invert()) : result; @@ -318,9 +314,8 @@ public class ImmutableListMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableList<V> removeAll(Object key) { + @Override public ImmutableList<V> removeAll(Object key) { throw new UnsupportedOperationException(); } @@ -328,9 +323,8 @@ public class ImmutableListMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableList<V> replaceValues( + @Override public ImmutableList<V> replaceValues( K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); } diff --git a/guava/src/com/google/common/collect/ImmutableMap.java b/guava/src/com/google/common/collect/ImmutableMap.java index c1d7933..0a2ef77 100644 --- a/guava/src/com/google/common/collect/ImmutableMap.java +++ b/guava/src/com/google/common/collect/ImmutableMap.java @@ -19,15 +19,12 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getOnlyElement; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; -import java.util.EnumMap; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -50,10 +47,6 @@ import javax.annotation.Nullable; * having your element type cache its own hash codes, and by making use of the * cached values to short-circuit a slow {@code equals} algorithm. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jesse Wilson * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) @@ -66,8 +59,10 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * {@link Collections#emptyMap}, and is preferable mainly for consistency * and maintainability of your code. */ + // Casting to any type is safe because the set will never hold any elements. + @SuppressWarnings("unchecked") public static <K, V> ImmutableMap<K, V> of() { - return ImmutableBiMap.of(); + return (ImmutableMap<K, V>) EmptyImmutableMap.INSTANCE; } /** @@ -77,7 +72,8 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * maintainability of your code. */ public static <K, V> ImmutableMap<K, V> of(K k1, V v1) { - return ImmutableBiMap.of(k1, v1); + return new SingletonImmutableMap<K, V>( + checkNotNull(k1), checkNotNull(v1)); } /** @@ -140,9 +136,9 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * throw {@link UnsupportedOperationException}. */ static <K, V> Entry<K, V> entryOf(K key, V value) { - checkNotNull(key, "null key in entry: null=%s", value); - checkNotNull(value, "null value in entry: %s=null", key); - return Maps.immutableEntry(key, value); + return Maps.immutableEntry( + checkNotNull(key, "null key"), + checkNotNull(value, "null value")); } /** @@ -193,7 +189,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { K key = entry.getKey(); V value = entry.getValue(); - if (entry instanceof ImmutableEntry) { + if (entry instanceof ImmutableEntry<?, ?>) { checkNotNull(key); checkNotNull(value); @SuppressWarnings("unchecked") // all supported methods are covariant @@ -242,7 +238,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { case 0: return of(); case 1: - return new SingletonImmutableBiMap<K, V>(getOnlyElement(entries)); + return new SingletonImmutableMap<K, V>(getOnlyElement(entries)); default: Entry<?, ?>[] entryArray = entries.toArray(new Entry<?, ?>[entries.size()]); @@ -274,16 +270,6 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { if (!kvMap.isPartialView()) { return kvMap; } - } else if (map instanceof EnumMap) { - EnumMap<?, ?> enumMap = (EnumMap<?, ?>) map; - for (Map.Entry<?, ?> entry : enumMap.entrySet()) { - checkNotNull(entry.getKey()); - checkNotNull(entry.getValue()); - } - @SuppressWarnings("unchecked") - // immutable collections are safe for covariant casts - ImmutableMap<K, V> result = ImmutableEnumMap.asImmutable(new EnumMap(enumMap)); - return result; } @SuppressWarnings("unchecked") // we won't write to this array @@ -292,7 +278,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { case 0: return of(); case 1: - return new SingletonImmutableBiMap<K, V>(entryOf( + return new SingletonImmutableMap<K, V>(entryOf( entries[0].getKey(), entries[0].getValue())); default: for (int i = 0; i < entries.length; i++) { @@ -310,9 +296,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V put(K k, V v) { throw new UnsupportedOperationException(); @@ -322,9 +306,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V remove(Object o) { throw new UnsupportedOperationException(); @@ -334,9 +316,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void putAll(Map<? extends K, ? extends V> map) { throw new UnsupportedOperationException(); @@ -346,9 +326,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { throw new UnsupportedOperationException(); @@ -364,132 +342,44 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { return get(key) != null; } + // Overriding to mark it Nullable @Override - public boolean containsValue(@Nullable Object value) { - return value != null && Maps.containsValueImpl(this, value); - } + public abstract boolean containsValue(@Nullable Object value); // Overriding to mark it Nullable @Override public abstract V get(@Nullable Object key); - private transient ImmutableSet<Entry<K, V>> entrySet; - /** * Returns an immutable set of the mappings in this map. The entries are in * the same order as the parameters used to build this map. */ @Override - public ImmutableSet<Entry<K, V>> entrySet() { - ImmutableSet<Entry<K, V>> result = entrySet; - return (result == null) ? entrySet = createEntrySet() : result; - } - - abstract ImmutableSet<Entry<K, V>> createEntrySet(); - - private transient ImmutableSet<K> keySet; + public abstract ImmutableSet<Entry<K, V>> entrySet(); /** * Returns an immutable set of the keys in this map. These keys are in * the same order as the parameters used to build this map. */ @Override - public ImmutableSet<K> keySet() { - ImmutableSet<K> result = keySet; - return (result == null) ? keySet = createKeySet() : result; - } - - ImmutableSet<K> createKeySet() { - return new ImmutableMapKeySet<K, V>(this); - } - - private transient ImmutableCollection<V> values; + public abstract ImmutableSet<K> keySet(); /** * Returns an immutable collection of the values in this map. The values are * in the same order as the parameters used to build this map. */ @Override - public ImmutableCollection<V> values() { - ImmutableCollection<V> result = values; - return (result == null) ? values = new ImmutableMapValues<K, V>(this) : result; - } - - // cached so that this.multimapView().inverse() only computes inverse once - private transient ImmutableSetMultimap<K, V> multimapView; - - /** - * Returns a multimap view of the map. - * - * @since 14.0 - */ - @Beta - public ImmutableSetMultimap<K, V> asMultimap() { - ImmutableSetMultimap<K, V> result = multimapView; - return (result == null) ? (multimapView = createMultimapView()) : result; - } - - private ImmutableSetMultimap<K, V> createMultimapView() { - ImmutableMap<K, ImmutableSet<V>> map = viewMapValuesAsSingletonSets(); - return new ImmutableSetMultimap<K, V>(map, map.size(), null); - } - - private ImmutableMap<K, ImmutableSet<V>> viewMapValuesAsSingletonSets() { - class MapViewOfValuesAsSingletonSets extends ImmutableMap<K, ImmutableSet<V>> { - @Override public int size() { - return ImmutableMap.this.size(); - } - - @Override public boolean containsKey(@Nullable Object key) { - return ImmutableMap.this.containsKey(key); - } - - @Override public ImmutableSet<V> get(@Nullable Object key) { - V outerValue = ImmutableMap.this.get(key); - return (outerValue == null) ? null : ImmutableSet.of(outerValue); - } - - @Override boolean isPartialView() { - return false; - } - - @Override ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() { - return new ImmutableMapEntrySet<K, ImmutableSet<V>>() { - @Override ImmutableMap<K, ImmutableSet<V>> map() { - return MapViewOfValuesAsSingletonSets.this; - } - - @Override - public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() { - final Iterator<Entry<K,V>> backingIterator = ImmutableMap.this - .entrySet().iterator(); - return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() { - @Override public boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override public Entry<K, ImmutableSet<V>> next() { - final Entry<K, V> backingEntry = backingIterator.next(); - return new AbstractMapEntry<K, ImmutableSet<V>>() { - @Override public K getKey() { - return backingEntry.getKey(); - } - - @Override public ImmutableSet<V> getValue() { - return ImmutableSet.of(backingEntry.getValue()); - } - }; - } - }; - } - }; - } - } - return new MapViewOfValuesAsSingletonSets(); - } + public abstract ImmutableCollection<V> values(); @Override public boolean equals(@Nullable Object object) { - return Maps.equalsImpl(this, object); + if (object == this) { + return true; + } + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + return this.entrySet().equals(that.entrySet()); + } + return false; } abstract boolean isPartialView(); diff --git a/guava/src/com/google/common/collect/ImmutableMapEntrySet.java b/guava/src/com/google/common/collect/ImmutableMapEntrySet.java deleted file mode 100644 index a6aa6e0..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapEntrySet.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -import java.util.Map.Entry; - -import javax.annotation.Nullable; - -/** - * {@code entrySet()} implementation for {@link ImmutableMap}. - * - * @author Jesse Wilson - * @author Kevin Bourrillion - */ -@GwtCompatible(emulated = true) -abstract class ImmutableMapEntrySet<K, V> extends ImmutableSet<Entry<K, V>> { - ImmutableMapEntrySet() {} - - abstract ImmutableMap<K, V> map(); - - @Override - public int size() { - return map().size(); - } - - @Override - public boolean contains(@Nullable Object object) { - if (object instanceof Entry) { - Entry<?, ?> entry = (Entry<?, ?>) object; - V value = map().get(entry.getKey()); - return value != null && value.equals(entry.getValue()); - } - return false; - } - - @Override - boolean isPartialView() { - return map().isPartialView(); - } - - @GwtIncompatible("serialization") - @Override - Object writeReplace() { - return new EntrySetSerializedForm<K, V>(map()); - } - - @GwtIncompatible("serialization") - private static class EntrySetSerializedForm<K, V> implements Serializable { - final ImmutableMap<K, V> map; - EntrySetSerializedForm(ImmutableMap<K, V> map) { - this.map = map; - } - Object readResolve() { - return map.entrySet(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMapKeySet.java b/guava/src/com/google/common/collect/ImmutableMapKeySet.java deleted file mode 100644 index fbb59d8..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapKeySet.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -import java.util.Map.Entry; - -import javax.annotation.Nullable; - -/** - * {@code keySet()} implementation for {@link ImmutableMap}. - * - * @author Jesse Wilson - * @author Kevin Bourrillion - */ -@GwtCompatible(emulated = true) -final class ImmutableMapKeySet<K, V> extends ImmutableSet<K> { - private final ImmutableMap<K, V> map; - - ImmutableMapKeySet(ImmutableMap<K, V> map) { - this.map = map; - } - - @Override - public int size() { - return map.size(); - } - - @Override - public UnmodifiableIterator<K> iterator() { - return asList().iterator(); - } - - @Override - public boolean contains(@Nullable Object object) { - return map.containsKey(object); - } - - @Override - ImmutableList<K> createAsList() { - final ImmutableList<Entry<K, V>> entryList = map.entrySet().asList(); - return new ImmutableAsList<K>() { - - @Override - public K get(int index) { - return entryList.get(index).getKey(); - } - - @Override - ImmutableCollection<K> delegateCollection() { - return ImmutableMapKeySet.this; - } - - }; - } - - @Override - boolean isPartialView() { - return true; - } - - @GwtIncompatible("serialization") - @Override Object writeReplace() { - return new KeySetSerializedForm<K>(map); - } - - @GwtIncompatible("serialization") - private static class KeySetSerializedForm<K> implements Serializable { - final ImmutableMap<K, ?> map; - KeySetSerializedForm(ImmutableMap<K, ?> map) { - this.map = map; - } - Object readResolve() { - return map.keySet(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMapValues.java b/guava/src/com/google/common/collect/ImmutableMapValues.java deleted file mode 100644 index 6ec7464..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapValues.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -import java.util.Map.Entry; - -/** - * {@code values()} implementation for {@link ImmutableMap}. - * - * @author Jesse Wilson - * @author Kevin Bourrillion - */ -@GwtCompatible(emulated = true) -final class ImmutableMapValues<K, V> extends ImmutableCollection<V> { - private final ImmutableMap<K, V> map; - - ImmutableMapValues(ImmutableMap<K, V> map) { - this.map = map; - } - - @Override - public int size() { - return map.size(); - } - - @Override - public UnmodifiableIterator<V> iterator() { - return Maps.valueIterator(map.entrySet().iterator()); - } - - @Override - public boolean contains(Object object) { - return map.containsValue(object); - } - - @Override - boolean isPartialView() { - return true; - } - - @Override - ImmutableList<V> createAsList() { - final ImmutableList<Entry<K, V>> entryList = map.entrySet().asList(); - return new ImmutableAsList<V>() { - @Override - public V get(int index) { - return entryList.get(index).getValue(); - } - - @Override - ImmutableCollection<V> delegateCollection() { - return ImmutableMapValues.this; - } - }; - } - - @GwtIncompatible("serialization") - @Override Object writeReplace() { - return new SerializedForm<V>(map); - } - - @GwtIncompatible("serialization") - private static class SerializedForm<V> implements Serializable { - final ImmutableMap<?, V> map; - SerializedForm(ImmutableMap<?, V> map) { - this.map = map; - } - Object readResolve() { - return map.values(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMultimap.java b/guava/src/com/google/common/collect/ImmutableMultimap.java index e29dbc1..13e213e 100644 --- a/guava/src/com/google/common/collect/ImmutableMultimap.java +++ b/guava/src/com/google/common/collect/ImmutableMultimap.java @@ -18,9 +18,9 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Function; import java.io.Serializable; import java.util.Arrays; @@ -30,9 +30,8 @@ import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.Set; +import java.util.TreeMap; import javax.annotation.Nullable; @@ -54,16 +53,13 @@ import javax.annotation.Nullable; * <p>In addition to methods defined by {@link Multimap}, an {@link #inverse} * method is also supported. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(emulated = true) -public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> - implements Serializable { +// TODO(user): If BiMultimap graduates from labs, this class should implement it. +public abstract class ImmutableMultimap<K, V> + implements Multimap<K, V>, Serializable { /** Returns an empty multimap. */ public static <K, V> ImmutableMultimap<K, V> of() { @@ -123,7 +119,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * value orderings, allows duplicate values, and performs better than * {@link LinkedListMultimap}. */ - private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { + private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { BuilderMultimap() { super(new LinkedHashMap<K, Collection<V>>()); } @@ -134,6 +130,23 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> } /** + * Multimap for {@link ImmutableMultimap.Builder} that sorts key and allows + * duplicate values, + */ + private static class SortedKeyBuilderMultimap<K, V> + extends AbstractMultimap<K, V> { + SortedKeyBuilderMultimap( + Comparator<? super K> keyComparator, Multimap<K, V> multimap) { + super(new TreeMap<K, Collection<V>>(keyComparator)); + putAll(multimap); + } + @Override Collection<V> createCollection() { + return Lists.newArrayList(); + } + private static final long serialVersionUID = 0; + } + + /** * A builder for creating immutable multimap instances, especially * {@code public static final} multimaps ("constant multimaps"). Example: * <pre> {@code @@ -153,7 +166,6 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ public static class Builder<K, V> { Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>(); - Comparator<? super K> keyComparator; Comparator<? super V> valueComparator; /** @@ -228,8 +240,10 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * * @since 8.0 */ + @Beta public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { - this.keyComparator = checkNotNull(keyComparator); + builderMultimap = new SortedKeyBuilderMultimap<K, V>( + checkNotNull(keyComparator), builderMultimap); return this; } @@ -238,6 +252,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * * @since 8.0 */ + @Beta public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { this.valueComparator = checkNotNull(valueComparator); return this; @@ -253,23 +268,6 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> Collections.sort(list, valueComparator); } } - if (keyComparator != null) { - Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>(); - List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList( - builderMultimap.asMap().entrySet()); - Collections.sort( - entries, - Ordering.from(keyComparator).onResultOf(new Function<Entry<K, Collection<V>>, K>() { - @Override - public K apply(Entry<K, Collection<V>> entry) { - return entry.getKey(); - } - })); - for (Map.Entry<K, Collection<V>> entry : entries) { - sortedCopy.putAll(entry.getKey(), entry.getValue()); - } - builderMultimap = sortedCopy; - } return copyOf(builderMultimap); } } @@ -327,9 +325,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableCollection<V> removeAll(Object key) { throw new UnsupportedOperationException(); @@ -339,9 +335,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableCollection<V> replaceValues(K key, Iterable<? extends V> values) { @@ -352,9 +346,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public void clear() { throw new UnsupportedOperationException(); @@ -374,17 +366,16 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * key-value mapping in the original, the result will have a mapping with * key and value reversed. * - * @since 11.0 + * @since 11 */ + @Beta public abstract ImmutableMultimap<V, K> inverse(); /** * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public boolean put(K key, V value) { throw new UnsupportedOperationException(); @@ -394,9 +385,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public boolean putAll(K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); @@ -406,9 +395,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public boolean putAll(Multimap<? extends K, ? extends V> multimap) { throw new UnsupportedOperationException(); @@ -418,30 +405,65 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public boolean remove(Object key, Object value) { throw new UnsupportedOperationException(); } - boolean isPartialView() { + boolean isPartialView(){ return map.isPartialView(); } // accessors @Override + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + Collection<V> values = map.get(key); + return values != null && values.contains(value); + } + + @Override public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } @Override + public boolean containsValue(@Nullable Object value) { + for (Collection<V> valueCollection : map.values()) { + if (valueCollection.contains(value)) { + return true; + } + } + return false; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override public int size() { return size; } + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multimap) { + Multimap<?, ?> that = (Multimap<?, ?>) object; + return this.map.equals(that.asMap()); + } + return false; + } + + @Override public int hashCode() { + return map.hashCode(); + } + + @Override public String toString() { + return map.toString(); + } + // views /** @@ -463,11 +485,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> public ImmutableMap<K, Collection<V>> asMap() { return (ImmutableMap) map; } - - @Override - Map<K, Collection<V>> createAsMap() { - throw new AssertionError("should never be called"); - } + + private transient ImmutableCollection<Entry<K, V>> entries; /** * Returns an immutable collection of all key-value pairs in the multimap. Its @@ -476,12 +495,9 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableCollection<Entry<K, V>> entries() { - return (ImmutableCollection<Entry<K, V>>) super.entries(); - } - - @Override - ImmutableCollection<Entry<K, V>> createEntries() { - return new EntryCollection<K, V>(this); + ImmutableCollection<Entry<K, V>> result = entries; + return (result == null) + ? (entries = new EntryCollection<K, V>(this)) : result; } private static class EntryCollection<K, V> @@ -493,7 +509,30 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> } @Override public UnmodifiableIterator<Entry<K, V>> iterator() { - return multimap.entryIterator(); + final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>> + mapIterator = this.multimap.map.entrySet().iterator(); + + return new UnmodifiableIterator<Entry<K, V>>() { + K key; + Iterator<V> valueIterator; + + @Override + public boolean hasNext() { + return (key != null && valueIterator.hasNext()) + || mapIterator.hasNext(); + } + + @Override + public Entry<K, V> next() { + if (key == null || !valueIterator.hasNext()) { + Entry<K, ? extends ImmutableCollection<V>> entry + = mapIterator.next(); + key = entry.getKey(); + valueIterator = entry.getValue().iterator(); + } + return Maps.immutableEntry(key, valueIterator.next()); + } + }; } @Override boolean isPartialView() { @@ -515,34 +554,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> private static final long serialVersionUID = 0; } - - @Override - UnmodifiableIterator<Entry<K, V>> entryIterator() { - final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>> - mapIterator = map.entrySet().iterator(); - - return new UnmodifiableIterator<Entry<K, V>>() { - K key; - Iterator<V> valueIterator; - - @Override - public boolean hasNext() { - return (key != null && valueIterator.hasNext()) - || mapIterator.hasNext(); - } - @Override - public Entry<K, V> next() { - if (key == null || !valueIterator.hasNext()) { - Entry<K, ? extends ImmutableCollection<V>> entry - = mapIterator.next(); - key = entry.getKey(); - valueIterator = entry.getValue().iterator(); - } - return Maps.immutableEntry(key, valueIterator.next()); - } - }; - } + private transient ImmutableMultiset<K> keys; /** * Returns a collection, which may contain duplicates, of all keys. The number @@ -552,78 +565,21 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableMultiset<K> keys() { - return (ImmutableMultiset<K>) super.keys(); + ImmutableMultiset<K> result = keys; + return (result == null) ? (keys = createKeys()) : result; } - @Override - ImmutableMultiset<K> createKeys() { - return new Keys(); - } - - @SuppressWarnings("serial") // Uses writeReplace, not default serialization - class Keys extends ImmutableMultiset<K> { - @Override - public boolean contains(@Nullable Object object) { - return containsKey(object); - } - - @Override - public int count(@Nullable Object element) { - Collection<V> values = map.get(element); - return (values == null) ? 0 : values.size(); - } - - @Override - public Set<K> elementSet() { - return keySet(); - } - - @Override - public int size() { - return ImmutableMultimap.this.size(); - } - - @Override - ImmutableSet<Entry<K>> createEntrySet() { - return new KeysEntrySet(); - } - - private class KeysEntrySet extends ImmutableMultiset<K>.EntrySet { - @Override - public int size() { - return keySet().size(); - } - - @Override - public UnmodifiableIterator<Entry<K>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<K>> createAsList() { - final ImmutableList<? extends Map.Entry<K, ? extends Collection<V>>> mapEntries = - map.entrySet().asList(); - return new ImmutableAsList<Entry<K>>() { - @Override - public Entry<K> get(int index) { - Map.Entry<K, ? extends Collection<V>> entry = mapEntries.get(index); - return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); - } - - @Override - ImmutableCollection<Entry<K>> delegateCollection() { - return KeysEntrySet.this; - } - }; - } - } - - @Override - boolean isPartialView() { - return true; + private ImmutableMultiset<K> createKeys() { + ImmutableMultiset.Builder<K> builder = ImmutableMultiset.builder(); + for (Entry<K, ? extends ImmutableCollection<V>> entry + : map.entrySet()) { + builder.addCopies(entry.getKey(), entry.getValue().size()); } + return builder.build(); } + private transient ImmutableCollection<V> values; + /** * Returns an immutable collection of the values in this multimap. Its * iterator traverses the values for the first key, the values for the second @@ -631,12 +587,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableCollection<V> values() { - return (ImmutableCollection<V>) super.values(); - } - - @Override - ImmutableCollection<V> createValues() { - return new Values<V>(this); + ImmutableCollection<V> result = values; + return (result == null) ? (values = new Values<V>(this)) : result; } private static class Values<V> extends ImmutableCollection<V> { @@ -647,7 +599,18 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> } @Override public UnmodifiableIterator<V> iterator() { - return Maps.valueIterator(multimap.entries().iterator()); + final Iterator<? extends Entry<?, V>> entryIterator + = multimap.entries().iterator(); + return new UnmodifiableIterator<V>() { + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override + public V next() { + return entryIterator.next().getValue(); + } + }; } @Override diff --git a/guava/src/com/google/common/collect/ImmutableMultiset.java b/guava/src/com/google/common/collect/ImmutableMultiset.java index 6680a2d..bd07423 100644 --- a/guava/src/com/google/common/collect/ImmutableMultiset.java +++ b/guava/src/com/google/common/collect/ImmutableMultiset.java @@ -19,6 +19,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.Multiset.Entry; import com.google.common.primitives.Ints; import java.io.Serializable; @@ -28,6 +29,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Set; import javax.annotation.Nullable; @@ -39,10 +41,6 @@ import javax.annotation.Nullable; * multiset contains multiple instances of an element, those instances are * consecutive in the iteration order. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jared Levy * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) @@ -136,6 +134,23 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Returns an immutable multiset containing the given elements. * * <p>The multiset is ordered by the first occurrence of each element. For + * example, {@code ImmutableMultiset.of(2, 3, 1, 3)} yields a multiset with + * elements in the order {@code 2, 3, 3, 1}. + * + * @throws NullPointerException if any of {@code elements} is null + * @deprecated use {@link #copyOf(Object[])}. <b>This method is scheduled for + * deletion in January 2012.</b> + * @since 2.0 (changed from varargs in 6.0) + */ + @Deprecated + public static <E> ImmutableMultiset<E> of(E[] elements) { + return copyOf(Arrays.asList(elements)); + } + + /** + * Returns an immutable multiset containing the given elements. + * + * <p>The multiset is ordered by the first occurrence of each element. For * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset * with elements in the order {@code 2, 3, 3, 1}. * @@ -206,8 +221,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> if (size == 0) { return of(); } - return new RegularImmutableMultiset<E>( - builder.build(), Ints.saturatedCast(size)); + return new RegularImmutableMultiset<E>(builder.build(), Ints.saturatedCast(size)); } /** @@ -230,7 +244,8 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> ImmutableMultiset() {} @Override public UnmodifiableIterator<E> iterator() { - final Iterator<Entry<E>> entryIterator = entrySet().iterator(); + final Iterator<Entry<E>> entryIterator = entryIterator(); + return new UnmodifiableIterator<E>() { int remaining; E element; @@ -267,9 +282,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int add(E element, int occurrences) { throw new UnsupportedOperationException(); @@ -279,9 +292,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int remove(Object element, int occurrences) { throw new UnsupportedOperationException(); @@ -291,9 +302,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int setCount(E element, int count) { throw new UnsupportedOperationException(); @@ -303,9 +312,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean setCount(E element, int oldCount, int newCount) { throw new UnsupportedOperationException(); @@ -341,17 +348,39 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> private transient ImmutableSet<Entry<E>> entrySet; @Override - public ImmutableSet<Entry<E>> entrySet() { + public Set<Entry<E>> entrySet() { ImmutableSet<Entry<E>> es = entrySet; return (es == null) ? (entrySet = createEntrySet()) : es; } - abstract ImmutableSet<Entry<E>> createEntrySet(); + abstract UnmodifiableIterator<Entry<E>> entryIterator(); + + abstract int distinctElements(); + + ImmutableSet<Entry<E>> createEntrySet() { + return new EntrySet<E>(this); + } + + static class EntrySet<E> extends ImmutableSet<Entry<E>> { + transient final ImmutableMultiset<E> multiset; + + public EntrySet(ImmutableMultiset<E> multiset) { + this.multiset = multiset; + } + + @Override + public UnmodifiableIterator<Entry<E>> iterator() { + return multiset.entryIterator(); + } + + @Override + public int size() { + return multiset.distinctElements(); + } - abstract class EntrySet extends ImmutableSet<Entry<E>> { @Override boolean isPartialView() { - return ImmutableMultiset.this.isPartialView(); + return multiset.isPartialView(); } @Override @@ -361,7 +390,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> if (entry.getCount() <= 0) { return false; } - int count = count(entry.getElement()); + int count = multiset.count(entry.getElement()); return count == entry.getCount(); } return false; @@ -401,29 +430,28 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> @Override public int hashCode() { - return ImmutableMultiset.this.hashCode(); + return multiset.hashCode(); } // We can't label this with @Override, because it doesn't override anything // in the GWT emulated version. - // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead Object writeReplace() { - return new EntrySetSerializedForm<E>(ImmutableMultiset.this); + return new EntrySetSerializedForm<E>(multiset); } - private static final long serialVersionUID = 0; - } + static class EntrySetSerializedForm<E> implements Serializable { + final ImmutableMultiset<E> multiset; - static class EntrySetSerializedForm<E> implements Serializable { - final ImmutableMultiset<E> multiset; + EntrySetSerializedForm(ImmutableMultiset<E> multiset) { + this.multiset = multiset; + } - EntrySetSerializedForm(ImmutableMultiset<E> multiset) { - this.multiset = multiset; + Object readResolve() { + return multiset.entrySet(); + } } - Object readResolve() { - return multiset.entrySet(); - } + private static final long serialVersionUID = 0; } private static class SerializedForm implements Serializable { diff --git a/guava/src/com/google/common/collect/ImmutableRangeMap.java b/guava/src/com/google/common/collect/ImmutableRangeMap.java deleted file mode 100644 index 9545f1d..0000000 --- a/guava/src/com/google/common/collect/ImmutableRangeMap.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2012 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.checkElementIndex; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.SortedLists.KeyAbsentBehavior; -import com.google.common.collect.SortedLists.KeyPresentBehavior; - -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; - -import javax.annotation.Nullable; - -/** - * An immutable implementation of {@code RangeMap}, supporting all query operations efficiently. - * - * <p>Like all {@code RangeMap} implementations, this supports neither null keys nor null values. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("NavigableMap") -public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V> { - - @SuppressWarnings("unchecked") - private static final ImmutableRangeMap EMPTY = - new ImmutableRangeMap(ImmutableList.of(), ImmutableList.of()); - - /** - * Returns an empty immutable range map. - */ - @SuppressWarnings("unchecked") - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() { - return EMPTY; - } - - /** - * Returns an immutable range map mapping a single range to a single value. - */ - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of( - Range<K> range, V value) { - return new ImmutableRangeMap<K, V>(ImmutableList.of(range), ImmutableList.of(value)); - } - - @SuppressWarnings("unchecked") - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf( - RangeMap<K, ? extends V> rangeMap) { - if (rangeMap instanceof ImmutableRangeMap) { - return (ImmutableRangeMap<K, V>) rangeMap; - } - Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges(); - ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<Range<K>>(map.size()); - ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size()); - for (Entry<Range<K>, ? extends V> entry : map.entrySet()) { - rangesBuilder.add(entry.getKey()); - valuesBuilder.add(entry.getValue()); - } - return new ImmutableRangeMap<K, V>(rangesBuilder.build(), valuesBuilder.build()); - } - - /** - * Returns a new builder for an immutable range map. - */ - public static <K extends Comparable<?>, V> Builder<K, V> builder() { - return new Builder<K, V>(); - } - - /** - * A builder for immutable range maps. Overlapping ranges are prohibited. - */ - public static final class Builder<K extends Comparable<?>, V> { - private final RangeSet<K> keyRanges; - private final RangeMap<K, V> rangeMap; - - public Builder() { - this.keyRanges = TreeRangeSet.create(); - this.rangeMap = TreeRangeMap.create(); - } - - /** - * Associates the specified range with the specified value. - * - * @throws IllegalArgumentException if {@code range} overlaps with any other ranges inserted - * into this builder, or if {@code range} is empty - */ - public Builder<K, V> put(Range<K> range, V value) { - checkNotNull(range); - checkNotNull(value); - checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); - if (!keyRanges.complement().encloses(range)) { - // it's an error case; we can afford an expensive lookup - for (Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) { - Range<K> key = entry.getKey(); - if (key.isConnected(range) && !key.intersection(range).isEmpty()) { - throw new IllegalArgumentException( - "Overlapping ranges: range " + range + " overlaps with entry " + entry); - } - } - } - keyRanges.add(range); - rangeMap.put(range, value); - return this; - } - - /** - * Copies all associations from the specified range map into this builder. - * - * @throws IllegalArgumentException if any of the ranges in {@code rangeMap} overlap with ranges - * already in this builder - */ - public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) { - for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { - put(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Returns an {@code ImmutableRangeMap} containing the associations previously added to this - * builder. - */ - public ImmutableRangeMap<K, V> build() { - Map<Range<K>, V> map = rangeMap.asMapOfRanges(); - ImmutableList.Builder<Range<K>> rangesBuilder = - new ImmutableList.Builder<Range<K>>(map.size()); - ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size()); - for (Entry<Range<K>, V> entry : map.entrySet()) { - rangesBuilder.add(entry.getKey()); - valuesBuilder.add(entry.getValue()); - } - return new ImmutableRangeMap<K, V>(rangesBuilder.build(), valuesBuilder.build()); - } - } - - private final ImmutableList<Range<K>> ranges; - private final ImmutableList<V> values; - - ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) { - this.ranges = ranges; - this.values = values; - } - - @Override - @Nullable - public V get(K key) { - int index = SortedLists.binarySearch(ranges, Range.<K>lowerBoundFn(), - Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); - if (index == -1) { - return null; - } else { - Range<K> range = ranges.get(index); - return range.contains(key) ? values.get(index) : null; - } - } - - @Override - @Nullable - public Map.Entry<Range<K>, V> getEntry(K key) { - int index = SortedLists.binarySearch(ranges, Range.<K>lowerBoundFn(), - Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); - if (index == -1) { - return null; - } else { - Range<K> range = ranges.get(index); - return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; - } - } - - @Override - public Range<K> span() { - if (ranges.isEmpty()) { - throw new NoSuchElementException(); - } - Range<K> firstRange = ranges.get(0); - Range<K> lastRange = ranges.get(ranges.size() - 1); - return Range.create(firstRange.lowerBound, lastRange.upperBound); - } - - @Override - public void put(Range<K> range, V value) { - throw new UnsupportedOperationException(); - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<K> range) { - throw new UnsupportedOperationException(); - } - - @Override - public ImmutableMap<Range<K>, V> asMapOfRanges() { - if (ranges.isEmpty()) { - return ImmutableMap.of(); - } - RegularImmutableSortedSet<Range<K>> rangeSet = - new RegularImmutableSortedSet<Range<K>>(ranges, Range.RANGE_LEX_ORDERING); - return new RegularImmutableSortedMap<Range<K>, V>(rangeSet, values); - } - - @Override - public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) { - if (checkNotNull(range).isEmpty()) { - return ImmutableRangeMap.of(); - } else if (ranges.isEmpty() || range.encloses(span())) { - return this; - } - int lowerIndex = SortedLists.binarySearch( - ranges, Range.<K>upperBoundFn(), range.lowerBound, - KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); - int upperIndex = SortedLists.binarySearch(ranges, - Range.<K>lowerBoundFn(), range.upperBound, - KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); - if (lowerIndex >= upperIndex) { - return ImmutableRangeMap.of(); - } - final int off = lowerIndex; - final int len = upperIndex - lowerIndex; - ImmutableList<Range<K>> subRanges = new ImmutableList<Range<K>>() { - @Override - public int size() { - return len; - } - - @Override - public Range<K> get(int index) { - checkElementIndex(index, len); - if (index == 0 || index == len - 1) { - return ranges.get(index + off).intersection(range); - } else { - return ranges.get(index + off); - } - } - - @Override - boolean isPartialView() { - return true; - } - }; - final ImmutableRangeMap<K, V> outer = this; - return new ImmutableRangeMap<K, V>( - subRanges, values.subList(lowerIndex, upperIndex)) { - @Override - public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) { - if (range.isConnected(subRange)) { - return outer.subRangeMap(subRange.intersection(range)); - } else { - return ImmutableRangeMap.of(); - } - } - }; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public String toString() { - return asMapOfRanges().toString(); - } -} diff --git a/guava/src/com/google/common/collect/ImmutableRangeSet.java b/guava/src/com/google/common/collect/ImmutableRangeSet.java deleted file mode 100644 index bd21bbe..0000000 --- a/guava/src/com/google/common/collect/ImmutableRangeSet.java +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Copyright (C) 2012 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.checkElementIndex; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; -import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.SortedLists.KeyAbsentBehavior; -import com.google.common.collect.SortedLists.KeyPresentBehavior; -import com.google.common.primitives.Ints; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * An efficient immutable implementation of a {@link RangeSet}. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public final class ImmutableRangeSet<C extends Comparable> extends AbstractRangeSet<C> - implements Serializable { - - @SuppressWarnings("unchecked") - private static final ImmutableRangeSet EMPTY = new ImmutableRangeSet(ImmutableList.of()); - - @SuppressWarnings("unchecked") - private static final ImmutableRangeSet ALL = new ImmutableRangeSet(ImmutableList.of(Range.all())); - - /** - * Returns an empty immutable range set. - */ - @SuppressWarnings("unchecked") - public static <C extends Comparable> ImmutableRangeSet<C> of() { - return EMPTY; - } - - /** - * Returns an immutable range set containing the single range {@link Range#all()}. - */ - @SuppressWarnings("unchecked") - static <C extends Comparable> ImmutableRangeSet<C> all() { - return ALL; - } - - /** - * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty() - * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}. - */ - public static <C extends Comparable> ImmutableRangeSet<C> of(Range<C> range) { - checkNotNull(range); - if (range.isEmpty()) { - return of(); - } else if (range.equals(Range.all())) { - return all(); - } else { - return new ImmutableRangeSet<C>(ImmutableList.of(range)); - } - } - - /** - * Returns an immutable copy of the specified {@code RangeSet}. - */ - public static <C extends Comparable> ImmutableRangeSet<C> copyOf(RangeSet<C> rangeSet) { - checkNotNull(rangeSet); - if (rangeSet.isEmpty()) { - return of(); - } else if (rangeSet.encloses(Range.<C>all())) { - return all(); - } - - if (rangeSet instanceof ImmutableRangeSet) { - ImmutableRangeSet<C> immutableRangeSet = (ImmutableRangeSet<C>) rangeSet; - if (!immutableRangeSet.isPartialView()) { - return immutableRangeSet; - } - } - return new ImmutableRangeSet<C>(ImmutableList.copyOf(rangeSet.asRanges())); - } - - ImmutableRangeSet(ImmutableList<Range<C>> ranges) { - this.ranges = ranges; - } - - private ImmutableRangeSet(ImmutableList<Range<C>> ranges, ImmutableRangeSet<C> complement) { - this.ranges = ranges; - this.complement = complement; - } - - private transient final ImmutableList<Range<C>> ranges; - - @Override - public boolean encloses(Range<C> otherRange) { - int index = SortedLists.binarySearch(ranges, - Range.<C>lowerBoundFn(), - otherRange.lowerBound, - Ordering.natural(), - ANY_PRESENT, - NEXT_LOWER); - return index != -1 && ranges.get(index).encloses(otherRange); - } - - @Override - public Range<C> rangeContaining(C value) { - int index = SortedLists.binarySearch(ranges, - Range.<C>lowerBoundFn(), - Cut.belowValue(value), - Ordering.natural(), - ANY_PRESENT, - NEXT_LOWER); - if (index != -1) { - Range<C> range = ranges.get(index); - return range.contains(value) ? range : null; - } - return null; - } - - @Override - public Range<C> span() { - if (ranges.isEmpty()) { - throw new NoSuchElementException(); - } - return Range.create( - ranges.get(0).lowerBound, - ranges.get(ranges.size() - 1).upperBound); - } - - @Override - public boolean isEmpty() { - return ranges.isEmpty(); - } - - @Override - public void add(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void addAll(RangeSet<C> other) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeAll(RangeSet<C> other) { - throw new UnsupportedOperationException(); - } - - @Override - public ImmutableSet<Range<C>> asRanges() { - if (ranges.isEmpty()) { - return ImmutableSet.of(); - } - return new RegularImmutableSortedSet<Range<C>>(ranges, Range.RANGE_LEX_ORDERING); - } - - private transient ImmutableRangeSet<C> complement; - - private final class ComplementRanges extends ImmutableList<Range<C>> { - // True if the "positive" range set is empty or bounded below. - private final boolean positiveBoundedBelow; - - // True if the "positive" range set is empty or bounded above. - private final boolean positiveBoundedAbove; - - private final int size; - - ComplementRanges() { - this.positiveBoundedBelow = ranges.get(0).hasLowerBound(); - this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound(); - - int size = ranges.size() - 1; - if (positiveBoundedBelow) { - size++; - } - if (positiveBoundedAbove) { - size++; - } - this.size = size; - } - - @Override - public int size() { - return size; - } - - @Override - public Range<C> get(int index) { - checkElementIndex(index, size); - - Cut<C> lowerBound; - if (positiveBoundedBelow) { - lowerBound = (index == 0) ? Cut.<C>belowAll() : ranges.get(index - 1).upperBound; - } else { - lowerBound = ranges.get(index).upperBound; - } - - Cut<C> upperBound; - if (positiveBoundedAbove && index == size - 1) { - upperBound = Cut.<C>aboveAll(); - } else { - upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound; - } - - return Range.create(lowerBound, upperBound); - } - - @Override - boolean isPartialView() { - return true; - } - } - - @Override - public ImmutableRangeSet<C> complement() { - ImmutableRangeSet<C> result = complement; - if (result != null) { - return result; - } else if (ranges.isEmpty()) { - return complement = all(); - } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) { - return complement = of(); - } else { - ImmutableList<Range<C>> complementRanges = new ComplementRanges(); - result = complement = new ImmutableRangeSet<C>(complementRanges, this); - } - return result; - } - - /** - * Returns a list containing the nonempty intersections of {@code range} - * with the ranges in this range set. - */ - private ImmutableList<Range<C>> intersectRanges(final Range<C> range) { - if (ranges.isEmpty() || range.isEmpty()) { - return ImmutableList.of(); - } else if (range.encloses(span())) { - return ranges; - } - - final int fromIndex; - if (range.hasLowerBound()) { - fromIndex = SortedLists.binarySearch( - ranges, Range.<C>upperBoundFn(), range.lowerBound, KeyPresentBehavior.FIRST_AFTER, - KeyAbsentBehavior.NEXT_HIGHER); - } else { - fromIndex = 0; - } - - int toIndex; - if (range.hasUpperBound()) { - toIndex = SortedLists.binarySearch( - ranges, Range.<C>lowerBoundFn(), range.upperBound, KeyPresentBehavior.FIRST_PRESENT, - KeyAbsentBehavior.NEXT_HIGHER); - } else { - toIndex = ranges.size(); - } - final int length = toIndex - fromIndex; - if (length == 0) { - return ImmutableList.of(); - } else { - return new ImmutableList<Range<C>>() { - @Override - public int size() { - return length; - } - - @Override - public Range<C> get(int index) { - checkElementIndex(index, length); - if (index == 0 || index == length - 1) { - return ranges.get(index + fromIndex).intersection(range); - } else { - return ranges.get(index + fromIndex); - } - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - } - - /** - * Returns a view of the intersection of this range set with the given range. - */ - @Override - public ImmutableRangeSet<C> subRangeSet(Range<C> range) { - if (!isEmpty()) { - Range<C> span = span(); - if (range.encloses(span)) { - return this; - } else if (range.isConnected(span)) { - return new ImmutableRangeSet<C>(intersectRanges(range)); - } - } - return of(); - } - - /** - * Returns an {@link ImmutableSortedSet} containing the same values in the given domain - * {@linkplain RangeSet#contains contained} by this range set. - * - * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For - * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty - * ranges {@code [3..3)} and {@code [4..4)}. - * - * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large - * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on - * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or - * {@link Collections#frequency}) can cause major performance problems. - * - * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's - * contents, such as {@code "[1..100]}"}. - * - * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if - * neither has an upper bound - */ - public ImmutableSortedSet<C> asSet(DiscreteDomain<C> domain) { - checkNotNull(domain); - if (isEmpty()) { - return ImmutableSortedSet.of(); - } - Range<C> span = span().canonical(domain); - if (!span.hasLowerBound()) { - // according to the spec of canonical, neither this ImmutableRangeSet nor - // the range have a lower bound - throw new IllegalArgumentException( - "Neither the DiscreteDomain nor this range set are bounded below"); - } else if (!span.hasUpperBound()) { - try { - domain.maxValue(); - } catch (NoSuchElementException e) { - throw new IllegalArgumentException( - "Neither the DiscreteDomain nor this range set are bounded above"); - } - } - - return new AsSet(domain); - } - - private final class AsSet extends ImmutableSortedSet<C> { - private final DiscreteDomain<C> domain; - - AsSet(DiscreteDomain<C> domain) { - super(Ordering.natural()); - this.domain = domain; - } - - private transient Integer size; - - @Override - public int size() { - // racy single-check idiom - Integer result = size; - if (result == null) { - long total = 0; - for (Range<C> range : ranges) { - total += range.asSet(domain).size(); - if (total >= Integer.MAX_VALUE) { - break; - } - } - result = size = Ints.saturatedCast(total); - } - return result.intValue(); - } - - @Override - public UnmodifiableIterator<C> iterator() { - return new AbstractIterator<C>() { - final Iterator<Range<C>> rangeItr = ranges.iterator(); - Iterator<C> elemItr = Iterators.emptyIterator(); - - @Override - protected C computeNext() { - while (!elemItr.hasNext()) { - if (rangeItr.hasNext()) { - elemItr = rangeItr.next().asSet(domain).iterator(); - } else { - return endOfData(); - } - } - return elemItr.next(); - } - }; - } - - @Override - @GwtIncompatible("NavigableSet") - public UnmodifiableIterator<C> descendingIterator() { - return new AbstractIterator<C>() { - final Iterator<Range<C>> rangeItr = ranges.reverse().iterator(); - Iterator<C> elemItr = Iterators.emptyIterator(); - - @Override - protected C computeNext() { - while (!elemItr.hasNext()) { - if (rangeItr.hasNext()) { - elemItr = rangeItr.next().asSet(domain).descendingIterator(); - } else { - return endOfData(); - } - } - return elemItr.next(); - } - }; - } - - ImmutableSortedSet<C> subSet(Range<C> range) { - return subRangeSet(range).asSet(domain); - } - - @Override - ImmutableSortedSet<C> headSetImpl(C toElement, boolean inclusive) { - return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive))); - } - - @Override - ImmutableSortedSet<C> subSetImpl( - C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { - if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) { - return ImmutableSortedSet.of(); - } - return subSet(Range.range( - fromElement, BoundType.forBoolean(fromInclusive), - toElement, BoundType.forBoolean(toInclusive))); - } - - @Override - ImmutableSortedSet<C> tailSetImpl(C fromElement, boolean inclusive) { - return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); - } - - @Override - public boolean contains(@Nullable Object o) { - if (o == null) { - return false; - } - try { - @SuppressWarnings("unchecked") // we catch CCE's - C c = (C) o; - return ImmutableRangeSet.this.contains(c); - } catch (ClassCastException e) { - return false; - } - } - - @Override - int indexOf(Object target) { - if (contains(target)) { - @SuppressWarnings("unchecked") // if it's contained, it's definitely a C - C c = (C) target; - long total = 0; - for (Range<C> range : ranges) { - if (range.contains(c)) { - return Ints.saturatedCast(total + range.asSet(domain).indexOf(c)); - } else { - total += range.asSet(domain).size(); - } - } - throw new AssertionError("impossible"); - } - return -1; - } - - @Override - boolean isPartialView() { - return ranges.isPartialView(); - } - - @Override - public String toString() { - return ranges.toString(); - } - - @Override - Object writeReplace() { - return new AsSetSerializedForm<C>(ranges, domain); - } - } - - private static class AsSetSerializedForm<C extends Comparable> implements Serializable { - private final ImmutableList<Range<C>> ranges; - private final DiscreteDomain<C> domain; - - AsSetSerializedForm(ImmutableList<Range<C>> ranges, DiscreteDomain<C> domain) { - this.ranges = ranges; - this.domain = domain; - } - - Object readResolve() { - return new ImmutableRangeSet<C>(ranges).asSet(domain); - } - } - - boolean isPartialView() { - return ranges.isPartialView(); - } - - /** - * Returns a new builder for an immutable range set. - */ - public static <C extends Comparable<?>> Builder<C> builder() { - return new Builder<C>(); - } - - /** - * A builder for immutable range sets. - */ - public static class Builder<C extends Comparable<?>> { - private final RangeSet<C> rangeSet; - - public Builder() { - this.rangeSet = TreeRangeSet.create(); - } - - /** - * Add the specified range to this builder. Adjacent/abutting ranges are permitted, but - * empty ranges, or ranges with nonempty overlap, are forbidden. - * - * @throws IllegalArgumentException if {@code range} is empty or has nonempty intersection with - * any ranges already added to the builder - */ - public Builder<C> add(Range<C> range) { - if (range.isEmpty()) { - throw new IllegalArgumentException("range must not be empty, but was " + range); - } else if (!rangeSet.complement().encloses(range)) { - for (Range<C> currentRange : rangeSet.asRanges()) { - checkArgument( - !currentRange.isConnected(range) || currentRange.intersection(range).isEmpty(), - "Ranges may not overlap, but received %s and %s", currentRange, range); - } - throw new AssertionError("should have thrown an IAE above"); - } - rangeSet.add(range); - return this; - } - - /** - * Add all ranges from the specified range set to this builder. Duplicate or connected ranges - * are permitted, and will be merged in the resulting immutable range set. - */ - public Builder<C> addAll(RangeSet<C> ranges) { - for (Range<C> range : ranges.asRanges()) { - add(range); - } - return this; - } - - /** - * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder. - */ - public ImmutableRangeSet<C> build() { - return copyOf(rangeSet); - } - } - - private static final class SerializedForm<C extends Comparable> implements Serializable { - private final ImmutableList<Range<C>> ranges; - - SerializedForm(ImmutableList<Range<C>> ranges) { - this.ranges = ranges; - } - - Object readResolve() { - if (ranges.isEmpty()) { - return of(); - } else if (ranges.equals(ImmutableList.of(Range.all()))) { - return all(); - } else { - return new ImmutableRangeSet<C>(ranges); - } - } - } - - Object writeReplace() { - return new SerializedForm<C>(ranges); - } -} diff --git a/guava/src/com/google/common/collect/ImmutableSet.java b/guava/src/com/google/common/collect/ImmutableSet.java index b96829c..fb60ce6 100644 --- a/guava/src/com/google/common/collect/ImmutableSet.java +++ b/guava/src/com/google/common/collect/ImmutableSet.java @@ -20,14 +20,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import java.io.Serializable; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -59,10 +57,6 @@ import javax.annotation.Nullable; * outside its package as it has no public or protected constructors. Thus, * instances of this type are guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @see ImmutableList * @see ImmutableMap * @author Kevin Bourrillion @@ -102,7 +96,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2) { - return construct(2, e1, e2); + return construct(e1, e2); } /** @@ -113,7 +107,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3) { - return construct(3, e1, e2, e3); + return construct(e1, e2, e3); } /** @@ -124,7 +118,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4) { - return construct(4, e1, e2, e3, e4); + return construct(e1, e2, e3, e4); } /** @@ -135,7 +129,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5) { - return construct(5, e1, e2, e3, e4, e5); + return construct(e1, e2, e3, e4, e5); } /** @@ -156,72 +150,59 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> elements[3] = e4; elements[4] = e5; elements[5] = e6; - System.arraycopy(others, 0, elements, paramCount, others.length); - return construct(elements.length, elements); + for (int i = paramCount; i < elements.length; i++) { + elements[i] = others[i - paramCount]; + } + return construct(elements); } - /** - * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array. - * If {@code k} is the size of the returned {@code ImmutableSet}, then the unique elements of - * {@code elements} will be in the first {@code k} positions, and {@code elements[i] == null} for - * {@code k <= i < n}. - * - * <p>This may modify {@code elements}. Additionally, if {@code n == elements.length} and - * {@code elements} contains no duplicates, {@code elements} may be used without copying in the - * returned {@code ImmutableSet}, in which case it may no longer be modified. - * - * <p>{@code elements} may contain only values of type {@code E}. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is - * null - */ - private static <E> ImmutableSet<E> construct(int n, Object... elements) { - switch (n) { - case 0: - return of(); - case 1: - @SuppressWarnings("unchecked") // safe; elements contains only E's - E elem = (E) elements[0]; - return of(elem); - default: - // continue below to handle the general case - } - int tableSize = chooseTableSize(n); + /** {@code elements} has to be internally created array. */ + private static <E> ImmutableSet<E> construct(Object... elements) { + int tableSize = chooseTableSize(elements.length); Object[] table = new Object[tableSize]; int mask = tableSize - 1; + ArrayList<Object> uniqueElementsList = null; int hashCode = 0; - int uniques = 0; - for (int i = 0; i < n; i++) { - Object element = ObjectArrays.checkElementNotNull(elements[i], i); + for (int i = 0; i < elements.length; i++) { + Object element = elements[i]; int hash = element.hashCode(); for (int j = Hashing.smear(hash); ; j++) { int index = j & mask; Object value = table[index]; if (value == null) { + if (uniqueElementsList != null) { + uniqueElementsList.add(element); + } // Came to an empty slot. Put the element here. - elements[uniques++] = element; table[index] = element; hashCode += hash; break; } else if (value.equals(element)) { + if (uniqueElementsList == null) { + // first dup + uniqueElementsList = new ArrayList<Object>(elements.length); + for (int k = 0; k < i; k++) { + Object previous = elements[k]; + uniqueElementsList.add(previous); + } + } break; } } } - Arrays.fill(elements, uniques, n, null); - if (uniques == 1) { + Object[] uniqueElements = uniqueElementsList == null + ? elements + : uniqueElementsList.toArray(); + if (uniqueElements.length == 1) { // There is only one element or elements are all duplicates @SuppressWarnings("unchecked") // we are careful to only pass in E - E element = (E) elements[0]; + E element = (E) uniqueElements[0]; return new SingletonImmutableSet<E>(element, hashCode); - } else if (tableSize != chooseTableSize(uniques)) { + } else if (tableSize > 2 * chooseTableSize(uniqueElements.length)) { // Resize the table when the array includes too many duplicates. // when this happens, we have already made a copy - return construct(uniques, elements); + return construct(uniqueElements); } else { - Object[] uniqueElements = (uniques < elements.length) - ? ObjectArrays.arraysCopyOf(elements, uniques) - : elements; return new RegularImmutableSet<E>(uniqueElements, hashCode, table, mask); } } @@ -229,30 +210,18 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> // We use power-of-2 tables, and this is the highest int that's a power of 2 static final int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; - // Represents how tightly we can pack things, as a maximum. - private static final double DESIRED_LOAD_FACTOR = 0.7; - // If the set has this many elements, it will "max out" the table size - private static final int CUTOFF = - (int) Math.floor(MAX_TABLE_SIZE * DESIRED_LOAD_FACTOR); + static final int CUTOFF = 1 << 29; /** * Returns an array size suitable for the backing array of a hash table that - * uses open addressing with linear probing in its implementation. The - * returned size is the smallest power of two that can hold setSize elements - * with the desired load factor. - * - * <p>Do not call this method with setSize < 2. + * uses linear probing in its implementation. The returned size is the + * smallest power of two that can hold setSize elements while being at most + * 50% full, if possible. */ - @VisibleForTesting static int chooseTableSize(int setSize) { - // Correct the size for open addressing to match desired load factor. + static int chooseTableSize(int setSize) { if (setSize < CUTOFF) { - // Round up to the next highest power of 2. - int tableSize = Integer.highestOneBit(setSize - 1) << 1; - while (tableSize * DESIRED_LOAD_FACTOR < setSize) { - tableSize <<= 1; - } - return tableSize; + return Integer.highestOneBit(setSize) << 2; } // The table can't be completely full or we'll get infinite reprobes @@ -277,7 +246,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> case 1: return of(elements[0]); default: - return construct(elements.length, elements.clone()); + return construct(elements.clone()); } } @@ -312,19 +281,9 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any of {@code elements} is null */ public static <E> ImmutableSet<E> copyOf(Iterator<? extends E> elements) { - // We special-case for 0 or 1 elements, but anything further is madness. - if (!elements.hasNext()) { - return of(); - } - E first = elements.next(); - if (!elements.hasNext()) { - return of(first); - } else { - return new ImmutableSet.Builder<E>() - .add(first) - .addAll(elements) - .build(); - } + // TODO(benyu): here we could avoid toArray() for 0 or 1-element list, + // worth it? + return copyFromCollection(Lists.newArrayList(elements)); } /** @@ -366,12 +325,6 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> if (!set.isPartialView()) { return set; } - } else if (elements instanceof EnumSet) { - EnumSet<?> enumSet = EnumSet.copyOf((EnumSet<?>) elements); - @SuppressWarnings("unchecked") - // immutable collections are safe for covariant casts - ImmutableSet<E> result = (ImmutableSet<E>) ImmutableEnumSet.asImmutable(enumSet); - return result; } return copyFromCollection(elements); } @@ -389,7 +342,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> default: // safe to use the array without copying it // as specified by Collection.toArray(). - return construct(elements.length, elements); + return construct(elements); } } @@ -438,16 +391,30 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> return false; } + /* + * The cast is safe because the only way to create an instance is via the + * create() method above, which only permits elements of type E. + */ + @SuppressWarnings("unchecked") @Override public UnmodifiableIterator<E> iterator() { - return asList().iterator(); + return (UnmodifiableIterator<E>) Iterators.forArray(elements); } @Override public Object[] toArray() { - return asList().toArray(); + Object[] array = new Object[size()]; + System.arraycopy(elements, 0, array, 0, size()); + return array; } @Override public <T> T[] toArray(T[] array) { - return asList().toArray(array); + int size = size(); + if (array.length < size) { + array = ObjectArrays.newArray(array, size); + } else if (array.length > size) { + array[size] = null; + } + System.arraycopy(elements, 0, array, 0, size); + return array; } @Override public boolean containsAll(Collection<?> targets) { @@ -473,7 +440,65 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> } @Override ImmutableList<E> createAsList() { - return new RegularImmutableAsList<E>(this, elements); + return new ImmutableAsList<E>(elements, this); + } + } + + /** such as ImmutableMap.keySet() */ + abstract static class TransformedImmutableSet<D, E> extends ImmutableSet<E> { + final D[] source; + final int hashCode; + + TransformedImmutableSet(D[] source, int hashCode) { + this.source = source; + this.hashCode = hashCode; + } + + abstract E transform(D element); + + @Override + public int size() { + return source.length; + } + + @Override public boolean isEmpty() { + return false; + } + + @Override public UnmodifiableIterator<E> iterator() { + return new AbstractIndexedListIterator<E>(source.length) { + @Override protected E get(int index) { + return transform(source[index]); + } + }; + } + + @Override public Object[] toArray() { + return toArray(new Object[size()]); + } + + @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; + } + + // Writes will produce ArrayStoreException when the toArray() doc requires + Object[] objectArray = array; + for (int i = 0; i < source.length; i++) { + objectArray[i] = transform(source[i]); + } + return array; + } + + @Override public final int hashCode() { + return hashCode; + } + + @Override boolean isHashCodeFast() { + return true; } } @@ -524,34 +549,14 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @since 2.0 (imported from Google Collections Library) */ public static class Builder<E> extends ImmutableCollection.Builder<E> { - Object[] contents; - int size; + // accessed directly by ImmutableSortedSet + final ArrayList<E> contents = Lists.newArrayList(); /** * Creates a new builder. The returned builder is equivalent to the builder * generated by {@link ImmutableSet#builder}. */ - public Builder() { - this(DEFAULT_INITIAL_CAPACITY); - } - - Builder(int capacity) { - checkArgument(capacity >= 0, "capacity must be >= 0 but was %s", capacity); - this.contents = new Object[capacity]; - this.size = 0; - } - - /** - * Expand the absolute capacity of the builder so it can accept at least - * the specified number of elements without being resized. - */ - Builder<E> ensureCapacity(int minCapacity) { - if (contents.length < minCapacity) { - contents = ObjectArrays.arraysCopyOf( - contents, expandedCapacity(contents.length, minCapacity)); - } - return this; - } + public Builder() {} /** * Adds {@code element} to the {@code ImmutableSet}. If the {@code @@ -563,8 +568,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if {@code element} is null */ @Override public Builder<E> add(E element) { - ensureCapacity(size + 1); - contents[size++] = checkNotNull(element); + contents.add(checkNotNull(element)); return this; } @@ -578,12 +582,8 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * null element */ @Override public Builder<E> add(E... elements) { - for (int i = 0; i < elements.length; i++) { - ObjectArrays.checkElementNotNull(elements[i], i); - } - ensureCapacity(size + elements.length); - System.arraycopy(elements, 0, contents, size, elements.length); - size += elements.length; + contents.ensureCapacity(contents.size() + elements.length); + super.add(elements); return this; } @@ -599,7 +599,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> @Override public Builder<E> addAll(Iterable<? extends E> elements) { if (elements instanceof Collection) { Collection<?> collection = (Collection<?>) elements; - ensureCapacity(size + collection.size()); + contents.ensureCapacity(contents.size() + collection.size()); } super.addAll(elements); return this; @@ -624,11 +624,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * the {@code Builder}. */ @Override public ImmutableSet<E> build() { - ImmutableSet<E> result = construct(size, contents); - // construct has the side effect of deduping contents, so we update size - // accordingly. - size = result.size(); - return result; + return copyOf(contents); } } } diff --git a/guava/src/com/google/common/collect/ImmutableSetMultimap.java b/guava/src/com/google/common/collect/ImmutableSetMultimap.java index 6eedf1a..04a6978 100644 --- a/guava/src/com/google/common/collect/ImmutableSetMultimap.java +++ b/guava/src/com/google/common/collect/ImmutableSetMultimap.java @@ -18,9 +18,9 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Function; import java.io.IOException; import java.io.InvalidObjectException; @@ -28,12 +28,10 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import java.util.Map.Entry; +import java.util.TreeMap; import javax.annotation.Nullable; @@ -53,10 +51,6 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this class * are guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Mike Ward * @since 2.0 (imported from Google Collections Library) */ @@ -151,7 +145,7 @@ public class ImmutableSetMultimap<K, V> * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key * and value orderings and performs better than {@link LinkedHashMultimap}. */ - private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { + private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { BuilderMultimap() { super(new LinkedHashMap<K, Collection<V>>()); } @@ -162,6 +156,23 @@ public class ImmutableSetMultimap<K, V> } /** + * Multimap for {@link ImmutableSetMultimap.Builder} that sorts keys and + * maintains value orderings. + */ + private static class SortedKeyBuilderMultimap<K, V> + extends AbstractMultimap<K, V> { + SortedKeyBuilderMultimap( + Comparator<? super K> keyComparator, Multimap<K, V> multimap) { + super(new TreeMap<K, Collection<V>>(keyComparator)); + putAll(multimap); + } + @Override Collection<V> createCollection() { + return Sets.newLinkedHashSet(); + } + private static final long serialVersionUID = 0; + } + + /** * A builder for creating immutable {@code SetMultimap} instances, especially * {@code public static final} multimaps ("constant multimaps"). Example: * <pre> {@code @@ -186,7 +197,7 @@ public class ImmutableSetMultimap<K, V> * generated by {@link ImmutableSetMultimap#builder}. */ public Builder() { - builderMultimap = new BuilderMultimap<K, V>(); + builderMultimap = new BuilderMultimap<K, V>(); } /** @@ -235,25 +246,26 @@ public class ImmutableSetMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { - this.keyComparator = checkNotNull(keyComparator); + builderMultimap = new SortedKeyBuilderMultimap<K, V>( + checkNotNull(keyComparator), builderMultimap); return this; } /** * Specifies the ordering of the generated multimap's values for each key. - * - * <p>If this method is called, the sets returned by the {@code get()} + * + * <p>If this method is called, the sets returned by the {@code get()} * method of the generated multimap and its {@link Multimap#asMap()} view * are {@link ImmutableSortedSet} instances. However, serialization does not * preserve that property, though it does maintain the key and value * ordering. - * + * * @since 8.0 */ // TODO: Make serialization behavior consistent. - @Override + @Beta @Override public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { super.orderValuesBy(valueComparator); return this; @@ -263,23 +275,6 @@ public class ImmutableSetMultimap<K, V> * Returns a newly-created immutable set multimap. */ @Override public ImmutableSetMultimap<K, V> build() { - if (keyComparator != null) { - Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>(); - List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList( - builderMultimap.asMap().entrySet()); - Collections.sort( - entries, - Ordering.from(keyComparator).onResultOf(new Function<Entry<K, Collection<V>>, K>() { - @Override - public K apply(Entry<K, Collection<V>> entry) { - return entry.getKey(); - } - })); - for (Map.Entry<K, Collection<V>> entry : entries) { - sortedCopy.putAll(entry.getKey(), entry.getValue()); - } - builderMultimap = sortedCopy; - } return copyOf(builderMultimap, valueComparator); } } @@ -302,7 +297,7 @@ public class ImmutableSetMultimap<K, V> Multimap<? extends K, ? extends V> multimap) { return copyOf(multimap, null); } - + private static <K, V> ImmutableSetMultimap<K, V> copyOf( Multimap<? extends K, ? extends V> multimap, Comparator<? super V> valueComparator) { @@ -328,7 +323,7 @@ public class ImmutableSetMultimap<K, V> K key = entry.getKey(); Collection<? extends V> values = entry.getValue(); ImmutableSet<V> set = (valueComparator == null) - ? ImmutableSet.copyOf(values) + ? ImmutableSet.copyOf(values) : ImmutableSortedSet.copyOf(valueComparator, values); if (!set.isEmpty()) { builder.put(key, set); @@ -343,10 +338,10 @@ public class ImmutableSetMultimap<K, V> // Returned by get() when values are sorted and a missing key is provided. private final transient ImmutableSortedSet<V> emptySet; - ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size, + ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size, @Nullable Comparator<? super V> valueComparator) { super(map, size); - this.emptySet = (valueComparator == null) + this.emptySet = (valueComparator == null) ? null : ImmutableSortedSet.<V>emptySet(valueComparator); } @@ -375,13 +370,13 @@ public class ImmutableSetMultimap<K, V> /** * {@inheritDoc} * - * <p>Because an inverse of a set multimap cannot contain multiple pairs with - * the same key and value, this method returns an {@code ImmutableSetMultimap} - * rather than the {@code ImmutableMultimap} specified in the {@code - * ImmutableMultimap} class. + * <p>Because an inverse of a set multimap cannot contain multiple pairs with the same key and + * value, this method returns an {@code ImmutableSetMultimap} rather than the + * {@code ImmutableMultimap} specified in the {@code ImmutableMultimap} class. * - * @since 11.0 + * @since 11 */ + @Beta public ImmutableSetMultimap<V, K> inverse() { ImmutableSetMultimap<V, K> result = inverse; return (result == null) ? (inverse = invert()) : result; @@ -401,9 +396,8 @@ public class ImmutableSetMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableSet<V> removeAll(Object key) { + @Override public ImmutableSet<V> removeAll(Object key) { throw new UnsupportedOperationException(); } @@ -411,9 +405,8 @@ public class ImmutableSetMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableSet<V> replaceValues( + @Override public ImmutableSet<V> replaceValues( K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); } diff --git a/guava/src/com/google/common/collect/ImmutableSortedAsList.java b/guava/src/com/google/common/collect/ImmutableSortedAsList.java index 98aab41..e557570 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedAsList.java +++ b/guava/src/com/google/common/collect/ImmutableSortedAsList.java @@ -14,8 +14,7 @@ package com.google.common.collect; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; import java.util.Comparator; @@ -27,60 +26,80 @@ import javax.annotation.Nullable; * @author Jared Levy * @author Louis Wasserman */ -@GwtCompatible(emulated = true) @SuppressWarnings("serial") -final class ImmutableSortedAsList<E> extends RegularImmutableAsList<E> - implements SortedIterable<E> { +final class ImmutableSortedAsList<E> extends ImmutableList<E> implements SortedIterable<E> { + private final transient ImmutableSortedSet<E> backingSet; + private final transient ImmutableList<E> backingList; + ImmutableSortedAsList( ImmutableSortedSet<E> backingSet, ImmutableList<E> backingList) { - super(backingSet, backingList); - } - - @Override - ImmutableSortedSet<E> delegateCollection() { - return (ImmutableSortedSet<E>) super.delegateCollection(); + this.backingSet = backingSet; + this.backingList = backingList; } @Override public Comparator<? super E> comparator() { - return delegateCollection().comparator(); + return backingSet.comparator(); } - // Override indexOf() and lastIndexOf() to be O(log N) instead of O(N). + // Override contains(), indexOf(), and lastIndexOf() to be O(log N) instead of O(N). + + @Override public boolean contains(@Nullable Object target) { + // TODO: why not contains(target)? + return backingSet.indexOf(target) >= 0; + } - @GwtIncompatible("ImmutableSortedSet.indexOf") - // TODO(cpovirk): consider manual binary search under GWT to preserve O(log N) lookup @Override public int indexOf(@Nullable Object target) { - int index = delegateCollection().indexOf(target); + return backingSet.indexOf(target); + } - // TODO(kevinb): reconsider if it's really worth making feeble attempts at - // sanity for inconsistent comparators. + @Override public int lastIndexOf(@Nullable Object target) { + return backingSet.indexOf(target); + } - // The equals() check is needed when the comparator isn't compatible with - // equals(). - return (index >= 0 && get(index).equals(target)) ? index : -1; + // The returned ImmutableSortedAsList maintains the contains(), indexOf(), and + // lastIndexOf() performance benefits. + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, size()); + return (fromIndex == toIndex) ? ImmutableList.<E>of() + : new RegularImmutableSortedSet<E>( + backingList.subList(fromIndex, toIndex), backingSet.comparator()) + .asList(); } - @GwtIncompatible("ImmutableSortedSet.indexOf") - @Override public int lastIndexOf(@Nullable Object target) { - return indexOf(target); + // The ImmutableAsList serialized form has the correct behavior. + @Override Object writeReplace() { + return new ImmutableAsList.SerializedForm(backingSet); + } + + @Override public UnmodifiableIterator<E> iterator() { + return backingList.iterator(); + } + + @Override public E get(int index) { + return backingList.get(index); + } + + @Override public UnmodifiableListIterator<E> listIterator() { + return backingList.listIterator(); + } + + @Override public UnmodifiableListIterator<E> listIterator(int index) { + return backingList.listIterator(index); + } + + @Override public int size() { + return backingList.size(); + } + + @Override public boolean equals(@Nullable Object obj) { + return backingList.equals(obj); } - @Override - public boolean contains(Object target) { - // Necessary for ISS's with comparators inconsistent with equals. - return indexOf(target) >= 0; + @Override public int hashCode() { + return backingList.hashCode(); } - @GwtIncompatible("super.subListUnchecked does not exist; inherited subList is valid if slow") - /* - * TODO(cpovirk): if we start to override indexOf/lastIndexOf under GWT, we'll want some way to - * override subList to return an ImmutableSortedAsList for better performance. Right now, I'm not - * sure there's any performance hit from our failure to override subListUnchecked under GWT - */ - @Override - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new RegularImmutableSortedSet<E>( - super.subListUnchecked(fromIndex, toIndex), comparator()) - .asList(); + @Override boolean isPartialView() { + return backingList.isPartialView(); } } diff --git a/guava/src/com/google/common/collect/ImmutableSortedMap.java b/guava/src/com/google/common/collect/ImmutableSortedMap.java index d8420fe..c700f7f 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedMap.java +++ b/guava/src/com/google/common/collect/ImmutableSortedMap.java @@ -18,17 +18,22 @@ 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.collect.Maps.keyOrNull; +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.annotations.GwtCompatible; +import com.google.common.collect.SortedLists.KeyAbsentBehavior; +import com.google.common.collect.SortedLists.KeyPresentBehavior; +import java.io.Serializable; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.NavigableMap; +import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.TreeMap; @@ -48,64 +53,28 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this class are * guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jared Levy * @author Louis Wasserman - * @since 2.0 (imported from Google Collections Library; implements {@code - * NavigableMap} since 12.0) + * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(serializable = true, emulated = true) -public abstract class ImmutableSortedMap<K, V> - extends ImmutableSortedMapFauxverideShim<K, V> implements NavigableMap<K, V> { +public class ImmutableSortedMap<K, V> + extends ImmutableSortedMapFauxverideShim<K, V> implements SortedMap<K, V> { /* * TODO(kevinb): Confirm that ImmutableSortedMap is faster to construct and * uses less memory than TreeMap; then say so in the class Javadoc. + * + * TODO(kevinb): Create separate subclasses for empty, single-entry, and + * multiple-entry instances, if it's deemed beneficial. */ - private static final Comparator<Comparable> NATURAL_ORDER = Ordering.natural(); - - private static final ImmutableSortedMap<Comparable, Object> NATURAL_EMPTY_MAP = - new EmptyImmutableSortedMap<Comparable, Object>(NATURAL_ORDER); - - static <K, V> ImmutableSortedMap<K, V> emptyMap(Comparator<? super K> comparator) { - if (Ordering.natural().equals(comparator)) { - return of(); - } else { - return new EmptyImmutableSortedMap<K, V>(comparator); - } - } - - static <K, V> ImmutableSortedMap<K, V> fromSortedEntries( - Comparator<? super K> comparator, - Collection<? extends Entry<? extends K, ? extends V>> entries) { - if (entries.isEmpty()) { - return emptyMap(comparator); - } - ImmutableList.Builder<K> keyBuilder = ImmutableList.builder(); - ImmutableList.Builder<V> valueBuilder = ImmutableList.builder(); - for (Entry<? extends K, ? extends V> entry : entries) { - keyBuilder.add(entry.getKey()); - valueBuilder.add(entry.getValue()); - } + private static final Comparator<Comparable> NATURAL_ORDER = + Ordering.natural(); - return new RegularImmutableSortedMap<K, V>( - new RegularImmutableSortedSet<K>(keyBuilder.build(), comparator), - valueBuilder.build()); - } - - static <K, V> ImmutableSortedMap<K, V> from( - ImmutableSortedSet<K> keySet, ImmutableList<V> valueList) { - if (keySet.isEmpty()) { - return emptyMap(keySet.comparator()); - } else { - return new RegularImmutableSortedMap<K, V>( - (RegularImmutableSortedSet<K>) keySet, - valueList); - } - } + private static final ImmutableSortedMap<Comparable, Object> + NATURAL_EMPTY_MAP = + new ImmutableSortedMap<Comparable, Object>( + ImmutableList.<Entry<Comparable, Object>>of(), NATURAL_ORDER); /** * Returns the empty sorted map. @@ -117,12 +86,24 @@ public abstract class ImmutableSortedMap<K, V> return (ImmutableSortedMap<K, V>) NATURAL_EMPTY_MAP; } + @SuppressWarnings("unchecked") + private static <K, V> ImmutableSortedMap<K, V> emptyMap( + Comparator<? super K> comparator) { + if (NATURAL_ORDER.equals(comparator)) { + return (ImmutableSortedMap<K, V>) NATURAL_EMPTY_MAP; + } else { + return new ImmutableSortedMap<K, V>( + ImmutableList.<Entry<K, V>>of(), comparator); + } + } + /** * Returns an immutable map containing a single entry. */ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(K k1, V v1) { - return from(ImmutableSortedSet.of(k1), ImmutableList.of(v1)); + return new ImmutableSortedMap<K, V>( + ImmutableList.of(entryOf(k1, v1)), Ordering.natural()); } /** @@ -249,7 +230,7 @@ public abstract class ImmutableSortedMap<K, V> SortedMap<?, ?> sortedMap = (SortedMap<?, ?>) map; Comparator<?> comparator2 = sortedMap.comparator(); sameComparator = (comparator2 == null) - ? comparator == NATURAL_ORDER + ? comparator == NATURAL_ORDER : comparator.equals(comparator2); } @@ -264,7 +245,7 @@ public abstract class ImmutableSortedMap<K, V> } // "adding" type params to an array of a raw type should be safe as - // long as no one can ever cast that same array instance back to a + // long as no one can ever cast that same array instance back to a // raw type. @SuppressWarnings("unchecked") Entry<K, V>[] entries = map.entrySet().toArray(new Entry[0]); @@ -281,9 +262,9 @@ public abstract class ImmutableSortedMap<K, V> validateEntries(list, comparator); } - return fromSortedEntries(comparator, list); + return new ImmutableSortedMap<K, V>(ImmutableList.copyOf(list), comparator); } - + private static <K, V> void sortEntries( List<Entry<K, V>> entries, final Comparator<? super K> comparator) { Comparator<Entry<K, V>> entryComparator = new Comparator<Entry<K, V>>() { @@ -292,7 +273,7 @@ public abstract class ImmutableSortedMap<K, V> return comparator.compare(entry1.getKey(), entry2.getKey()); } }; - + Collections.sort(entries, entryComparator); } @@ -312,8 +293,13 @@ public abstract class ImmutableSortedMap<K, V> * Returns a builder that creates immutable sorted maps whose keys are * ordered by their natural ordering. The sorted maps use {@link * Ordering#natural()} as the comparator. + * + * <p>Note: the type parameter {@code K} extends {@code Comparable<K>} rather + * than {@code Comparable<? super K>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <K extends Comparable<?>, V> Builder<K, V> naturalOrder() { + public static <K extends Comparable<K>, V> Builder<K, V> naturalOrder() { return new Builder<K, V>(Ordering.natural()); } @@ -332,8 +318,13 @@ public abstract class ImmutableSortedMap<K, V> /** * Returns a builder that creates immutable sorted maps whose keys are * ordered by the reverse of their natural ordering. + * + * <p>Note: the type parameter {@code K} extends {@code Comparable<K>} rather + * than {@code Comparable<? super K>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <K extends Comparable<?>, V> Builder<K, V> reverseOrder() { + public static <K extends Comparable<K>, V> Builder<K, V> reverseOrder() { return new Builder<K, V>(Ordering.natural().reverse()); } @@ -414,48 +405,209 @@ public abstract class ImmutableSortedMap<K, V> @Override public ImmutableSortedMap<K, V> build() { sortEntries(entries, comparator); validateEntries(entries, comparator); - return fromSortedEntries(comparator, entries); + return new ImmutableSortedMap<K, V>( + ImmutableList.copyOf(entries), comparator); } } - ImmutableSortedMap() { - } + final transient ImmutableList<Entry<K, V>> entries; + private final transient Comparator<? super K> comparator; - ImmutableSortedMap(ImmutableSortedMap<K, V> descendingMap) { - this.descendingMap = descendingMap; + ImmutableSortedMap( + ImmutableList<Entry<K, V>> entries, Comparator<? super K> comparator) { + this.entries = entries; + this.comparator = comparator; } @Override public int size() { - return values().size(); + return entries.size(); + } + + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator() { + return (Comparator<Object>) comparator; + } + + @Override public V get(@Nullable Object key) { + if (key == null) { + return null; + } + int i; + try { + i = index(key, ANY_PRESENT, INVERTED_INSERTION_INDEX); + } catch (ClassCastException e) { + return null; + } + return i >= 0 ? entries.get(i).getValue() : null; } @Override public boolean containsValue(@Nullable Object value) { - return values().contains(value); + if (value == null) { + return false; + } + return Iterators.contains(valueIterator(), value); } @Override boolean isPartialView() { - return keySet().isPartialView() || values().isPartialView(); + return entries.isPartialView(); } + private transient ImmutableSet<Entry<K, V>> entrySet; + /** * Returns an immutable set of the mappings in this map, sorted by the key * ordering. */ @Override public ImmutableSet<Entry<K, V>> entrySet() { - return super.entrySet(); + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = createEntrySet()) : es; } + private ImmutableSet<Entry<K, V>> createEntrySet() { + return isEmpty() ? ImmutableSet.<Entry<K, V>>of() + : new EntrySet<K, V>(this); + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> { + final transient ImmutableSortedMap<K, V> map; + + EntrySet(ImmutableSortedMap<K, V> map) { + this.map = map; + } + + @Override boolean isPartialView() { + return map.isPartialView(); + } + + @Override + public int size() { + return map.size(); + } + + @Override public UnmodifiableIterator<Entry<K, V>> iterator() { + return map.entries.iterator(); + } + + @Override public boolean contains(Object target) { + if (target instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) target; + V mappedValue = map.get(entry.getKey()); + return mappedValue != null && mappedValue.equals(entry.getValue()); + } + return false; + } + + @Override Object writeReplace() { + return new EntrySetSerializedForm<K, V>(map); + } + } + + private static class EntrySetSerializedForm<K, V> implements Serializable { + final ImmutableSortedMap<K, V> map; + EntrySetSerializedForm(ImmutableSortedMap<K, V> map) { + this.map = map; + } + Object readResolve() { + return map.entrySet(); + } + private static final long serialVersionUID = 0; + } + + private transient ImmutableSortedSet<K> keySet; + /** * Returns an immutable sorted set of the keys in this map. */ - @Override public abstract ImmutableSortedSet<K> keySet(); + @Override public ImmutableSortedSet<K> keySet() { + ImmutableSortedSet<K> ks = keySet; + return (ks == null) ? (keySet = createKeySet()) : ks; + } + + @SuppressWarnings("serial") // does not use default serialization + private ImmutableSortedSet<K> createKeySet() { + if (isEmpty()) { + return ImmutableSortedSet.emptySet(comparator); + } + + return new RegularImmutableSortedSet<K>( + new TransformedImmutableList<Entry<K, V>, K>(entries) { + + @Override K transform(Entry<K, V> entry) { + return entry.getKey(); + } + }, comparator); + } + + private transient ImmutableCollection<V> values; /** * Returns an immutable collection of the values in this map, sorted by the * ordering of the corresponding keys. */ - @Override public abstract ImmutableCollection<V> values(); + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(this)) : v; + } + + UnmodifiableIterator<V> valueIterator(){ + final UnmodifiableIterator<Entry<K, V>> entryIterator = entries.iterator(); + return new UnmodifiableIterator<V>() { + + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override public V next() { + return entryIterator.next().getValue(); + } + }; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + private final ImmutableSortedMap<?, V> map; + + Values(ImmutableSortedMap<?, V> map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override public UnmodifiableIterator<V> iterator() { + return map.valueIterator(); + } + + @Override public boolean contains(Object target) { + return map.containsValue(target); + } + + @Override boolean isPartialView() { + return true; + } + + @Override Object writeReplace() { + return new ValuesSerializedForm<V>(map); + } + } + + private static class ValuesSerializedForm<V> implements Serializable { + final ImmutableSortedMap<?, V> map; + ValuesSerializedForm(ImmutableSortedMap<?, V> map) { + this.map = map; + } + Object readResolve() { + return map.values(); + } + private static final long serialVersionUID = 0; + } /** * Returns the comparator that orders the keys, which is @@ -465,17 +617,23 @@ public abstract class ImmutableSortedMap<K, V> */ @Override public Comparator<? super K> comparator() { - return keySet().comparator(); + return comparator; } @Override public K firstKey() { - return keySet().first(); + if (isEmpty()) { + throw new NoSuchElementException(); + } + return entries.get(0).getKey(); } @Override public K lastKey() { - return keySet().last(); + if (isEmpty()) { + throw new NoSuchElementException(); + } + return entries.get(size() - 1).getKey(); } /** @@ -493,20 +651,15 @@ public abstract class ImmutableSortedMap<K, V> return headMap(toKey, false); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys are less than (or equal to, if {@code inclusive}) {@code toKey}. - * - * <p>The {@link SortedMap#headMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code toKey} - * greater than an earlier {@code toKey}. However, this method doesn't throw - * an exception in that situation, but instead keeps the original {@code - * toKey}. - * - * @since 12.0 - */ - @Override - public abstract ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive); + ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive){ + int index; + if (inclusive) { + index = index(toKey, ANY_PRESENT, NEXT_LOWER) + 1; + } else { + index = index(toKey, ANY_PRESENT, NEXT_HIGHER); + } + return createSubmap(0, index); + } /** * This method returns a {@code ImmutableSortedMap}, consisting of the entries @@ -526,29 +679,12 @@ public abstract class ImmutableSortedMap<K, V> return subMap(fromKey, true, toKey, false); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys ranges from {@code fromKey} to {@code toKey}, inclusive or - * exclusive as indicated by the boolean flags. - * - * <p>The {@link SortedMap#subMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code - * fromKey} less than an earlier {@code fromKey}. However, this method doesn't - * throw an exception in that situation, but instead keeps the original {@code - * fromKey}. Similarly, this method keeps the original {@code toKey}, instead - * of throwing an exception, if passed a {@code toKey} greater than an earlier - * {@code toKey}. - * - * @since 12.0 - */ - @Override - public ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, + ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { checkNotNull(fromKey); checkNotNull(toKey); - checkArgument(comparator().compare(fromKey, toKey) <= 0, - "expected fromKey <= toKey but %s > %s", fromKey, toKey); - return headMap(toKey, toInclusive).tailMap(fromKey, fromInclusive); + checkArgument(comparator.compare(fromKey, toKey) <= 0); + return tailMap(fromKey, fromInclusive).headMap(toKey, toInclusive); } /** @@ -566,117 +702,39 @@ public abstract class ImmutableSortedMap<K, V> return tailMap(fromKey, true); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys are greater than (or equal to, if {@code inclusive}) - * {@code fromKey}. - * - * <p>The {@link SortedMap#tailMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code - * fromKey} less than an earlier {@code fromKey}. However, this method doesn't - * throw an exception in that situation, but instead keeps the original {@code - * fromKey}. - * - * @since 12.0 - */ - @Override - public abstract ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive); - - @Override - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return isEmpty() ? null : entrySet().asList().get(0); - } - - @Override - public Entry<K, V> lastEntry() { - return isEmpty() ? null : entrySet().asList().get(size() - 1); + ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { + int index; + if (inclusive) { + index = index(fromKey, ANY_PRESENT, NEXT_HIGHER); + } else { + index = index(fromKey, ANY_PRESENT, NEXT_LOWER) + 1; + } + return createSubmap(index, size()); } - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - public final Entry<K, V> pollFirstEntry() { - throw new UnsupportedOperationException(); + private ImmutableList<K> keyList() { + return new TransformedImmutableList<Entry<K, V>, K>(entries) { + @Override + K transform(Entry<K, V> entry) { + return entry.getKey(); + } + }; } - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - public final Entry<K, V> pollLastEntry() { - throw new UnsupportedOperationException(); + private int index( + Object key, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + return SortedLists.binarySearch( + keyList(), checkNotNull(key), unsafeComparator(), presentBehavior, absentBehavior); } - private transient ImmutableSortedMap<K, V> descendingMap; - - @Override - public ImmutableSortedMap<K, V> descendingMap() { - ImmutableSortedMap<K, V> result = descendingMap; - if (result == null) { - result = descendingMap = createDescendingMap(); + private ImmutableSortedMap<K, V> createSubmap( + int newFromIndex, int newToIndex) { + if (newFromIndex < newToIndex) { + return new ImmutableSortedMap<K, V>( + entries.subList(newFromIndex, newToIndex), comparator); + } else { + return emptyMap(comparator); } - return result; - } - - abstract ImmutableSortedMap<K, V> createDescendingMap(); - - @Override - public ImmutableSortedSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public ImmutableSortedSet<K> descendingKeySet() { - return keySet().descendingSet(); } /** diff --git a/guava/src/com/google/common/collect/ImmutableSortedMultiset.java b/guava/src/com/google/common/collect/ImmutableSortedMultiset.java index dadcfb5..82f4abe 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/ImmutableSortedMultiset.java @@ -14,13 +14,12 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -62,7 +61,7 @@ import java.util.List; * * {(x, y) | x.compareTo(y) == 0}}</pre> * - * <b>Warning:</b> Like most multisets, an {@code ImmutableSortedMultiset} will not function + * <b>Warning:</b> Like most multisets, an {@code ImmutableSortedMultiset} will not function * correctly if an element is modified after being placed in the multiset. For this reason, and to * avoid general confusion, it is strongly recommended to place only immutable objects into this * collection. @@ -70,16 +69,10 @@ import java.util.List; * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as it has no public or * protected constructors. Thus, instances of this type are guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Louis Wasserman - * @since 12.0 */ -@Beta @GwtIncompatible("hasn't been tested yet") -public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> +abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> implements SortedMultiset<E> { // TODO(user): GWT compatibility @@ -100,11 +93,8 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * Returns an immutable sorted multiset containing a single element. */ public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E element) { - RegularImmutableSortedSet<E> elementSet = - (RegularImmutableSortedSet<E>) ImmutableSortedSet.of(element); - int[] counts = {1}; - long[] cumulativeCounts = {0, 1}; - return new RegularImmutableSortedMultiset<E>(elementSet, counts, cumulativeCounts, 0, 1); + return RegularImmutableSortedMultiset.createFromSorted( + NATURAL_ORDER, ImmutableList.of(Multisets.immutableEntry(checkNotNull(element), 1))); } /** @@ -161,9 +151,15 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ @SuppressWarnings("unchecked") public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( - E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + E e1, + E e2, + E e3, + E e4, + E e5, + E e6, + E... remaining) { int size = remaining.length + 6; - List<E> all = Lists.newArrayListWithCapacity(size); + List<E> all = new ArrayList<E>(size); Collections.addAll(all, e1, e2, e3, e4, e5, e6); Collections.addAll(all, remaining); return copyOf(Ordering.natural(), all); @@ -224,7 +220,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset // Unsafe, see ImmutableSortedMultisetFauxverideShim. @SuppressWarnings("unchecked") Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); - return copyOf(naturalOrder, elements); + return copyOfInternal(naturalOrder, elements); } /** @@ -236,7 +232,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset public static <E> ImmutableSortedMultiset<E> copyOf( Comparator<? super E> comparator, Iterator<? extends E> elements) { checkNotNull(comparator); - return new Builder<E>(comparator).addAll(elements).build(); + return copyOfInternal(comparator, elements); } /** @@ -251,21 +247,8 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ public static <E> ImmutableSortedMultiset<E> copyOf( Comparator<? super E> comparator, Iterable<? extends E> elements) { - if (elements instanceof ImmutableSortedMultiset) { - @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts - ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) elements; - if (comparator.equals(multiset.comparator())) { - if (multiset.isPartialView()) { - return copyOfSortedEntries(comparator, multiset.entrySet().asList()); - } else { - return multiset; - } - } - } - elements = Lists.newArrayList(elements); // defensive copy - TreeMultiset<E> sortedCopy = TreeMultiset.create(checkNotNull(comparator)); - Iterables.addAll(sortedCopy, elements); - return copyOfSortedEntries(comparator, sortedCopy.entrySet()); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -282,29 +265,50 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * * @throws NullPointerException if {@code sortedMultiset} or any of its elements is null */ + @SuppressWarnings("unchecked") public static <E> ImmutableSortedMultiset<E> copyOfSorted(SortedMultiset<E> sortedMultiset) { - return copyOfSortedEntries(sortedMultiset.comparator(), - Lists.newArrayList(sortedMultiset.entrySet())); + Comparator<? super E> comparator = sortedMultiset.comparator(); + if (comparator == null) { + comparator = (Comparator<? super E>) NATURAL_ORDER; + } + return copyOfInternal(comparator, sortedMultiset); + } + + @SuppressWarnings("unchecked") + private static <E> ImmutableSortedMultiset<E> copyOfInternal( + Comparator<? super E> comparator, Iterable<? extends E> iterable) { + if (SortedIterables.hasSameComparator(comparator, iterable) + && iterable instanceof ImmutableSortedMultiset<?>) { + ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) iterable; + if (!multiset.isPartialView()) { + return (ImmutableSortedMultiset<E>) iterable; + } + } + ImmutableList<Entry<E>> entries = + (ImmutableList) ImmutableList.copyOf(SortedIterables.sortedCounts(comparator, iterable)); + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + verifyEntries(entries); + return RegularImmutableSortedMultiset.createFromSorted(comparator, entries); } - private static <E> ImmutableSortedMultiset<E> copyOfSortedEntries( - Comparator<? super E> comparator, Collection<Entry<E>> entries) { + private static <E> ImmutableSortedMultiset<E> copyOfInternal( + Comparator<? super E> comparator, Iterator<? extends E> iterator) { + @SuppressWarnings("unchecked") // We can safely cast from IL<Entry<? extends E>> to IL<Entry<E>> + ImmutableList<Entry<E>> entries = + (ImmutableList) ImmutableList.copyOf(SortedIterables.sortedCounts(comparator, iterator)); if (entries.isEmpty()) { return emptyMultiset(comparator); } - ImmutableList.Builder<E> elementsBuilder = new ImmutableList.Builder<E>(entries.size()); - int[] counts = new int[entries.size()]; - long[] cumulativeCounts = new long[entries.size() + 1]; - int i = 0; + verifyEntries(entries); + return RegularImmutableSortedMultiset.createFromSorted(comparator, entries); + } + + private static <E> void verifyEntries(Collection<Entry<E>> entries) { for (Entry<E> entry : entries) { - elementsBuilder.add(entry.getElement()); - counts[i] = entry.getCount(); - cumulativeCounts[i + 1] = cumulativeCounts[i] + counts[i]; - i++; + checkNotNull(entry.getElement()); } - return new RegularImmutableSortedMultiset<E>( - new RegularImmutableSortedSet<E>(elementsBuilder.build(), comparator), - counts, cumulativeCounts, 0, entries.size()); } @SuppressWarnings("unchecked") @@ -315,15 +319,49 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset return new EmptyImmutableSortedMultiset<E>(comparator); } - ImmutableSortedMultiset() {} + private final transient Comparator<? super E> comparator; + + ImmutableSortedMultiset(Comparator<? super E> comparator) { + this.comparator = checkNotNull(comparator); + } @Override - public final Comparator<? super E> comparator() { - return elementSet().comparator(); + public Comparator<? super E> comparator() { + return comparator; } + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator() { + return (Comparator<Object>) comparator; + } + + private transient Comparator<? super E> reverseComparator; + + Comparator<? super E> reverseComparator() { + Comparator<? super E> result = reverseComparator; + if (result == null) { + return reverseComparator = Ordering.from(comparator).<E>reverse(); + } + return result; + } + + private transient ImmutableSortedSet<E> elementSet; + @Override - public abstract ImmutableSortedSet<E> elementSet(); + public ImmutableSortedSet<E> elementSet() { + ImmutableSortedSet<E> result = elementSet; + if (result == null) { + return elementSet = createElementSet(); + } + return result; + } + + abstract ImmutableSortedSet<E> createElementSet(); + + abstract ImmutableSortedSet<E> createDescendingElementSet(); transient ImmutableSortedMultiset<E> descendingMultiset; @@ -336,15 +374,13 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset return result; } + abstract UnmodifiableIterator<Entry<E>> descendingEntryIterator(); + /** * {@inheritDoc} * * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final Entry<E> pollFirstEntry() { throw new UnsupportedOperationException(); @@ -354,13 +390,9 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * {@inheritDoc} * * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override - public final Entry<E> pollLastEntry() { + public Entry<E> pollLastEntry() { throw new UnsupportedOperationException(); } @@ -370,8 +402,6 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset @Override public ImmutableSortedMultiset<E> subMultiset( E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { - checkArgument(comparator().compare(lowerBound, upperBound) <= 0, - "Expected lowerBound <= upperBound but %s > %s", lowerBound, upperBound); return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); } @@ -432,8 +462,6 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * * Builder instances can be reused; it is safe to call {@link #build} multiple times to build * multiple multisets in series. - * - * @since 12.0 */ public static class Builder<E> extends ImmutableMultiset.Builder<E> { private final Comparator<? super E> comparator; @@ -538,7 +566,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ @Override public ImmutableSortedMultiset<E> build() { - return copyOfSorted((SortedMultiset<E>) contents); + return copyOf(comparator, contents); } } diff --git a/guava/src/com/google/common/collect/ImmutableSortedSet.java b/guava/src/com/google/common/collect/ImmutableSortedSet.java index cfeb8a5..b2d871f 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/ImmutableSortedSet.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.InvalidObjectException; import java.io.ObjectInputStream; @@ -32,7 +31,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.NavigableSet; import java.util.SortedSet; import javax.annotation.Nullable; @@ -80,20 +78,16 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this type are * guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @see ImmutableSet * @author Jared Levy * @author Louis Wasserman - * @since 2.0 (imported from Google Collections Library; implements {@code NavigableSet} since 12.0) + * @since 2.0 (imported from Google Collections Library) */ // TODO(benyu): benchmark and optimize all creation paths, which are a mess now @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxverideShim<E> - implements NavigableSet<E>, SortedIterable<E> { + implements SortedSet<E>, SortedIterable<E> { private static final Comparator<Comparable> NATURAL_ORDER = Ordering.natural(); @@ -182,7 +176,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride E e1, E e2, E e3, E e4, E e5) { return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); } - + /** * Returns an immutable sorted set containing the given elements sorted by * their natural ordering. When multiple elements are equivalent according to @@ -306,7 +300,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride // Unsafe, see ImmutableSortedSetFauxverideShim. @SuppressWarnings("unchecked") Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); - return copyOf(naturalOrder, elements); + return copyOfInternal(naturalOrder, elements); } /** @@ -320,7 +314,8 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride */ public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Iterator<? extends E> elements) { - return copyOf(comparator, Lists.newArrayList(elements)); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -339,19 +334,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Iterable<? extends E> elements) { checkNotNull(comparator); - boolean hasSameComparator = - SortedIterables.hasSameComparator(comparator, elements); - - if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { - @SuppressWarnings("unchecked") - ImmutableSortedSet<E> original = (ImmutableSortedSet<E>) elements; - if (!original.isPartialView()) { - return original; - } - } - @SuppressWarnings("unchecked") // elements only contains E's; it's safe. - E[] array = (E[]) Iterables.toArray(elements); - return construct(comparator, array.length, array); + return copyOfInternal(comparator, elements); } /** @@ -374,7 +357,8 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride */ public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Collection<? extends E> elements) { - return copyOf(comparator, (Iterable<? extends E>) elements); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -394,69 +378,41 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * @throws NullPointerException if {@code sortedSet} or any of its elements * is null */ + @SuppressWarnings("unchecked") public static <E> ImmutableSortedSet<E> copyOfSorted(SortedSet<E> sortedSet) { - Comparator<? super E> comparator = SortedIterables.comparator(sortedSet); - Object[] elements = sortedSet.toArray(); - if (elements.length == 0) { - return emptySet(comparator); - } else { - return new RegularImmutableSortedSet<E>( - ImmutableList.<E>asImmutableList(elements), comparator); + Comparator<? super E> comparator = sortedSet.comparator(); + if (comparator == null) { + comparator = (Comparator<? super E>) NATURAL_ORDER; } + return copyOfInternal(comparator, sortedSet); } - /** - * Sorts and eliminates duplicates from the first {@code n} positions in {@code contents}. - * Returns the number of unique elements. If this returns {@code k}, then the first {@code k} - * elements of {@code contents} will be the sorted, unique elements, and {@code - * contents[i] == null} for {@code k <= i < n}. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code contents} is - * null - */ - static <E> int sortAndUnique( - Comparator<? super E> comparator, int n, E... contents) { - if (n == 0) { - return 0; - } - for (int i = 0; i < n; i++) { - ObjectArrays.checkElementNotNull(contents[i], i); - } - Arrays.sort(contents, 0, n, comparator); - int uniques = 1; - for (int i = 1; i < n; i++) { - E cur = contents[i]; - E prev = contents[uniques - 1]; - if (comparator.compare(cur, prev) != 0) { - contents[uniques++] = cur; + private static <E> ImmutableSortedSet<E> copyOfInternal( + Comparator<? super E> comparator, Iterable<? extends E> elements) { + boolean hasSameComparator = + SortedIterables.hasSameComparator(comparator, elements); + + if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { + @SuppressWarnings("unchecked") + ImmutableSortedSet<E> original = (ImmutableSortedSet<E>) elements; + if (!original.isPartialView()) { + return original; } } - Arrays.fill(contents, uniques, n, null); - return uniques; + ImmutableList<E> list = ImmutableList.copyOf( + SortedIterables.sortedUnique(comparator, elements)); + return list.isEmpty() + ? ImmutableSortedSet.<E>emptySet(comparator) + : new RegularImmutableSortedSet<E>(list, comparator); } - /** - * Constructs an {@code ImmutableSortedSet} from the first {@code n} elements of - * {@code contents}. If {@code k} is the size of the returned {@code ImmutableSortedSet}, then - * the sorted unique elements are in the first {@code k} positions of {@code contents}, and - * {@code contents[i] == null} for {@code k <= i < n}. - * - * <p>If {@code k == contents.length}, then {@code contents} may no longer be safe for - * modification. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code contents} is - * null - */ - static <E> ImmutableSortedSet<E> construct( - Comparator<? super E> comparator, int n, E... contents) { - int uniques = sortAndUnique(comparator, n, contents); - if (uniques == 0) { - return emptySet(comparator); - } else if (uniques < contents.length) { - contents = ObjectArrays.arraysCopyOf(contents, uniques); - } - return new RegularImmutableSortedSet<E>( - ImmutableList.<E>asImmutableList(contents), comparator); + private static <E> ImmutableSortedSet<E> copyOfInternal( + Comparator<? super E> comparator, Iterator<? extends E> elements) { + ImmutableList<E> list = + ImmutableList.copyOf(SortedIterables.sortedUnique(comparator, elements)); + return list.isEmpty() + ? ImmutableSortedSet.<E>emptySet(comparator) + : new RegularImmutableSortedSet<E>(list, comparator); } /** @@ -474,8 +430,13 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride /** * Returns a builder that creates immutable sorted sets whose elements are * ordered by the reverse of their natural ordering. + * + * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather + * than {@code Comparable<? super E>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <E extends Comparable<?>> Builder<E> reverseOrder() { + public static <E extends Comparable<E>> Builder<E> reverseOrder() { return new Builder<E>(Ordering.natural().reverse()); } @@ -485,8 +446,13 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * Ordering#natural()} as the comparator. This method provides more * type-safety than {@link #builder}, as it can be called only for classes * that implement {@link Comparable}. + * + * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather + * than {@code Comparable<? super E>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <E extends Comparable<?>> Builder<E> naturalOrder() { + public static <E extends Comparable<E>> Builder<E> naturalOrder() { return new Builder<E>(Ordering.natural()); } @@ -577,11 +543,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * of the {@code Builder} and its comparator. */ @Override public ImmutableSortedSet<E> build() { - @SuppressWarnings("unchecked") // we're careful to put only E's in here - E[] contentsArray = (E[]) contents; - ImmutableSortedSet<E> result = construct(comparator, size, contentsArray); - this.size = result.size(); // we eliminated duplicates in-place in contentsArray - return result; + return copyOfInternal(comparator, contents.iterator()); } } @@ -636,12 +598,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return headSet(toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) { + ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) { return headSetImpl(checkNotNull(toElement), inclusive); } @@ -663,12 +620,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return subSet(fromElement, true, toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> subSet( + ImmutableSortedSet<E> subSet( E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { checkNotNull(fromElement); checkNotNull(toElement); @@ -692,12 +644,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return tailSet(fromElement, true); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) { + ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) { return tailSetImpl(checkNotNull(fromElement), inclusive); } @@ -713,110 +660,6 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride abstract ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive); /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E lower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E floor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E ceiling(E e) { - return Iterables.getFirst(tailSet(e, true), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E higher(E e) { - return Iterables.getFirst(tailSet(e, false), null); - } - - @Override - public E first() { - return iterator().next(); - } - - @Override - public E last() { - return descendingIterator().next(); - } - - /** - * Guaranteed to throw an exception and leave the set unmodified. - * - * @since 12.0 - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @GwtIncompatible("NavigableSet") - @Override - public final E pollFirst() { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the set unmodified. - * - * @since 12.0 - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @GwtIncompatible("NavigableSet") - @Override - public final E pollLast() { - throw new UnsupportedOperationException(); - } - - @GwtIncompatible("NavigableSet") - transient ImmutableSortedSet<E> descendingSet; - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> descendingSet() { - // racy single-check idiom - ImmutableSortedSet<E> result = descendingSet; - if (result == null) { - result = descendingSet = createDescendingSet(); - result.descendingSet = this; - } - return result; - } - - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<E> createDescendingSet() { - return new DescendingImmutableSortedSet<E>(this); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public abstract UnmodifiableIterator<E> descendingIterator(); - - /** * Returns the position of an element within the set, or -1 if not present. */ abstract int indexOf(@Nullable Object target); diff --git a/guava/src/com/google/common/collect/ImmutableTable.java b/guava/src/com/google/common/collect/ImmutableTable.java index 8296a83..debd49b 100644 --- a/guava/src/com/google/common/collect/ImmutableTable.java +++ b/guava/src/com/google/common/collect/ImmutableTable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Guava Authors + * 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. @@ -18,6 +18,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; @@ -34,13 +35,10 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this class are * guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) * @since 11.0 */ +@Beta @GwtCompatible // TODO(gak): make serializable public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { @@ -72,7 +70,7 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { */ public static final <R, C, V> ImmutableTable<R, C, V> copyOf( Table<? extends R, ? extends C, ? extends V> table) { - if (table instanceof ImmutableTable) { + if (table instanceof ImmutableTable<?, ?, ?>) { @SuppressWarnings("unchecked") ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; @@ -106,7 +104,7 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * Returns a new builder. The generated builder is equivalent to the builder - * created by the {@link Builder#ImmutableTable.Builder()} constructor. + * created by the {@link Builder#Builder()} constructor. */ public static final <R, C, V> Builder<R, C, V> builder() { return new Builder<R, C, V>(); @@ -256,8 +254,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * {@inheritDoc} * - * <p>The value {@code Map<R, V>} instances in the returned map are - * {@link ImmutableMap} instances as well. + * <p>The value {@code Map<R, V>}s in the returned map are + * {@link ImmutableMap}s as well. */ @Override public abstract ImmutableMap<C, Map<R, V>> columnMap(); @@ -273,8 +271,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * {@inheritDoc} * - * <p>The value {@code Map<C, V>} instances in the returned map are - * {@link ImmutableMap} instances as well. + * <p>The value {@code Map<C, V>}s in the returned map are + * {@link ImmutableMap}s as well. */ @Override public abstract ImmutableMap<R, Map<C, V>> rowMap(); @@ -282,9 +280,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { + @Override public final void clear() { throw new UnsupportedOperationException(); } @@ -292,9 +289,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V put(R rowKey, C columnKey, V value) { + @Override public final V put(R rowKey, C columnKey, V value) { throw new UnsupportedOperationException(); } @@ -302,9 +298,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void putAll( + @Override public final void putAll( Table<? extends R, ? extends C, ? extends V> table) { throw new UnsupportedOperationException(); } @@ -313,16 +308,15 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V remove(Object rowKey, Object columnKey) { + @Override public final V remove(Object rowKey, Object columnKey) { throw new UnsupportedOperationException(); } @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; return this.cellSet().equals(that.cellSet()); } else { diff --git a/guava/src/com/google/common/collect/Interners.java b/guava/src/com/google/common/collect/Interners.java index bd592d6..f5a6c85 100644 --- a/guava/src/com/google/common/collect/Interners.java +++ b/guava/src/com/google/common/collect/Interners.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.collect.MapMakerInternalMap.ReferenceEntry; @@ -51,24 +51,16 @@ public final class Interners { }; } - /** - * Returns a new thread-safe interner which retains a weak reference to each instance it has - * interned, and so does not prevent these instances from being garbage-collected. This most - * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative - * when the memory usage of that implementation is unacceptable. Note that unlike {@link - * String#intern}, using this interner does not consume memory in the permanent generation. - */ - @GwtIncompatible("java.lang.ref.WeakReference") - public static <E> Interner<E> newWeakInterner() { - return new WeakInterner<E>(); - } - - private static class WeakInterner<E> implements Interner<E> { + private static class CustomInterner<E> implements Interner<E> { // MapMaker is our friend, we know about this type - private final MapMakerInternalMap<E, Dummy> map = new MapMaker() - .weakKeys() - .keyEquivalence(Equivalence.equals()) + private final MapMakerInternalMap<E, Dummy> map; + + CustomInterner(GenericMapMaker<? super E, Object> mm) { + this.map = mm + .strongValues() + .keyEquivalence(Equivalences.equals()) .makeCustomMap(); + } @Override public E intern(E sample) { while (true) { @@ -100,6 +92,18 @@ public final class Interners { } /** + * Returns a new thread-safe interner which retains a weak reference to each instance it has + * interned, and so does not prevent these instances from being garbage-collected. This most + * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative + * when the memory usage of that implementation is unacceptable. Note that unlike {@link + * String#intern}, using this interner does not consume memory in the permanent generation. + */ + @GwtIncompatible("java.lang.ref.WeakReference") + public static <E> Interner<E> newWeakInterner() { + return new CustomInterner<E>(new MapMaker().weakKeys()); + } + + /** * Returns a function that delegates to the {@link Interner#intern} method of the given interner. * * @since 8.0 @@ -125,7 +129,7 @@ public final class Interners { } @Override public boolean equals(Object other) { - if (other instanceof InternerFunction) { + if (other instanceof InternerFunction<?>) { InternerFunction<?> that = (InternerFunction<?>) other; return interner.equals(that.interner); } diff --git a/guava/src/com/google/common/collect/Iterables.java b/guava/src/com/google/common/collect/Iterables.java index 39ad2f2..a92c2b2 100644 --- a/guava/src/com/google/common/collect/Iterables.java +++ b/guava/src/com/google/common/collect/Iterables.java @@ -23,6 +23,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; +import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; @@ -31,6 +32,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -50,10 +52,6 @@ import javax.annotation.Nullable; * produced in this class are <i>lazy</i>, which means that their iterators * only advance the backing iteration when absolutely necessary. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Iterables"> - * {@code Iterables}</a>. - * * @author Kevin Bourrillion * @author Jared Levy * @since 2.0 (imported from Google Collections Library) @@ -84,7 +82,7 @@ public final class Iterables { return checkNotNull(iterable); } - private static final class UnmodifiableIterable<T> extends FluentIterable<T> { + private static final class UnmodifiableIterable<T> implements Iterable<T> { private final Iterable<T> iterable; private UnmodifiableIterable(Iterable<T> iterable) { @@ -113,14 +111,20 @@ public final class Iterables { } /** - * Returns {@code true} if {@code iterable} contains any object for which {@code equals(element)} - * is true. + * Returns {@code true} if {@code iterable} contains {@code element}; that is, + * any object for which {@code equals(element)} is true. */ public static boolean contains(Iterable<?> iterable, @Nullable Object element) { if (iterable instanceof Collection) { Collection<?> collection = (Collection<?>) iterable; - return Collections2.safeContains(collection, element); + try { + return collection.contains(element); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } } return Iterators.contains(iterable.iterator(), element); } @@ -242,13 +246,6 @@ public final class Iterables { */ public static boolean elementsEqual( Iterable<?> iterable1, Iterable<?> iterable2) { - if (iterable1 instanceof Collection && iterable2 instanceof Collection) { - Collection<?> collection1 = (Collection<?>) iterable1; - Collection<?> collection2 = (Collection<?>) iterable2; - if (collection1.size() != collection2.size()) { - return false; - } - } return Iterators.elementsEqual(iterable1.iterator(), iterable2.iterator()); } @@ -278,9 +275,8 @@ public final class Iterables { * @throws IllegalArgumentException if the iterator contains multiple * elements */ - @Nullable public static <T> T getOnlyElement( - Iterable<? extends T> iterable, @Nullable T defaultValue) { + Iterable<T> iterable, @Nullable T defaultValue) { return Iterators.getOnlyElement(iterable.iterator(), defaultValue); } @@ -372,7 +368,7 @@ public final class Iterables { */ public static <T> Iterable<T> cycle(final Iterable<T> iterable) { checkNotNull(iterable); - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return Iterators.cycle(iterable); @@ -487,7 +483,7 @@ public final class Iterables { public static <T> Iterable<T> concat( final Iterable<? extends Iterable<? extends T>> inputs) { checkNotNull(inputs); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.concat(iterators(inputs)); @@ -538,7 +534,7 @@ public final class Iterables { final Iterable<T> iterable, final int size) { checkNotNull(iterable); checkArgument(size > 0); - return new FluentIterable<List<T>>() { + return new IterableWithToString<List<T>>() { @Override public Iterator<List<T>> iterator() { return Iterators.partition(iterable.iterator(), size); @@ -567,7 +563,7 @@ public final class Iterables { final Iterable<T> iterable, final int size) { checkNotNull(iterable); checkArgument(size > 0); - return new FluentIterable<List<T>>() { + return new IterableWithToString<List<T>>() { @Override public Iterator<List<T>> iterator() { return Iterators.paddedPartition(iterable.iterator(), size); @@ -583,7 +579,7 @@ public final class Iterables { final Iterable<T> unfiltered, final Predicate<? super T> predicate) { checkNotNull(unfiltered); checkNotNull(predicate); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.filter(unfiltered.iterator(), predicate); @@ -607,7 +603,7 @@ public final class Iterables { final Iterable<?> unfiltered, final Class<T> type) { checkNotNull(unfiltered); checkNotNull(type); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.filter(unfiltered.iterator(), type); @@ -616,7 +612,8 @@ public final class Iterables { } /** - * Returns {@code true} if any element in {@code iterable} satisfies the predicate. + * Returns {@code true} if one or more elements in {@code iterable} satisfy + * the predicate. */ public static <T> boolean any( Iterable<T> iterable, Predicate<? super T> predicate) { @@ -635,8 +632,8 @@ public final class Iterables { /** * Returns the first element in {@code iterable} that satisfies the given * predicate; use this method only when such an element is known to exist. If - * it is possible that <i>no</i> element will match, use {@link #tryFind} or - * {@link #find(Iterable, Predicate, Object)} instead. + * it is possible that <i>no</i> element will match, use {@link + * #tryFind)} or {@link #find(Iterable, Predicate, T)} instead. * * @throws NoSuchElementException if no element in {@code iterable} matches * the given predicate @@ -654,8 +651,7 @@ public final class Iterables { * * @since 7.0 */ - @Nullable - public static <T> T find(Iterable<? extends T> iterable, + public static <T> T find(Iterable<T> iterable, Predicate<? super T> predicate, @Nullable T defaultValue) { return Iterators.find(iterable.iterator(), predicate, defaultValue); } @@ -707,7 +703,7 @@ public final class Iterables { final Function<? super F, ? extends T> function) { checkNotNull(fromIterable); checkNotNull(function); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.transform(fromIterable.iterator(), function); @@ -760,8 +756,8 @@ public final class Iterables { * @throws IndexOutOfBoundsException if {@code position} is negative * @since 4.0 */ - @Nullable - public static <T> T get(Iterable<? extends T> iterable, int position, @Nullable T defaultValue) { + public static <T> T get(Iterable<T> iterable, int position, + @Nullable T defaultValue) { checkNotNull(iterable); checkNonnegativeIndex(position); @@ -777,16 +773,11 @@ public final class Iterables { * the iterable is empty. The {@link Iterators} analog to this method is * {@link Iterators#getNext}. * - * <p>If no default value is desired (and the caller instead wants a - * {@link NoSuchElementException} to be thrown), it is recommended that - * {@code iterable.iterator().next()} is used instead. - * * @param defaultValue the default value to return if the iterable is empty * @return the first element of {@code iterable} or the default value * @since 7.0 */ - @Nullable - public static <T> T getFirst(Iterable<? extends T> iterable, @Nullable T defaultValue) { + public static <T> T getFirst(Iterable<T> iterable, @Nullable T defaultValue) { return Iterators.getNext(iterable.iterator(), defaultValue); } @@ -827,17 +818,16 @@ public final class Iterables { * @return the last element of {@code iterable} or the default value * @since 3.0 */ - @Nullable - public static <T> T getLast(Iterable<? extends T> iterable, @Nullable T defaultValue) { + public static <T> T getLast(Iterable<T> iterable, @Nullable T defaultValue) { if (iterable instanceof Collection) { - Collection<? extends T> collection = Collections2.cast(iterable); + Collection<T> collection = (Collection<T>) iterable; if (collection.isEmpty()) { return defaultValue; } } if (iterable instanceof List) { - List<? extends T> list = Lists.cast(iterable); + List<T> list = (List<T>) iterable; return getLastInNonemptyList(list); } @@ -847,7 +837,7 @@ public final class Iterables { * call this method. */ if (iterable instanceof SortedSet) { - SortedSet<? extends T> sortedSet = Sets.cast(iterable); + SortedSet<T> sortedSet = (SortedSet<T>) iterable; return sortedSet.last(); } @@ -885,7 +875,7 @@ public final class Iterables { if (iterable instanceof List) { final List<T> list = (List<T>) iterable; - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { // TODO(kevinb): Support a concurrently modified collection? @@ -896,12 +886,12 @@ public final class Iterables { }; } - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { final Iterator<T> iterator = iterable.iterator(); - Iterators.advance(iterator, numberToSkip); + Iterators.skip(iterator, numberToSkip); /* * We can't just return the iterator because an immediate call to its @@ -957,7 +947,7 @@ public final class Iterables { final Iterable<T> iterable, final int limitSize) { checkNotNull(iterable); checkArgument(limitSize >= 0, "limit is negative"); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.limit(iterable.iterator(), limitSize); @@ -986,7 +976,7 @@ public final class Iterables { */ public static <T> Iterable<T> consumingIterable(final Iterable<T> iterable) { if (iterable instanceof Queue) { - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return new ConsumingQueueIterator<T>((Queue<T>) iterable); @@ -996,7 +986,7 @@ public final class Iterables { checkNotNull(iterable); - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return Iterators.consumingIterator(iterable.iterator()); @@ -1023,6 +1013,30 @@ public final class Iterables { // Methods only in Iterables, not in Iterators /** + * Adapts a list to an iterable with reversed iteration order. It is + * especially useful in foreach-style loops: <pre> {@code + * + * List<String> mylist = ... + * for (String str : Iterables.reverse(mylist)) { + * ... + * }}</pre> + * + * There is no corresponding method in {@link Iterators}, since {@link + * Iterable#iterator} can simply be invoked on the result of calling this + * method. + * + * @return an iterable with the same elements as the list, in reverse + * + * @deprecated use {@link Lists#reverse(List)} or {@link + * ImmutableList#reverse()}. <b>This method is scheduled for deletion in + * July 2012.</b> + */ + @Deprecated + public static <T> Iterable<T> reverse(final List<T> list) { + return Lists.reverse(list); + } + + /** * Determines if the given iterable contains no elements. * * <p>There is no precise {@link Iterator} equivalent to this method, since @@ -1038,6 +1052,43 @@ public final class Iterables { return !iterable.iterator().hasNext(); } + // Non-public + + /** + * Removes the specified element from the specified iterable. + * + * <p>This method iterates over the iterable, checking each element returned + * by the iterator in turn to see if it equals the object {@code o}. If they + * are equal, it is removed from the iterable with the iterator's + * {@code remove} method. At most one element is removed, even if the iterable + * contains multiple members that equal {@code o}. + * + * <p><b>Warning:</b> Do not use this method for a collection, such as a + * {@link HashSet}, that has a fast {@code remove} method. + * + * @param iterable the iterable from which to remove + * @param o an element to remove from the collection + * @return {@code true} if the iterable changed as a result + * @throws UnsupportedOperationException if the iterator does not support the + * {@code remove} method and the iterable contains the object + */ + static boolean remove(Iterable<?> iterable, @Nullable Object o) { + Iterator<?> i = iterable.iterator(); + while (i.hasNext()) { + if (Objects.equal(i.next(), o)) { + i.remove(); + return true; + } + } + return false; + } + + abstract static class IterableWithToString<E> implements Iterable<E> { + @Override public String toString() { + return Iterables.toString(this); + } + } + /** * Returns an iterable over the merged contents of all given * {@code iterables}. Equivalent entries will not be de-duplicated. @@ -1056,7 +1107,7 @@ public final class Iterables { final Comparator<? super T> comparator) { checkNotNull(iterables, "iterables"); checkNotNull(comparator, "comparator"); - Iterable<T> iterable = new FluentIterable<T>() { + Iterable<T> iterable = new Iterable<T>() { @Override public Iterator<T> iterator() { return Iterators.mergeSorted( diff --git a/guava/src/com/google/common/collect/Iterators.java b/guava/src/com/google/common/collect/Iterators.java index 39e13c3..847d1dd 100644 --- a/guava/src/com/google/common/collect/Iterators.java +++ b/guava/src/com/google/common/collect/Iterators.java @@ -24,7 +24,6 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; -import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -38,7 +37,6 @@ import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; @@ -54,10 +52,6 @@ import javax.annotation.Nullable; * produced in this class are <i>lazy</i>, which means that they only advance * the backing iteration when absolutely necessary. * - * <p>See the Guava User Guide section on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Iterables"> - * {@code Iterators}</a>. - * * @author Kevin Bourrillion * @author Jared Levy * @since 2.0 (imported from Google Collections Library) @@ -66,8 +60,8 @@ import javax.annotation.Nullable; public final class Iterators { private Iterators() {} - static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR - = new UnmodifiableListIterator<Object>() { + static final UnmodifiableIterator<Object> EMPTY_ITERATOR + = new UnmodifiableIterator<Object>() { @Override public boolean hasNext() { return false; @@ -76,22 +70,6 @@ public final class Iterators { public Object next() { throw new NoSuchElementException(); } - @Override - public boolean hasPrevious() { - return false; - } - @Override - public Object previous() { - throw new NoSuchElementException(); - } - @Override - public int nextIndex() { - return 0; - } - @Override - public int previousIndex() { - return -1; - } }; /** @@ -100,20 +78,10 @@ public final class Iterators { * <p>The {@link Iterable} equivalent of this method is {@link * ImmutableSet#of()}. */ - public static <T> UnmodifiableIterator<T> emptyIterator() { - return emptyListIterator(); - } - - /** - * Returns the empty iterator. - * - * <p>The {@link Iterable} equivalent of this method is {@link - * ImmutableSet#of()}. - */ // Casting to any type is safe since there are no actual elements. @SuppressWarnings("unchecked") - static <T> UnmodifiableListIterator<T> emptyListIterator() { - return (UnmodifiableListIterator<T>) EMPTY_LIST_ITERATOR; + public static <T> UnmodifiableIterator<T> emptyIterator() { + return (UnmodifiableIterator<T>) EMPTY_ITERATOR; } private static final Iterator<Object> EMPTY_MODIFIABLE_ITERATOR = @@ -307,11 +275,15 @@ public final class Iterators { * {@code hasNext()} method will return {@code false}. */ public static String toString(Iterator<?> iterator) { - return Joiner.on(", ") - .useForNull("null") - .appendTo(new StringBuilder().append('['), iterator) - .append(']') - .toString(); + if (!iterator.hasNext()) { + return "[]"; + } + StringBuilder builder = new StringBuilder(); + builder.append('[').append(iterator.next()); + while (iterator.hasNext()) { + builder.append(", ").append(iterator.next()); + } + return builder.append(']').toString(); } /** @@ -347,8 +319,8 @@ public final class Iterators { * @throws IllegalArgumentException if the iterator contains multiple * elements. The state of the iterator is unspecified. */ - @Nullable - public static <T> T getOnlyElement(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getOnlyElement( + Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue; } @@ -459,8 +431,8 @@ public final class Iterators { /** * Returns an iterator that cycles indefinitely over the provided elements. * - * <p>The returned iterator supports {@code remove()}. After {@code remove()} - * is called, subsequent cycles omit the removed + * <p>The returned iterator supports {@code remove()} if the provided iterator + * does. After {@code remove()} is called, subsequent cycles omit the removed * element, but {@code elements} does not change. The iterator's * {@code hasNext()} method returns {@code true} until all of the original * elements have been removed. @@ -480,11 +452,6 @@ public final class Iterators { * * <p>The returned iterator supports {@code remove()} when the corresponding * input iterator supports it. - * - * <p><b>Note:</b> the current implementation is not suitable for nested - * concatenated iterators, i.e. the following should be avoided when in a loop: - * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the - * resulting iterator has a cubic complexity to the depth of the nesting. */ @SuppressWarnings("unchecked") public static <T> Iterator<T> concat(Iterator<? extends T> a, @@ -502,11 +469,6 @@ public final class Iterators { * * <p>The returned iterator supports {@code remove()} when the corresponding * input iterator supports it. - * - * <p><b>Note:</b> the current implementation is not suitable for nested - * concatenated iterators, i.e. the following should be avoided when in a loop: - * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the - * resulting iterator has a cubic complexity to the depth of the nesting. */ @SuppressWarnings("unchecked") public static <T> Iterator<T> concat(Iterator<? extends T> a, @@ -525,11 +487,6 @@ public final class Iterators { * * <p>The returned iterator supports {@code remove()} when the corresponding * input iterator supports it. - * - * <p><b>Note:</b> the current implementation is not suitable for nested - * concatenated iterators, i.e. the following should be avoided when in a loop: - * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the - * resulting iterator has a cubic complexity to the depth of the nesting. */ @SuppressWarnings("unchecked") public static <T> Iterator<T> concat(Iterator<? extends T> a, @@ -550,11 +507,6 @@ public final class Iterators { * <p>The returned iterator supports {@code remove()} when the corresponding * input iterator supports it. * - * <p><b>Note:</b> the current implementation is not suitable for nested - * concatenated iterators, i.e. the following should be avoided when in a loop: - * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the - * resulting iterator has a cubic complexity to the depth of the nesting. - * * @throws NullPointerException if any of the provided iterators is null */ public static <T> Iterator<T> concat(Iterator<? extends T>... inputs) { @@ -569,11 +521,6 @@ public final class Iterators { * <p>The returned iterator supports {@code remove()} when the corresponding * input iterator supports it. The methods of the returned iterator may throw * {@code NullPointerException} if any of the input iterators is null. - * - * <p><b>Note:</b> the current implementation is not suitable for nested - * concatenated iterators, i.e. the following should be avoided when in a loop: - * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the - * resulting iterator has a cubic complexity to the depth of the nesting. */ public static <T> Iterator<T> concat( final Iterator<? extends Iterator<? extends T>> inputs) { @@ -764,8 +711,8 @@ public final class Iterators { * predicate; use this method only when such an element is known to exist. If * no such element is found, the iterator will be left exhausted: its {@code * hasNext()} method will return {@code false}. If it is possible that - * <i>no</i> element will match, use {@link #tryFind} or {@link - * #find(Iterator, Predicate, Object)} instead. + * <i>no</i> element will match, use {@link #tryFind)} or {@link + * #find(Iterator, Predicate, T)} instead. * * @throws NoSuchElementException if no element in {@code iterator} matches * the given predicate @@ -785,10 +732,9 @@ public final class Iterators { * * @since 7.0 */ - @Nullable - public static <T> T find(Iterator<? extends T> iterator, Predicate<? super T> predicate, + public static <T> T find(Iterator<T> iterator, Predicate<? super T> predicate, @Nullable T defaultValue) { - UnmodifiableIterator<? extends T> filteredIterator = filter(iterator, predicate); + UnmodifiableIterator<T> filteredIterator = filter(iterator, predicate); return filteredIterator.hasNext() ? filteredIterator.next() : defaultValue; } @@ -853,12 +799,22 @@ public final class Iterators { */ public static <F, T> Iterator<T> transform(final Iterator<F> fromIterator, final Function<? super F, ? extends T> function) { + checkNotNull(fromIterator); checkNotNull(function); - return new TransformedIterator<F, T>(fromIterator) { + return new Iterator<T>() { + @Override + public boolean hasNext() { + return fromIterator.hasNext(); + } @Override - T transform(F from) { + public T next() { + F from = fromIterator.next(); return function.apply(from); } + @Override + public void remove() { + fromIterator.remove(); + } }; } @@ -910,8 +866,8 @@ public final class Iterators { * @throws IndexOutOfBoundsException if {@code position} is negative * @since 4.0 */ - @Nullable - public static <T> T get(Iterator<? extends T> iterator, int position, @Nullable T defaultValue) { + public static <T> T get(Iterator<T> iterator, int position, + @Nullable T defaultValue) { checkNonnegative(position); try { @@ -930,8 +886,7 @@ public final class Iterators { * @return the next element of {@code iterator} or the default value * @since 7.0 */ - @Nullable - public static <T> T getNext(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getNext(Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? iterator.next() : defaultValue; } @@ -958,24 +913,24 @@ public final class Iterators { * @return the last element of {@code iterator} * @since 3.0 */ - @Nullable - public static <T> T getLast(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getLast(Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? getLast(iterator) : defaultValue; } /** - * Calls {@code next()} on {@code iterator}, either {@code numberToAdvance} times + * Calls {@code next()} on {@code iterator}, either {@code numberToSkip} times * or until {@code hasNext()} returns {@code false}, whichever comes first. * - * @return the number of elements the iterator was advanced - * @since 13.0 (since 3.0 as {@code Iterators.skip}) + * @return the number of elements skipped + * @since 3.0 */ - public static int advance(Iterator<?> iterator, int numberToAdvance) { + @Beta + public static <T> int skip(Iterator<T> iterator, int numberToSkip) { checkNotNull(iterator); - checkArgument(numberToAdvance >= 0, "number to advance cannot be negative"); + checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); int i; - for (i = 0; i < numberToAdvance && iterator.hasNext(); i++) { + for (i = 0; i < numberToSkip && iterator.hasNext(); i++) { iterator.next(); } return i; @@ -1051,21 +1006,6 @@ public final class Iterators { }; } - /** - * Deletes and returns the next value from the iterator, or returns - * {@code defaultValue} if there is no such value. - */ - @Nullable - static <T> T pollNext(Iterator<T> iterator) { - if (iterator.hasNext()) { - T result = iterator.next(); - iterator.remove(); - return result; - } else { - return null; - } - } - // Methods only in Iterators, not in Iterables /** @@ -1103,14 +1043,21 @@ public final class Iterators { } /** - * Returns a list iterator containing the elements in the specified range of - * {@code array} in order, starting at the specified index. + * Returns an iterator containing the elements in the specified range of + * {@code array} in order. The returned iterator is a view of the array; + * subsequent changes to the array will be reflected in the iterator. * * <p>The {@code Iterable} equivalent of this method is {@code - * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + * Arrays.asList(array).subList(offset, offset + length)}. + * + * @param array array to read elements out of + * @param offset index of first array element to retrieve + * @param length number of elements in iteration + * @throws IndexOutOfBoundsException if {@code offset} is negative, {@code + * length} is negative, or {@code offset + length > array.length} */ - static <T> UnmodifiableListIterator<T> forArray( - final T[] array, final int offset, int length, int index) { + static <T> UnmodifiableIterator<T> forArray( + final T[] array, final int offset, int length) { checkArgument(length >= 0); int end = offset + length; @@ -1122,7 +1069,7 @@ public final class Iterators { * because the returned Iterator is a ListIterator that may be moved back * past the beginning of the iteration. */ - return new AbstractIndexedListIterator<T>(length, index) { + return new AbstractIndexedListIterator<T>(length) { @Override protected T get(int index) { return array[offset + index]; } @@ -1379,19 +1326,4 @@ public final class Iterators { return next; } } - - /** - * Precondition tester for {@code Iterator.remove()} that throws an exception with a consistent - * error message. - */ - static void checkRemove(boolean canRemove) { - checkState(canRemove, "no calls to next() since the last call to remove()"); - } - - /** - * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 - */ - static <T> ListIterator<T> cast(Iterator<T> iterator) { - return (ListIterator<T>) iterator; - } } diff --git a/guava/src/com/google/common/collect/LinkedHashMultimap.java b/guava/src/com/google/common/collect/LinkedHashMultimap.java index 47bbe6f..39c0bfb 100644 --- a/guava/src/com/google/common/collect/LinkedHashMultimap.java +++ b/guava/src/com/google/common/collect/LinkedHashMultimap.java @@ -16,24 +16,20 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkArgument; - import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.Arrays; import java.util.Collection; -import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.Nullable; @@ -69,23 +65,29 @@ import javax.annotation.Nullable; * update operations, wrap your multimap with a call to {@link * Multimaps#synchronizedSetMultimap}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. - * * @author Jared Levy - * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(serializable = true, emulated = true) public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { + private static final int DEFAULT_VALUES_PER_KEY = 8; + + @VisibleForTesting + transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + + /** + * Map entries with an iteration order corresponding to the order in which the + * key-value pairs were added to the multimap. + */ + // package-private for GWT deserialization + transient Collection<Map.Entry<K, V>> linkedEntries; /** * Creates a new, empty {@code LinkedHashMultimap} with the default initial * capacities. */ public static <K, V> LinkedHashMultimap<K, V> create() { - return new LinkedHashMultimap<K, V>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); + return new LinkedHashMultimap<K, V>(); } /** @@ -99,9 +101,7 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { */ public static <K, V> LinkedHashMultimap<K, V> create( int expectedKeys, int expectedValuesPerKey) { - return new LinkedHashMultimap<K, V>( - Maps.capacity(expectedKeys), - Maps.capacity(expectedValuesPerKey)); + return new LinkedHashMultimap<K, V>(expectedKeys, expectedValuesPerKey); } /** @@ -115,158 +115,201 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { */ public static <K, V> LinkedHashMultimap<K, V> create( Multimap<? extends K, ? extends V> multimap) { - LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); - result.putAll(multimap); - return result; - } - - private interface ValueSetLink<K, V> { - ValueSetLink<K, V> getPredecessorInValueSet(); - ValueSetLink<K, V> getSuccessorInValueSet(); - - void setPredecessorInValueSet(ValueSetLink<K, V> entry); - void setSuccessorInValueSet(ValueSetLink<K, V> entry); + return new LinkedHashMultimap<K, V>(multimap); } - private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) { - pred.setSuccessorInValueSet(succ); - succ.setPredecessorInValueSet(pred); + private LinkedHashMultimap() { + super(new LinkedHashMap<K, Collection<V>>()); + linkedEntries = Sets.newLinkedHashSet(); } - private static <K, V> void succeedsInMultimap( - ValueEntry<K, V> pred, ValueEntry<K, V> succ) { - pred.setSuccessorInMultimap(succ); - succ.setPredecessorInMultimap(pred); + private LinkedHashMultimap(int expectedKeys, int expectedValuesPerKey) { + super(new LinkedHashMap<K, Collection<V>>(expectedKeys)); + Preconditions.checkArgument(expectedValuesPerKey >= 0); + this.expectedValuesPerKey = expectedValuesPerKey; + linkedEntries = new LinkedHashSet<Map.Entry<K, V>>( + (int) Math.min(Ints.MAX_POWER_OF_TWO, + ((long) expectedKeys) * expectedValuesPerKey)); } - private static <K, V> void deleteFromValueSet(ValueSetLink<K, V> entry) { - succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); + private LinkedHashMultimap(Multimap<? extends K, ? extends V> multimap) { + super(new LinkedHashMap<K, Collection<V>>( + Maps.capacity(multimap.keySet().size()))); + linkedEntries + = new LinkedHashSet<Map.Entry<K, V>>(Maps.capacity(multimap.size())); + putAll(multimap); } - private static <K, V> void deleteFromMultimap(ValueEntry<K, V> entry) { - succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); + /** + * {@inheritDoc} + * + * <p>Creates an empty {@code LinkedHashSet} for a collection of values for + * one key. + * + * @return a new {@code LinkedHashSet} containing a collection of values for + * one key + */ + @Override Set<V> createCollection() { + return new LinkedHashSet<V>(Maps.capacity(expectedValuesPerKey)); } /** - * LinkedHashMultimap entries are in no less than three coexisting linked lists: - * a row in the hash table for a Set<V> associated with a key, the linked list - * of insertion-ordered entries in that Set<V>, and the linked list of entries - * in the LinkedHashMultimap as a whole. + * {@inheritDoc} + * + * <p>Creates a decorated {@code LinkedHashSet} that also keeps track of the + * order in which key-value pairs are added to the multimap. + * + * @param key key to associate with values in the collection + * @return a new decorated {@code LinkedHashSet} containing a collection of + * values for one key */ - @VisibleForTesting - static final class ValueEntry<K, V> extends AbstractMapEntry<K, V> - implements ValueSetLink<K, V> { - final K key; - final V value; - final int valueHash; - - @Nullable ValueEntry<K, V> nextInValueSetHashRow; - - ValueSetLink<K, V> predecessorInValueSet; - ValueSetLink<K, V> successorInValueSet; + @Override Collection<V> createCollection(@Nullable K key) { + return new SetDecorator(key, createCollection()); + } - ValueEntry<K, V> predecessorInMultimap; - ValueEntry<K, V> successorInMultimap; + private class SetDecorator extends ForwardingSet<V> { + final Set<V> delegate; + final K key; - ValueEntry(@Nullable K key, @Nullable V value, int valueHash, - @Nullable ValueEntry<K, V> nextInValueSetHashRow) { + SetDecorator(@Nullable K key, Set<V> delegate) { + this.delegate = delegate; this.key = key; - this.value = value; - this.valueHash = valueHash; - this.nextInValueSetHashRow = nextInValueSetHashRow; } - @Override - public K getKey() { - return key; + @Override protected Set<V> delegate() { + return delegate; } - @Override - public V getValue() { - return value; + <E> Map.Entry<K, E> createEntry(@Nullable E value) { + return Maps.immutableEntry(key, value); } - @Override - public ValueSetLink<K, V> getPredecessorInValueSet() { - return predecessorInValueSet; + <E> Collection<Map.Entry<K, E>> createEntries(Collection<E> values) { + // converts a collection of values into a list of key/value map entries + Collection<Map.Entry<K, E>> entries + = Lists.newArrayListWithExpectedSize(values.size()); + for (E value : values) { + entries.add(createEntry(value)); + } + return entries; } - @Override - public ValueSetLink<K, V> getSuccessorInValueSet() { - return successorInValueSet; + @Override public boolean add(@Nullable V value) { + boolean changed = delegate.add(value); + if (changed) { + linkedEntries.add(createEntry(value)); + } + return changed; } - @Override - public void setPredecessorInValueSet(ValueSetLink<K, V> entry) { - predecessorInValueSet = entry; + @Override public boolean addAll(Collection<? extends V> values) { + boolean changed = delegate.addAll(values); + if (changed) { + linkedEntries.addAll(createEntries(delegate())); + } + return changed; } - @Override - public void setSuccessorInValueSet(ValueSetLink<K, V> entry) { - successorInValueSet = entry; + @Override public void clear() { + for (V value : delegate) { + linkedEntries.remove(createEntry(value)); + } + delegate.clear(); } - public ValueEntry<K, V> getPredecessorInMultimap() { - return predecessorInMultimap; - } + @Override public Iterator<V> iterator() { + final Iterator<V> delegateIterator = delegate.iterator(); + return new Iterator<V>() { + V value; - public ValueEntry<K, V> getSuccessorInMultimap() { - return successorInMultimap; + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + @Override + public V next() { + value = delegateIterator.next(); + return value; + } + @Override + public void remove() { + delegateIterator.remove(); + linkedEntries.remove(createEntry(value)); + } + }; } - public void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) { - this.successorInMultimap = multimapSuccessor; + @Override public boolean remove(@Nullable Object value) { + boolean changed = delegate.remove(value); + if (changed) { + /* + * linkedEntries.remove() will return false when this method is called + * by entries().iterator().remove() + */ + linkedEntries.remove(createEntry(value)); + } + return changed; } - public void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) { - this.predecessorInMultimap = multimapPredecessor; + @Override public boolean removeAll(Collection<?> values) { + boolean changed = delegate.removeAll(values); + if (changed) { + linkedEntries.removeAll(createEntries(values)); + } + return changed; } - } - - private static final int DEFAULT_KEY_CAPACITY = 16; - private static final int DEFAULT_VALUE_SET_CAPACITY = 2; - @VisibleForTesting static final double VALUE_SET_LOAD_FACTOR = 1.0; - @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; - private transient ValueEntry<K, V> multimapHeaderEntry; - - private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { - super(new LinkedHashMap<K, Collection<V>>(keyCapacity)); - - checkArgument(valueSetCapacity >= 0, - "expectedValuesPerKey must be >= 0 but was %s", valueSetCapacity); - - this.valueSetCapacity = valueSetCapacity; - this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + @Override public boolean retainAll(Collection<?> values) { + /* + * Calling linkedEntries.retainAll() would incorrectly remove values + * with other keys. + */ + boolean changed = false; + Iterator<V> iterator = delegate.iterator(); + while (iterator.hasNext()) { + V value = iterator.next(); + if (!values.contains(value)) { + iterator.remove(); + linkedEntries.remove(Maps.immutableEntry(key, value)); + changed = true; + } + } + return changed; + } } /** * {@inheritDoc} * - * <p>Creates an empty {@code LinkedHashSet} for a collection of values for - * one key. + * <p>Generates an iterator across map entries that follows the ordering in + * which the key-value pairs were added to the multimap. * - * @return a new {@code LinkedHashSet} containing a collection of values for - * one key + * @return a key-value iterator with the correct ordering */ - @Override - Set<V> createCollection() { - return new LinkedHashSet<V>(valueSetCapacity); - } + @Override Iterator<Map.Entry<K, V>> createEntryIterator() { + final Iterator<Map.Entry<K, V>> delegateIterator = linkedEntries.iterator(); - /** - * {@inheritDoc} - * - * <p>Creates a decorated insertion-ordered set that also keeps track of the - * order in which key-value pairs are added to the multimap. - * - * @param key key to associate with values in the collection - * @return a new decorated set containing a collection of values for one key - */ - @Override - Collection<V> createCollection(K key) { - return new ValueSet(key, valueSetCapacity); + return new Iterator<Map.Entry<K, V>>() { + Map.Entry<K, V> entry; + + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + + @Override + public Map.Entry<K, V> next() { + entry = delegateIterator.next(); + return entry; + } + + @Override + public void remove() { + // Remove from iterator first to keep iterator valid. + delegateIterator.remove(); + LinkedHashMultimap.this.remove(entry.getKey(), entry.getValue()); + } + }; } /** @@ -277,8 +320,8 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { * However, the provided values always come last in the {@link #entries()} and * {@link #values()} iteration orderings. */ - @Override - public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { + @Override public Set<V> replaceValues( + @Nullable K key, Iterable<? extends V> values) { return super.replaceValues(key, values); } @@ -309,265 +352,20 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { return super.values(); } - @VisibleForTesting - final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> { - /* - * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory - * consumption. - */ - - private final K key; - @VisibleForTesting ValueEntry<K, V>[] hashTable; - private int size = 0; - private int modCount = 0; - - // We use the set object itself as the end of the linked list, avoiding an unnecessary - // entry object per key. - private ValueSetLink<K, V> firstEntry; - private ValueSetLink<K, V> lastEntry; - - ValueSet(K key, int expectedValues) { - this.key = key; - this.firstEntry = this; - this.lastEntry = this; - // Round expected values up to a power of 2 to get the table size. - int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR); - - @SuppressWarnings("unchecked") - ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize]; - this.hashTable = hashTable; - } - - @Override - public ValueSetLink<K, V> getPredecessorInValueSet() { - return lastEntry; - } - - @Override - public ValueSetLink<K, V> getSuccessorInValueSet() { - return firstEntry; - } - - @Override - public void setPredecessorInValueSet(ValueSetLink<K, V> entry) { - lastEntry = entry; - } - - @Override - public void setSuccessorInValueSet(ValueSetLink<K, V> entry) { - firstEntry = entry; - } - - @Override - public Iterator<V> iterator() { - return new Iterator<V>() { - ValueSetLink<K, V> nextEntry = firstEntry; - ValueEntry<K, V> toRemove; - int expectedModCount = modCount; - - private void checkForComodification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForComodification(); - return nextEntry != ValueSet.this; - } - - @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry; - V result = entry.getValue(); - toRemove = entry; - nextEntry = entry.getSuccessorInValueSet(); - return result; - } - - @Override - public void remove() { - checkForComodification(); - Iterators.checkRemove(toRemove != null); - Object o = toRemove.getValue(); - int hash = (o == null) ? 0 : o.hashCode(); - int row = Hashing.smear(hash) & (hashTable.length - 1); - ValueEntry<K, V> prev = null; - for (ValueEntry<K, V> entry = hashTable[row]; entry != null; - prev = entry, entry = entry.nextInValueSetHashRow) { - if (entry == toRemove) { - if (prev == null) { - // first entry in row - hashTable[row] = entry.nextInValueSetHashRow; - } else { - prev.nextInValueSetHashRow = entry.nextInValueSetHashRow; - } - deleteFromValueSet(toRemove); - deleteFromMultimap(toRemove); - size--; - expectedModCount = ++modCount; - break; - } - } - toRemove = null; - } - }; - } - - @Override - public int size() { - return size; - } - - @Override - public boolean contains(@Nullable Object o) { - int hash = (o == null) ? 0 : o.hashCode(); - int row = Hashing.smear(hash) & (hashTable.length - 1); - - for (ValueEntry<K, V> entry = hashTable[row]; entry != null; - entry = entry.nextInValueSetHashRow) { - if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) { - return true; - } - } - return false; - } - - @Override - public boolean add(@Nullable V value) { - int hash = (value == null) ? 0 : value.hashCode(); - int row = Hashing.smear(hash) & (hashTable.length - 1); - - ValueEntry<K, V> rowHead = hashTable[row]; - for (ValueEntry<K, V> entry = rowHead; entry != null; - entry = entry.nextInValueSetHashRow) { - if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) { - return false; - } - } - - ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead); - succeedsInValueSet(lastEntry, newEntry); - succeedsInValueSet(newEntry, this); - succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry); - succeedsInMultimap(newEntry, multimapHeaderEntry); - hashTable[row] = newEntry; - size++; - modCount++; - rehashIfNecessary(); - return true; - } - - private void rehashIfNecessary() { - if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { - @SuppressWarnings("unchecked") - ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2]; - this.hashTable = hashTable; - int mask = hashTable.length - 1; - for (ValueSetLink<K, V> entry = firstEntry; - entry != this; entry = entry.getSuccessorInValueSet()) { - ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry; - int row = Hashing.smear(valueEntry.valueHash) & mask; - valueEntry.nextInValueSetHashRow = hashTable[row]; - hashTable[row] = valueEntry; - } - } - } - - @Override - public boolean remove(@Nullable Object o) { - int hash = (o == null) ? 0 : o.hashCode(); - int row = Hashing.smear(hash) & (hashTable.length - 1); - - ValueEntry<K, V> prev = null; - for (ValueEntry<K, V> entry = hashTable[row]; entry != null; - prev = entry, entry = entry.nextInValueSetHashRow) { - if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) { - if (prev == null) { - // first entry in the row - hashTable[row] = entry.nextInValueSetHashRow; - } else { - prev.nextInValueSetHashRow = entry.nextInValueSetHashRow; - } - deleteFromValueSet(entry); - deleteFromMultimap(entry); - size--; - modCount++; - return true; - } - } - return false; - } - - @Override - public void clear() { - Arrays.fill(hashTable, null); - size = 0; - for (ValueSetLink<K, V> entry = firstEntry; - entry != this; entry = entry.getSuccessorInValueSet()) { - ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry; - deleteFromMultimap(valueEntry); - } - succeedsInValueSet(this, this); - modCount++; - } - } - - @Override - Iterator<Map.Entry<K, V>> entryIterator() { - return new Iterator<Map.Entry<K, V>>() { - ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap; - ValueEntry<K, V> toRemove; - - @Override - public boolean hasNext() { - return nextEntry != multimapHeaderEntry; - } - - @Override - public Map.Entry<K, V> next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - ValueEntry<K, V> result = nextEntry; - toRemove = result; - nextEntry = nextEntry.successorInMultimap; - return result; - } - - @Override - public void remove() { - Iterators.checkRemove(toRemove != null); - LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue()); - toRemove = null; - } - }; - } - - @Override - public void clear() { - super.clear(); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); - } + // Unfortunately, the entries() ordering does not determine the key ordering; + // see the example in the LinkedListMultimap class Javadoc. /** - * @serialData the expected values per key, the number of distinct keys, - * the number of entries, and the entries in order + * @serialData the number of distinct keys, and then for each distinct key: + * the first key, the number of values for that key, and the key's values, + * followed by successive keys and values from the entries() ordering */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); - stream.writeInt(valueSetCapacity); - stream.writeInt(keySet().size()); - for (K key : keySet()) { - stream.writeObject(key); - } - stream.writeInt(size()); - for (Map.Entry<K, V> entry : entries()) { + stream.writeInt(expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + for (Map.Entry<K, V> entry : linkedEntries) { stream.writeObject(entry.getKey()); stream.writeObject(entry.getValue()); } @@ -577,28 +375,22 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); - valueSetCapacity = stream.readInt(); - int distinctKeys = stream.readInt(); - Map<K, Collection<V>> map = - new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys)); - for (int i = 0; i < distinctKeys; i++) { - @SuppressWarnings("unchecked") - K key = (K) stream.readObject(); - map.put(key, createCollection(key)); - } - int entries = stream.readInt(); - for (int i = 0; i < entries; i++) { - @SuppressWarnings("unchecked") + expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + setMap(new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys))); + linkedEntries = new LinkedHashSet<Map.Entry<K, V>>( + distinctKeys * expectedValuesPerKey); + Serialization.populateMultimap(this, stream, distinctKeys); + linkedEntries.clear(); // will clear and repopulate entries + for (int i = 0; i < size(); i++) { + @SuppressWarnings("unchecked") // reading data stored by writeObject K key = (K) stream.readObject(); - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // reading data stored by writeObject V value = (V) stream.readObject(); - map.get(key).add(value); + linkedEntries.add(Maps.immutableEntry(key, value)); } - setMap(map); } @GwtIncompatible("java serialization not supported") - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 0; } diff --git a/guava/src/com/google/common/collect/LinkedHashMultiset.java b/guava/src/com/google/common/collect/LinkedHashMultiset.java index 9b8c45b..c8db8e2 100644 --- a/guava/src/com/google/common/collect/LinkedHashMultiset.java +++ b/guava/src/com/google/common/collect/LinkedHashMultiset.java @@ -31,10 +31,6 @@ import java.util.LinkedHashMap; * element, those instances are consecutive in the iteration order. If all * occurrences of an element are removed, after which that element is added to * the multiset, the element will appear at the end of the iteration. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. * * @author Kevin Bourrillion * @author Jared Levy diff --git a/guava/src/com/google/common/collect/LinkedListMultimap.java b/guava/src/com/google/common/collect/LinkedListMultimap.java index 0a7b100..336ca72 100644 --- a/guava/src/com/google/common/collect/LinkedListMultimap.java +++ b/guava/src/com/google/common/collect/LinkedListMultimap.java @@ -17,7 +17,9 @@ 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.Multisets.setCountImpl; import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; @@ -29,10 +31,11 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractMap; import java.util.AbstractSequentialList; +import java.util.AbstractSet; import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -92,10 +95,6 @@ import javax.annotation.Nullable; * update operations, wrap your multimap with a call to {@link * Multimaps#synchronizedListMultimap}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. - * * @author Mike Bostock * @since 2.0 (imported from Google Collections Library) */ @@ -126,32 +125,12 @@ public class LinkedListMultimap<K, V> return key + "=" + value; } } - - private static class KeyList<K, V> { - Node<K, V> head; - Node<K, V> tail; - int count; - - KeyList(Node<K, V> firstNode) { - this.head = firstNode; - this.tail = firstNode; - firstNode.previousSibling = null; - firstNode.nextSibling = null; - this.count = 1; - } - } private transient Node<K, V> head; // the head for all keys private transient Node<K, V> tail; // the tail for all keys - private transient Map<K, KeyList<K, V>> keyToKeyList; - private transient int size; - - /* - * Tracks modifications to keyToKeyList so that addition or removal of keys invalidates - * preexisting iterators. This does *not* track simple additions and removals of values - * that are not the first to be added or last to be removed for their key. - */ - private transient int modCount; + private transient Multiset<K> keyCount; // the number of values for each key + private transient Map<K, Node<K, V>> keyToKeyHead; // the head for a given key + private transient Map<K, Node<K, V>> keyToKeyTail; // the tail for a given key /** * Creates a new, empty {@code LinkedListMultimap} with the default initial @@ -185,11 +164,15 @@ public class LinkedListMultimap<K, V> } LinkedListMultimap() { - keyToKeyList = Maps.newHashMap(); + keyCount = LinkedHashMultiset.create(); + keyToKeyHead = Maps.newHashMap(); + keyToKeyTail = Maps.newHashMap(); } private LinkedListMultimap(int expectedKeys) { - keyToKeyList = new HashMap<K, KeyList<K, V>>(expectedKeys); + keyCount = LinkedHashMultiset.create(expectedKeys); + keyToKeyHead = Maps.newHashMapWithExpectedSize(expectedKeys); + keyToKeyTail = Maps.newHashMapWithExpectedSize(expectedKeys); } private LinkedListMultimap(Multimap<? extends K, ? extends V> multimap) { @@ -208,32 +191,27 @@ public class LinkedListMultimap<K, V> Node<K, V> node = new Node<K, V>(key, value); if (head == null) { // empty list head = tail = node; - keyToKeyList.put(key, new KeyList<K, V>(node)); - modCount++; + keyToKeyHead.put(key, node); + keyToKeyTail.put(key, node); } else if (nextSibling == null) { // non-empty list, add to tail tail.next = node; node.previous = tail; - tail = node; - KeyList<K, V> keyList = keyToKeyList.get(key); - if (keyList == null) { - keyToKeyList.put(key, keyList = new KeyList<K, V>(node)); - modCount++; + Node<K, V> keyTail = keyToKeyTail.get(key); + if (keyTail == null) { // first for this key + keyToKeyHead.put(key, node); } else { - keyList.count++; - Node<K, V> keyTail = keyList.tail; keyTail.nextSibling = node; node.previousSibling = keyTail; - keyList.tail = node; } + keyToKeyTail.put(key, node); + tail = node; } else { // non-empty list, insert before nextSibling - KeyList<K, V> keyList = keyToKeyList.get(key); - keyList.count++; node.previous = nextSibling.previous; node.previousSibling = nextSibling.previousSibling; node.next = nextSibling; node.nextSibling = nextSibling; if (nextSibling.previousSibling == null) { // nextSibling was key head - keyToKeyList.get(key).head = node; + keyToKeyHead.put(key, node); } else { nextSibling.previousSibling.nextSibling = node; } @@ -245,7 +223,7 @@ public class LinkedListMultimap<K, V> nextSibling.previous = node; nextSibling.previousSibling = node; } - size++; + keyCount.add(key); return node; } @@ -265,27 +243,21 @@ public class LinkedListMultimap<K, V> } else { // node was tail tail = node.previous; } - if (node.previousSibling == null && node.nextSibling == null) { - KeyList<K, V> keyList = keyToKeyList.remove(node.key); - keyList.count = 0; - modCount++; + if (node.previousSibling != null) { + node.previousSibling.nextSibling = node.nextSibling; + } else if (node.nextSibling != null) { // node was key head + keyToKeyHead.put(node.key, node.nextSibling); } else { - KeyList<K, V> keyList = keyToKeyList.get(node.key); - keyList.count--; - - if (node.previousSibling == null) { - keyList.head = node.nextSibling; - } else { - node.previousSibling.nextSibling = node.nextSibling; - } - - if (node.nextSibling == null) { - keyList.tail = node.previousSibling; - } else { - node.nextSibling.previousSibling = node.previousSibling; - } + keyToKeyHead.remove(node.key); // don't leak a key-null entry } - size--; + if (node.nextSibling != null) { + node.nextSibling.previousSibling = node.previousSibling; + } else if (node.previousSibling != null) { // node was key tail + keyToKeyTail.put(node.key, node.previousSibling); + } else { + keyToKeyTail.remove(node.key); // don't leak a key-null entry + } + keyCount.remove(node.key); } /** Removes all nodes for the specified key. */ @@ -309,7 +281,6 @@ public class LinkedListMultimap<K, V> Node<K, V> next; Node<K, V> current; Node<K, V> previous; - int expectedModCount = modCount; NodeIterator() { next = head; @@ -331,19 +302,12 @@ public class LinkedListMultimap<K, V> } current = null; } - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } @Override public boolean hasNext() { - checkForConcurrentModification(); return next != null; } @Override public Node<K, V> next() { - checkForConcurrentModification(); checkElement(next); previous = current = next; next = next.next; @@ -352,7 +316,6 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); if (current != next) { // after call to next() previous = current.previous; @@ -362,16 +325,13 @@ public class LinkedListMultimap<K, V> } removeNode(current); current = null; - expectedModCount = modCount; } @Override public boolean hasPrevious() { - checkForConcurrentModification(); return previous != null; } @Override public Node<K, V> previous() { - checkForConcurrentModification(); checkElement(previous); next = current = previous; previous = previous.previous; @@ -405,21 +365,13 @@ public class LinkedListMultimap<K, V> final Set<K> seenKeys = Sets.<K>newHashSetWithExpectedSize(keySet().size()); Node<K, V> next = head; Node<K, V> current; - int expectedModCount = modCount; - - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } + @Override public boolean hasNext() { - checkForConcurrentModification(); return next != null; } @Override public K next() { - checkForConcurrentModification(); checkElement(next); current = next; seenKeys.add(current.key); @@ -430,11 +382,9 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); removeAllNodes(current.key); current = null; - expectedModCount = modCount; } } @@ -449,8 +399,7 @@ public class LinkedListMultimap<K, V> /** Constructs a new iterator over all values for the specified key. */ ValueForKeyIterator(@Nullable Object key) { this.key = key; - KeyList<K, V> keyList = keyToKeyList.get(key); - next = (keyList == null) ? null : keyList.head; + next = keyToKeyHead.get(key); } /** @@ -463,17 +412,16 @@ public class LinkedListMultimap<K, V> * @throws IndexOutOfBoundsException if index is invalid */ public ValueForKeyIterator(@Nullable Object key, int index) { - KeyList<K, V> keyList = keyToKeyList.get(key); - int size = (keyList == null) ? 0 : keyList.count; + int size = keyCount.count(key); Preconditions.checkPositionIndex(index, size); if (index >= (size / 2)) { - previous = (keyList == null) ? null : keyList.tail; + previous = keyToKeyTail.get(key); nextIndex = size; while (index++ < size) { previous(); } } else { - next = (keyList == null) ? null : keyList.head; + next = keyToKeyHead.get(key); while (index-- > 0) { next(); } @@ -552,7 +500,7 @@ public class LinkedListMultimap<K, V> @Override public int size() { - return size; + return keyCount.size(); } @Override @@ -562,7 +510,7 @@ public class LinkedListMultimap<K, V> @Override public boolean containsKey(@Nullable Object key) { - return keyToKeyList.containsKey(key); + return keyToKeyHead.containsKey(key); } @Override @@ -689,9 +637,9 @@ public class LinkedListMultimap<K, V> public void clear() { head = null; tail = null; - keyToKeyList.clear(); - size = 0; - modCount++; + keyCount.clear(); + keyToKeyHead.clear(); + keyToKeyTail.clear(); } // Views @@ -709,8 +657,7 @@ public class LinkedListMultimap<K, V> public List<V> get(final @Nullable K key) { return new AbstractSequentialList<V>() { @Override public int size() { - KeyList<K, V> keyList = keyToKeyList.get(key); - return (keyList == null) ? 0 : keyList.count; + return keyCount.count(key); } @Override public ListIterator<V> listIterator(int index) { return new ValueForKeyIterator(key, index); @@ -730,19 +677,19 @@ public class LinkedListMultimap<K, V> public Set<K> keySet() { Set<K> result = keySet; if (result == null) { - keySet = result = new Sets.ImprovedAbstractSet<K>() { + keySet = result = new AbstractSet<K>() { @Override public int size() { - return keyToKeyList.size(); + return keyCount.elementSet().size(); } @Override public Iterator<K> iterator() { return new DistinctKeyIterator(); } @Override public boolean contains(Object key) { // for performance - return containsKey(key); + return keyCount.contains(key); } - @Override - public boolean remove(Object o) { // for performance - return !LinkedListMultimap.this.removeAll(o).isEmpty(); + @Override public boolean removeAll(Collection<?> c) { + checkNotNull(c); // eager for GWT + return super.removeAll(c); } }; } @@ -760,50 +707,39 @@ public class LinkedListMultimap<K, V> return result; } - private class MultisetView extends AbstractMultiset<K> { - @Override - public int size() { - return size; - } + private class MultisetView extends AbstractCollection<K> + implements Multiset<K> { - @Override - public int count(Object element) { - KeyList<K, V> keyList = keyToKeyList.get(element); - return (keyList == null) ? 0 : keyList.count; + @Override public int size() { + return keyCount.size(); } - @Override - Iterator<Entry<K>> entryIterator() { - return new TransformedIterator<K, Entry<K>>(new DistinctKeyIterator()) { + @Override public Iterator<K> iterator() { + final Iterator<Node<K, V>> nodes = new NodeIterator(); + return new Iterator<K>() { @Override - Entry<K> transform(final K key) { - return new Multisets.AbstractEntry<K>() { - @Override - public K getElement() { - return key; - } - - @Override - public int getCount() { - return keyToKeyList.get(key).count; - } - }; + public boolean hasNext() { + return nodes.hasNext(); + } + @Override + public K next() { + return nodes.next().key; + } + @Override + public void remove() { + nodes.remove(); } }; } @Override - int distinctElements() { - return elementSet().size(); + public int count(@Nullable Object key) { + return keyCount.count(key); } - @Override public Iterator<K> iterator() { - return new TransformedIterator<Node<K, V>, K>(new NodeIterator()) { - @Override - K transform(Node<K, V> node) { - return node.key; - } - }; + @Override + public int add(@Nullable K key, int occurrences) { + throw new UnsupportedOperationException(); } @Override @@ -819,9 +755,77 @@ public class LinkedListMultimap<K, V> } @Override + public int setCount(K element, int count) { + return setCountImpl(this, element, count); + } + + @Override + public boolean setCount(K element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); + } + + @Override public boolean removeAll(Collection<?> c) { + return Iterators.removeAll(iterator(), c); + } + + @Override public boolean retainAll(Collection<?> c) { + return Iterators.retainAll(iterator(), c); + } + + @Override public Set<K> elementSet() { return keySet(); } + + @Override + public Set<Entry<K>> entrySet() { + // TODO(jlevy): lazy init? + return new AbstractSet<Entry<K>>() { + @Override public int size() { + return keyCount.elementSet().size(); + } + + @Override public Iterator<Entry<K>> iterator() { + final Iterator<K> keyIterator = new DistinctKeyIterator(); + return new Iterator<Entry<K>>() { + @Override + public boolean hasNext() { + return keyIterator.hasNext(); + } + @Override + public Entry<K> next() { + final K key = keyIterator.next(); + return new Multisets.AbstractEntry<K>() { + @Override + public K getElement() { + return key; + } + @Override + public int getCount() { + return keyCount.count(key); + } + }; + } + @Override + public void remove() { + keyIterator.remove(); + } + }; + } + }; + } + + @Override public boolean equals(@Nullable Object object) { + return keyCount.equals(object); + } + + @Override public int hashCode() { + return keyCount.hashCode(); + } + + @Override public String toString() { + return keyCount.toString(); // XXX observe order? + } } private transient List<V> valuesList; @@ -841,20 +845,47 @@ public class LinkedListMultimap<K, V> if (result == null) { valuesList = result = new AbstractSequentialList<V>() { @Override public int size() { - return size; + return keyCount.size(); } @Override public ListIterator<V> listIterator(int index) { final NodeIterator nodes = new NodeIterator(index); - return new TransformedListIterator<Node<K, V>, V>(nodes) { + return new ListIterator<V>() { @Override - V transform(Node<K, V> node) { - return node.value; + public boolean hasNext() { + return nodes.hasNext(); + } + @Override + public V next() { + return nodes.next().value; + } + @Override + public boolean hasPrevious() { + return nodes.hasPrevious(); + } + @Override + public V previous() { + return nodes.previous().value; } - @Override - public void set(V value) { - nodes.setValue(value); + public int nextIndex() { + return nodes.nextIndex(); + } + @Override + public int previousIndex() { + return nodes.previousIndex(); + } + @Override + public void remove() { + nodes.remove(); + } + @Override + public void set(V e) { + nodes.setValue(e); + } + @Override + public void add(V e) { + throw new UnsupportedOperationException(); } }; } @@ -905,14 +936,55 @@ public class LinkedListMultimap<K, V> if (result == null) { entries = result = new AbstractSequentialList<Entry<K, V>>() { @Override public int size() { - return size; + return keyCount.size(); } @Override public ListIterator<Entry<K, V>> listIterator(int index) { - return new TransformedListIterator<Node<K, V>, Entry<K, V>>(new NodeIterator(index)) { + final ListIterator<Node<K, V>> nodes = new NodeIterator(index); + return new ListIterator<Entry<K, V>>() { + @Override + public boolean hasNext() { + return nodes.hasNext(); + } + + @Override + public Entry<K, V> next() { + return createEntry(nodes.next()); + } + + @Override + public void remove() { + nodes.remove(); + } + + @Override + public boolean hasPrevious() { + return nodes.hasPrevious(); + } + + @Override + public Map.Entry<K, V> previous() { + return createEntry(nodes.previous()); + } + + @Override + public int nextIndex() { + return nodes.nextIndex(); + } + + @Override + public int previousIndex() { + return nodes.previousIndex(); + } + + @Override + public void set(Map.Entry<K, V> e) { + throw new UnsupportedOperationException(); + } + @Override - Entry<K, V> transform(Node<K, V> node) { - return createEntry(node); + public void add(Map.Entry<K, V> e) { + throw new UnsupportedOperationException(); } }; } @@ -921,39 +993,75 @@ public class LinkedListMultimap<K, V> return result; } + private class AsMapEntries extends AbstractSet<Entry<K, Collection<V>>> { + @Override public int size() { + return keyCount.elementSet().size(); + } + + @Override public Iterator<Entry<K, Collection<V>>> iterator() { + final Iterator<K> keyIterator = new DistinctKeyIterator(); + return new Iterator<Entry<K, Collection<V>>>() { + @Override + public boolean hasNext() { + return keyIterator.hasNext(); + } + + @Override + public Entry<K, Collection<V>> next() { + final K key = keyIterator.next(); + return new AbstractMapEntry<K, Collection<V>>() { + @Override public K getKey() { + return key; + } + + @Override public Collection<V> getValue() { + return LinkedListMultimap.this.get(key); + } + }; + } + + @Override + public void remove() { + keyIterator.remove(); + } + }; + } + + // TODO(jlevy): Override contains() and remove() for better performance. + } + private transient Map<K, Collection<V>> map; @Override public Map<K, Collection<V>> asMap() { Map<K, Collection<V>> result = map; if (result == null) { - map = result = new Multimaps.AsMap<K, V>() { - @Override - public int size() { - return keyToKeyList.size(); + map = result = new AbstractMap<K, Collection<V>>() { + Set<Entry<K, Collection<V>>> entrySet; + + @Override public Set<Entry<K, Collection<V>>> entrySet() { + Set<Entry<K, Collection<V>>> result = entrySet; + if (result == null) { + entrySet = result = new AsMapEntries(); + } + return result; } - @Override - Multimap<K, V> multimap() { - return LinkedListMultimap.this; + // The following methods are included for performance. + + @Override public boolean containsKey(@Nullable Object key) { + return LinkedListMultimap.this.containsKey(key); } - @Override - Iterator<Entry<K, Collection<V>>> entryIterator() { - return new TransformedIterator<K, Entry<K, Collection<V>>>(new DistinctKeyIterator()) { - @Override - Entry<K, Collection<V>> transform(final K key) { - return new AbstractMapEntry<K, Collection<V>>() { - @Override public K getKey() { - return key; - } + @SuppressWarnings("unchecked") + @Override public Collection<V> get(@Nullable Object key) { + Collection<V> collection = LinkedListMultimap.this.get((K) key); + return collection.isEmpty() ? null : collection; + } - @Override public Collection<V> getValue() { - return LinkedListMultimap.this.get(key); - } - }; - } - }; + @Override public Collection<V> remove(@Nullable Object key) { + Collection<V> collection = removeAll(key); + return collection.isEmpty() ? null : collection; } }; } @@ -1020,7 +1128,9 @@ public class LinkedListMultimap<K, V> private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - keyToKeyList = Maps.newLinkedHashMap(); + keyCount = LinkedHashMultiset.create(); + keyToKeyHead = Maps.newHashMap(); + keyToKeyTail = Maps.newHashMap(); int size = stream.readInt(); for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") // reading data stored by writeObject diff --git a/guava/src/com/google/common/collect/ListMultimap.java b/guava/src/com/google/common/collect/ListMultimap.java index a83b81a..cf4cbaa 100644 --- a/guava/src/com/google/common/collect/ListMultimap.java +++ b/guava/src/com/google/common/collect/ListMultimap.java @@ -26,16 +26,11 @@ import javax.annotation.Nullable; /** * A {@code Multimap} that can hold duplicate key-value pairs and that maintains - * the insertion ordering of values for a given key. See the {@link Multimap} - * documentation for information common to all multimaps. + * the insertion ordering of values for a given key. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link List} of values. Though the method signature doesn't say * so explicitly, the map returned by {@link #asMap} has {@code List} values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/Lists.java b/guava/src/com/google/common/collect/Lists.java index 271da37..850d87e 100644 --- a/guava/src/com/google/common/collect/Lists.java +++ b/guava/src/com/google/common/collect/Lists.java @@ -25,7 +25,6 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Objects; @@ -44,24 +43,19 @@ import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; -import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; /** * Static utility methods pertaining to {@link List} instances. Also see this - * class's counterparts {@link Sets}, {@link Maps} and {@link Queues}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Lists"> - * {@code Lists}</a>. + * class's counterparts {@link Sets} and {@link Maps}. * * @author Kevin Bourrillion * @author Mike Bostock * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) */ -@GwtCompatible(emulated = true) +@GwtCompatible public final class Lists { private Lists() {} @@ -227,38 +221,6 @@ public final class Lists { } /** - * Creates an empty {@code CopyOnWriteArrayList} instance. - * - * <p><b>Note:</b> if you need an immutable empty {@link List}, use - * {@link Collections#emptyList} instead. - * - * @return a new, empty {@code CopyOnWriteArrayList} - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArrayList") - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() { - return new CopyOnWriteArrayList<E>(); - } - - /** - * Creates a {@code CopyOnWriteArrayList} instance containing the given elements. - * - * @param elements the elements that the list should contain, in order - * @return a new {@code CopyOnWriteArrayList} containing those elements - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArrayList") - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList( - Iterable<? extends E> elements) { - // We copy elements to an ArrayList first, rather than incurring the - // quadratic cost of adding them to the COWAL directly. - Collection<? extends E> elementsCollection = (elements instanceof Collection) - ? Collections2.cast(elements) - : newArrayList(elements); - return new CopyOnWriteArrayList<E>(elementsCollection); - } - - /** * Returns an unmodifiable list containing the specified first element and * backed by the specified array of additional elements. Changes to the {@code * rest} array will be reflected in the returned list. Unlike {@link @@ -352,127 +314,6 @@ public final class Lists { } /** - * Returns every possible list that can be formed by choosing one element - * from each of the given lists in order; the "n-ary - * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian - * product</a>" of the lists. For example: <pre> {@code - * - * Lists.cartesianProduct(ImmutableList.of( - * ImmutableList.of(1, 2), - * ImmutableList.of("A", "B", "C")))}</pre> - * - * returns a list containing six lists in the following order: - * - * <ul> - * <li>{@code ImmutableList.of(1, "A")} - * <li>{@code ImmutableList.of(1, "B")} - * <li>{@code ImmutableList.of(1, "C")} - * <li>{@code ImmutableList.of(2, "A")} - * <li>{@code ImmutableList.of(2, "B")} - * <li>{@code ImmutableList.of(2, "C")} - * </ul> - * - * The result is guaranteed to be in the "traditional", lexicographical - * order for Cartesian products that you would get from nesting for loops: - * <pre> {@code - * - * for (B b0 : lists.get(0)) { - * for (B b1 : lists.get(1)) { - * ... - * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...); - * // operate on tuple - * } - * }}</pre> - * - * Note that if any input list is empty, the Cartesian product will also be - * empty. If no lists at all are provided (an empty list), the resulting - * Cartesian product has one element, an empty list (counter-intuitive, but - * mathematically consistent). - * - * <p><i>Performance notes:</i> while the cartesian product of lists of size - * {@code m, n, p} is a list of size {@code m x n x p}, its actual memory - * consumption is much smaller. When the cartesian product is constructed, the - * input lists are merely copied. Only as the resulting list is iterated are - * the individual lists created, and these are not retained after iteration. - * - * @param lists the lists to choose elements from, in the order that - * the elements chosen from those lists should appear in the resulting - * lists - * @param <B> any common base class shared by all axes (often just {@link - * Object}) - * @return the Cartesian product, as an immutable list containing immutable - * lists - * @throws IllegalArgumentException if the size of the cartesian product would - * be greater than {@link Integer#MAX_VALUE} - * @throws NullPointerException if {@code lists}, any one of the {@code lists}, - * or any element of a provided list is null - */ - static <B> List<List<B>> cartesianProduct( - List<? extends List<? extends B>> lists) { - return CartesianList.create(lists); - } - - /** - * Returns every possible list that can be formed by choosing one element - * from each of the given lists in order; the "n-ary - * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian - * product</a>" of the lists. For example: <pre> {@code - * - * Lists.cartesianProduct(ImmutableList.of( - * ImmutableList.of(1, 2), - * ImmutableList.of("A", "B", "C")))}</pre> - * - * returns a list containing six lists in the following order: - * - * <ul> - * <li>{@code ImmutableList.of(1, "A")} - * <li>{@code ImmutableList.of(1, "B")} - * <li>{@code ImmutableList.of(1, "C")} - * <li>{@code ImmutableList.of(2, "A")} - * <li>{@code ImmutableList.of(2, "B")} - * <li>{@code ImmutableList.of(2, "C")} - * </ul> - * - * The result is guaranteed to be in the "traditional", lexicographical - * order for Cartesian products that you would get from nesting for loops: - * <pre> {@code - * - * for (B b0 : lists.get(0)) { - * for (B b1 : lists.get(1)) { - * ... - * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...); - * // operate on tuple - * } - * }}</pre> - * - * Note that if any input list is empty, the Cartesian product will also be - * empty. If no lists at all are provided (an empty list), the resulting - * Cartesian product has one element, an empty list (counter-intuitive, but - * mathematically consistent). - * - * <p><i>Performance notes:</i> while the cartesian product of lists of size - * {@code m, n, p} is a list of size {@code m x n x p}, its actual memory - * consumption is much smaller. When the cartesian product is constructed, the - * input lists are merely copied. Only as the resulting list is iterated are - * the individual lists created, and these are not retained after iteration. - * - * @param lists the lists to choose elements from, in the order that - * the elements chosen from those lists should appear in the resulting - * lists - * @param <B> any common base class shared by all axes (often just {@link - * Object}) - * @return the Cartesian product, as an immutable list containing immutable - * lists - * @throws IllegalArgumentException if the size of the cartesian product would - * be greater than {@link Integer#MAX_VALUE} - * @throws NullPointerException if {@code lists}, any one of the - * {@code lists}, or any element of a provided list is null - */ - static <B> List<List<B>> cartesianProduct(List<? extends B>... lists) { - return cartesianProduct(Arrays.asList(lists)); - } - - /** * Returns a list that applies {@code function} to each element of {@code * fromList}. The returned list is a transformed view of {@code fromList}; * changes to {@code fromList} will be reflected in the returned list and vice @@ -491,19 +332,13 @@ public final class Lists { * view, copy the returned list into a new list of your choosing. * * <p>If {@code fromList} implements {@link RandomAccess}, so will the - * returned list. The returned list is threadsafe if the supplied list and - * function are. + * returned list. The returned list always implements {@link Serializable}, + * but serialization will succeed only when {@code fromList} and + * {@code function} are serializable. The returned list is threadsafe if the + * supplied list and function are. * * <p>If only a {@code Collection} or {@code Iterable} input is available, use * {@link Collections2#transform} or {@link Iterables#transform}. - * - * <p><b>Note:</b> serializing the returned list is implemented by serializing - * {@code fromList}, its contents, and {@code function} -- <i>not</i> by - * serializing the transformed values. This can lead to surprising behavior, - * so serializing the returned list is <b>not recommended</b>. Instead, - * copy the list using {@link ImmutableList#copyOf(Collection)} (for example), - * then serialize the copy. Other methods similar to this do not implement - * serialization at all for this reason. */ public static <F, T> List<T> transform( List<F> fromList, Function<? super F, ? extends T> function) { @@ -539,10 +374,51 @@ public final class Lists { return fromList.size(); } @Override public ListIterator<T> listIterator(final int index) { - return new TransformedListIterator<F, T>(fromList.listIterator(index)) { + final ListIterator<F> delegate = fromList.listIterator(index); + return new ListIterator<T>() { + @Override + public void add(T e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public boolean hasPrevious() { + return delegate.hasPrevious(); + } + + @Override + public T next() { + return function.apply(delegate.next()); + } + + @Override + public int nextIndex() { + return delegate.nextIndex(); + } + + @Override + public T previous() { + return function.apply(delegate.previous()); + } + + @Override + public int previousIndex() { + return delegate.previousIndex(); + } + @Override - T transform(F from) { - return function.apply(from); + public void remove() { + delegate.remove(); + } + + @Override + public void set(T e) { + throw new UnsupportedOperationException("not supported"); } }; } @@ -670,6 +546,10 @@ public final class Lists { this.string = string; } + @Override public boolean contains(@Nullable Object object) { + return indexOf(object) >= 0; + } + @Override public int indexOf(@Nullable Object object) { return (object instanceof Character) ? string.indexOf((Character) object) : -1; @@ -680,9 +560,17 @@ public final class Lists { ? string.lastIndexOf((Character) object) : -1; } + @Override public UnmodifiableListIterator<Character> listIterator( + int index) { + return new AbstractIndexedListIterator<Character>(size(), index) { + @Override protected Character get(int index) { + return string.charAt(index); + } + }; + } + @Override public ImmutableList<Character> subList( int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); // for GWT return charactersOf(string.substring(fromIndex, toIndex)); } @@ -691,7 +579,6 @@ public final class Lists { } @Override public Character get(int index) { - checkElementIndex(index, size()); // for GWT return string.charAt(index); } @@ -758,7 +645,6 @@ public final class Lists { } @Override public Character get(int index) { - checkElementIndex(index, size()); // for GWT return sequence.charAt(index); } @@ -795,7 +681,6 @@ public final class Lists { } @Override public List<Character> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); // for GWT return charactersOf(sequence.subSequence(fromIndex, toIndex)); } @@ -1003,13 +888,10 @@ public final class Lists { /** * An implementation of {@link List#hashCode()}. */ - static int hashCodeImpl(List<?> list) { + static int hashCodeImpl(List<?> list){ int hashCode = 1; for (Object o : list) { hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); - - hashCode = ~~hashCode; - // needed to deal with GWT integer overflow } return hashCode; } @@ -1146,11 +1028,4 @@ public final class Lists { super(backingList); } } - - /** - * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 - */ - static <T> List<T> cast(Iterable<T> iterable) { - return (List<T>) iterable; - } } diff --git a/guava/src/com/google/common/collect/MapConstraints.java b/guava/src/com/google/common/collect/MapConstraints.java index 090625d..11351bb 100644 --- a/guava/src/com/google/common/collect/MapConstraints.java +++ b/guava/src/com/google/common/collect/MapConstraints.java @@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import java.io.Serializable; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; @@ -396,7 +395,7 @@ public final class MapConstraints { /** @see MapConstraints#constrainedMultimap */ private static class ConstrainedMultimap<K, V> - extends ForwardingMultimap<K, V> implements Serializable { + extends ForwardingMultimap<K, V> { final MapConstraint<? super K, ? super V> constraint; final Multimap<K, V> delegate; transient Collection<Entry<K, V>> entries; diff --git a/guava/src/com/google/common/collect/MapMaker.java b/guava/src/com/google/common/collect/MapMaker.java index a5a035e..330018e 100644 --- a/guava/src/com/google/common/collect/MapMaker.java +++ b/guava/src/com/google/common/collect/MapMaker.java @@ -18,12 +18,12 @@ import static com.google.common.base.Objects.firstNonNull; 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.MapMakerInternalMap.Strength.SOFT; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Throwables; @@ -51,6 +51,8 @@ import javax.annotation.Nullable; * <ul> * <li>keys or values automatically wrapped in {@linkplain WeakReference weak} or {@linkplain * SoftReference soft} references + * <li>least-recently-used eviction when a maximum size is exceeded + * <li>time-based expiration of entries, measured since last access or last write * <li>notification of evicted (or otherwise removed) entries * <li>on-demand computation of values for keys not already present * </ul> @@ -60,6 +62,8 @@ import javax.annotation.Nullable; * ConcurrentMap<Key, Graph> graphs = new MapMaker() * .concurrencyLevel(4) * .weakKeys() + * .maximumSize(10000) + * .expireAfterWrite(10, TimeUnit.MINUTES) * .makeComputingMap( * new Function<Key, Graph>() { * public Graph apply(Key key) { @@ -75,9 +79,10 @@ import javax.annotation.Nullable; * interface. It does not permit null keys or values. * * <p><b>Note:</b> by default, the returned map uses equality comparisons (the {@link Object#equals - * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} was - * specified, the map uses identity ({@code ==}) comparisons instead for keys. Likewise, if {@link - * #weakValues} or {@link #softValues} was specified, the map uses identity comparisons for values. + * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} or {@link + * #softKeys} was specified, the map uses identity ({@code ==}) comparisons instead for keys. + * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the map uses identity + * comparisons for values. * * <p>The view collections of the returned map have <i>weakly consistent iterators</i>. This means * that they are safe for concurrent use, but if other threads modify the map after the iterator is @@ -129,6 +134,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { RemovalCause nullRemovalCause; Equivalence<Object> keyEquivalence; + Equivalence<Object> valueEquivalence; Ticker ticker; @@ -138,12 +144,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { */ public MapMaker() {} + private boolean useNullMap() { + return (nullRemovalCause == null); + } + /** * Sets a custom {@code Equivalence} strategy for comparing keys. * - * <p>By default, the map uses {@link Equivalence#identity} to determine key equality when {@link - * #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. The only place this is - * used is in {@link Interners.WeakInterner}. + * <p>By default, the map uses {@link Equivalences#identity} to determine key equality when + * {@link #weakKeys} or {@link #softKeys} is specified, and {@link Equivalences#equals()} + * otherwise. */ @GwtIncompatible("To be supported") @Override @@ -159,6 +169,27 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Sets a custom {@code Equivalence} strategy for comparing values. + * + * <p>By default, the map uses {@link Equivalences#identity} to determine value equality when + * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalences#equals()} + * otherwise. + */ + @GwtIncompatible("To be supported") + @Override + MapMaker valueEquivalence(Equivalence<Object> equivalence) { + checkState(valueEquivalence == null, + "value equivalence was already set to %s", valueEquivalence); + this.valueEquivalence = checkNotNull(equivalence); + this.useCustomMap = true; + return this; + } + + Equivalence<Object> getValueEquivalence() { + return firstNonNull(valueEquivalence, getValueStrength().defaultEquivalence()); + } + + /** * Sets the minimum total size for the internal hash tables. For example, if the initial capacity * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each * having a hash table of size eight. Providing a large enough estimate at construction time @@ -200,9 +231,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if a maximum size was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #maximumSize} being - * replaced by {@link com.google.common.cache.CacheBuilder#maximumSize}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#maximumSize}. */ @Deprecated @Override @@ -252,6 +281,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Specifies that each key (not value) stored in the map should be strongly referenced. + * + * @throws IllegalStateException if the key strength was already set + */ + @Override + MapMaker strongKeys() { + return setKeyStrength(Strength.STRONG); + } + + /** * Specifies that each key (not value) stored in the map should be wrapped in a {@link * WeakReference} (by default, strong references are used). * @@ -268,10 +307,36 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { return setKeyStrength(Strength.WEAK); } + /** + * <b>This method is broken.</b> Maps with soft keys offer no functional advantage over maps with + * weak keys, and they waste memory by keeping unreachable elements in the map. If your goal is to + * create a memory-sensitive map, then consider using soft values instead. + * + * <p>Specifies that each key (not value) stored in the map should be wrapped in a + * {@link SoftReference} (by default, strong references are used). Softly-referenced objects will + * be garbage-collected in a <i>globally</i> least-recently-used manner, in response to memory + * demand. + * + * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==}) + * comparison to determine equality of keys, which is a technical violation of the {@link Map} + * specification, and may not be what you expect. + * + * @throws IllegalStateException if the key strength was already set + * @see SoftReference + * @deprecated use {@link #softValues} to create a memory-sensitive map, or {@link #weakKeys} to + * create a map that doesn't hold strong references to the keys. + * <b>This method is scheduled for deletion in January 2013.</b> + */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + @Override + public MapMaker softKeys() { + return setKeyStrength(Strength.SOFT); + } + MapMaker setKeyStrength(Strength strength) { checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); keyStrength = checkNotNull(strength); - checkArgument(keyStrength != SOFT, "Soft keys are not supported"); if (strength != Strength.STRONG) { // STRONG could be used during deserialization. useCustomMap = true; @@ -284,6 +349,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Specifies that each value (not key) stored in the map should be strongly referenced. + * + * @throws IllegalStateException if the value strength was already set + */ + @Override + MapMaker strongValues() { + return setValueStrength(Strength.STRONG); + } + + /** * Specifies that each value (not key) stored in the map should be wrapped in a * {@link WeakReference} (by default, strong references are used). * @@ -347,6 +422,22 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Old name of {@link #expireAfterWrite}. + * + * @deprecated Caching functionality in {@code MapMaker} is being moved to + * {@link com.google.common.cache.CacheBuilder}. Functionality equivalent to + * {@link MapMaker#expiration} is provided by + * {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. + * <b>This method is scheduled for deletion in July 2012.</b> + */ + @Deprecated + @Override + public + MapMaker expiration(long duration, TimeUnit unit) { + return expireAfterWrite(duration, unit); + } + + /** * Specifies that each entry should be automatically removed from the map once a fixed duration * has elapsed after the entry's creation, or the most recent replacement of its value. * @@ -367,9 +458,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if the time to live or time to idle was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterWrite} being - * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. */ @Deprecated @Override @@ -417,9 +506,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if the time to idle or time to live was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterAccess} being - * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterAccess}. Note that - * {@code CacheBuilder} is simply an enhanced API for an implementation which was branched - * from {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterAccess}. */ @Deprecated @GwtIncompatible("To be supported") @@ -469,9 +556,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if a removal listener was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #removalListener} being - * replaced by {@link com.google.common.cache.CacheBuilder#removalListener}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#removalListener}. */ @Deprecated @GwtIncompatible("To be supported") @@ -571,16 +656,17 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @return a serializable concurrent map having the requested features * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #makeComputingMap} being replaced - * by {@link com.google.common.cache.CacheBuilder#build}. See the - * <a href="http://code.google.com/p/guava-libraries/wiki/MapMakerMigration">MapMaker - * Migration Guide</a> for more details. + * by {@link com.google.common.cache.CacheBuilder#build}. Note that uses of + * {@link #makeComputingMap} with {@code AtomicLong} values can often be migrated to + * {@link AtomicLongMap}. * <b>This method is scheduled for deletion in February 2013.</b> + * */ @Deprecated @Override public <K, V> ConcurrentMap<K, V> makeComputingMap( Function<? super K, ? extends V> computingFunction) { - return (nullRemovalCause == null) + return useNullMap() ? new MapMaker.ComputingMapAdapter<K, V>(this, computingFunction) : new NullComputingConcurrentMap<K, V>(this, computingFunction); } @@ -616,6 +702,9 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { if (keyEquivalence != null) { s.addValue("keyEquivalence"); } + if (valueEquivalence != null) { + s.addValue("valueEquivalence"); + } if (removalListener != null) { s.addValue("removalListener"); } @@ -706,8 +795,9 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { }, /** - * The entry was removed automatically because its key or value was garbage-collected. This can - * occur when using {@link #softValues}, {@link #weakKeys}, or {@link #weakValues}. + * The entry was removed automatically because its key or value was garbage-collected. This + * can occur when using {@link #softKeys}, {@link #softValues}, {@link #weakKeys}, or {@link + * #weakValues}. */ COLLECTED { @Override @@ -862,10 +952,6 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * Overrides get() to compute on demand. Also throws an exception when {@code null} is returned * from a computation. */ - /* - * This might make more sense in ComputingConcurrentHashMap, but it causes a javac crash in some - * cases there: http://code.google.com/p/guava-libraries/issues/detail?id=950 - */ static final class ComputingMapAdapter<K, V> extends ComputingConcurrentHashMap<K, V> implements Serializable { private static final long serialVersionUID = 0; @@ -893,4 +979,5 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { return value; } } + } diff --git a/guava/src/com/google/common/collect/MapMakerInternalMap.java b/guava/src/com/google/common/collect/MapMakerInternalMap.java index b2d05bd..a5a6be8 100644 --- a/guava/src/com/google/common/collect/MapMakerInternalMap.java +++ b/guava/src/com/google/common/collect/MapMakerInternalMap.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Ticker; import com.google.common.collect.GenericMapMaker.NullListener; import com.google.common.collect.MapMaker.RemovalCause; @@ -199,7 +200,7 @@ class MapMakerInternalMap<K, V> valueStrength = builder.getValueStrength(); keyEquivalence = builder.getKeyEquivalence(); - valueEquivalence = valueStrength.defaultEquivalence(); + valueEquivalence = builder.getValueEquivalence(); maximumSize = builder.maximumSize; expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); @@ -301,7 +302,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.equals(); + return Equivalences.equals(); } }, @@ -314,7 +315,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }, @@ -327,7 +328,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }; @@ -403,6 +404,60 @@ class MapMakerInternalMap<K, V> } }, + SOFT { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + }, + SOFT_EXPIRABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftExpirableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + SOFT_EVICTABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftEvictableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + SOFT_EXPIRABLE_EVICTABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftExpirableEvictableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + WEAK { @Override <K, V> ReferenceEntry<K, V> newEntry( @@ -469,7 +524,7 @@ class MapMakerInternalMap<K, V> */ static final EntryFactory[][] factories = { { STRONG, STRONG_EXPIRABLE, STRONG_EVICTABLE, STRONG_EXPIRABLE_EVICTABLE }, - {}, // no support for SOFT keys + { SOFT, SOFT_EXPIRABLE, SOFT_EVICTABLE, SOFT_EXPIRABLE_EVICTABLE }, { WEAK, WEAK_EXPIRABLE, WEAK_EVICTABLE, WEAK_EXPIRABLE_EVICTABLE } }; @@ -550,11 +605,8 @@ class MapMakerInternalMap<K, V> /** * Creates a copy of this reference for the given entry. - * - * <p>{@code value} may be null only for a loading reference. */ - ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry); + ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry); /** * Clears this reference object. @@ -587,8 +639,8 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<Object, Object> copyFor(ReferenceQueue<Object> queue, - @Nullable Object value, ReferenceEntry<Object, Object> entry) { + public ValueReference<Object, Object> copyFor( + ReferenceQueue<Object> queue, ReferenceEntry<Object, Object> entry) { return this; } @@ -783,7 +835,7 @@ class MapMakerInternalMap<K, V> public void setPreviousEvictable(ReferenceEntry<Object, Object> previous) {} } - abstract static class AbstractReferenceEntry<K, V> implements ReferenceEntry<K, V> { + static abstract class AbstractReferenceEntry<K, V> implements ReferenceEntry<K, V> { @Override public ValueReference<K, V> getValueReference() { throw new UnsupportedOperationException(); @@ -1704,8 +1756,8 @@ class MapMakerInternalMap<K, V> @Override public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new WeakValueReference<K, V>(queue, value, entry); + ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new WeakValueReference<K, V>(queue, get(), entry); } @Override @@ -1742,9 +1794,8 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new SoftValueReference<K, V>(queue, value, entry); + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new SoftValueReference<K, V>(queue, get(), entry); } @Override @@ -1779,8 +1830,7 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -2129,26 +2179,11 @@ class MapMakerInternalMap<K, V> return map.entryFactory.newEntry(this, key, hash, next); } - /** - * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, - * or {@code null} if {@code original} was already garbage collected. - */ @GuardedBy("Segment.this") ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { - if (original.getKey() == null) { - // key collected - return null; - } - ValueReference<K, V> valueReference = original.getValueReference(); - V value = valueReference.get(); - if ((value == null) && !valueReference.isComputingReference()) { - // value collected - return null; - } - ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext); - newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, newEntry)); return newEntry; } @@ -2617,14 +2652,14 @@ class MapMakerInternalMap<K, V> // Clone nodes leading up to the tail. for (ReferenceEntry<K, V> e = head; e != tail; e = e.getNext()) { - int newIndex = e.getHash() & newMask; - ReferenceEntry<K, V> newNext = newTable.get(newIndex); - ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); - if (newFirst != null) { - newTable.set(newIndex, newFirst); - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + int newIndex = e.getHash() & newMask; + ReferenceEntry<K, V> newNext = newTable.get(newIndex); + ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); + newTable.set(newIndex, newFirst); } } } @@ -2867,12 +2902,11 @@ class MapMakerInternalMap<K, V> int newCount = count; ReferenceEntry<K, V> newFirst = entry.getNext(); for (ReferenceEntry<K, V> e = first; e != entry; e = e.getNext()) { - ReferenceEntry<K, V> next = copyEntry(e, newFirst); - if (next != null) { - newFirst = next; - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + newFirst = copyEntry(e, newFirst); } } this.count = newCount; @@ -3008,6 +3042,17 @@ class MapMakerInternalMap<K, V> } /** + * Returns {@code true} if the entry has been partially collected, meaning that either the key + * is null, or the value is null and it is not computing. + */ + boolean isCollected(ReferenceEntry<K, V> entry) { + if (entry.getKey() == null) { + return true; + } + return isCollected(entry.getValueReference()); + } + + /** * Returns {@code true} if the value has been partially collected, meaning that the value is * null and it is not computing. */ @@ -3215,7 +3260,7 @@ class MapMakerInternalMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextEvictable(); @@ -3351,7 +3396,7 @@ class MapMakerInternalMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextExpirable(); @@ -3446,6 +3491,17 @@ class MapMakerInternalMap<K, V> return segmentFor(hash).getEntry(key, hash); } + /** + * Returns the live internal entry for the specified key. Does not impact recency ordering. + */ + ReferenceEntry<K, V> getLiveEntry(@Nullable Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).getLiveEntry(key, hash); + } + @Override public boolean containsKey(@Nullable Object key) { if (key == null) { @@ -3466,7 +3522,7 @@ class MapMakerInternalMap<K, V> // such that none of the subsequent iterations observed it, despite the fact that at every point // in time it was present somewhere int the map. This becomes increasingly unlikely as // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. - final Segment<K, V>[] segments = this.segments; + final Segment<K,V>[] segments = this.segments; long last = -1L; for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { long sum = 0L; @@ -3476,7 +3532,7 @@ class MapMakerInternalMap<K, V> int c = segment.count; // read-volatile AtomicReferenceArray<ReferenceEntry<K, V>> table = segment.table; - for (int j = 0; j < table.length(); j++) { + for (int j = 0 ; j < table.length(); j++) { for (ReferenceEntry<K, V> e = table.get(j); e != null; e = e.getNext()) { V v = segment.getLiveValue(e); if (v != null && valueEquivalence.equivalent(value, v)) { @@ -3561,7 +3617,7 @@ class MapMakerInternalMap<K, V> } } - transient Set<K> keySet; + Set<K> keySet; @Override public Set<K> keySet() { @@ -3569,7 +3625,7 @@ class MapMakerInternalMap<K, V> return (ks != null) ? ks : (keySet = new KeySet()); } - transient Collection<V> values; + Collection<V> values; @Override public Collection<V> values() { @@ -3577,7 +3633,7 @@ class MapMakerInternalMap<K, V> return (vs != null) ? vs : (values = new Values()); } - transient Set<Entry<K, V>> entrySet; + Set<Entry<K, V>> entrySet; @Override public Set<Entry<K, V>> entrySet() { @@ -3587,7 +3643,7 @@ class MapMakerInternalMap<K, V> // Iterator Support - abstract class HashIterator<E> implements Iterator<E> { + abstract class HashIterator { int nextSegmentIndex; int nextTableIndex; @@ -3603,8 +3659,6 @@ class MapMakerInternalMap<K, V> advance(); } - public abstract E next(); - final void advance() { nextExternal = null; @@ -3696,7 +3750,7 @@ class MapMakerInternalMap<K, V> } } - final class KeyIterator extends HashIterator<K> { + final class KeyIterator extends HashIterator implements Iterator<K> { @Override public K next() { @@ -3704,7 +3758,7 @@ class MapMakerInternalMap<K, V> } } - final class ValueIterator extends HashIterator<V> { + final class ValueIterator extends HashIterator implements Iterator<V> { @Override public V next() { @@ -3759,7 +3813,7 @@ class MapMakerInternalMap<K, V> } } - final class EntryIterator extends HashIterator<Entry<K, V>> { + final class EntryIterator extends HashIterator implements Iterator<Entry<K, V>> { @Override public Entry<K, V> next() { @@ -3945,6 +3999,7 @@ class MapMakerInternalMap<K, V> .setKeyStrength(keyStrength) .setValueStrength(valueStrength) .keyEquivalence(keyEquivalence) + .valueEquivalence(valueEquivalence) .concurrencyLevel(concurrencyLevel); mapMaker.removalListener(removalListener); if (expireAfterWriteNanos > 0) { diff --git a/guava/src/com/google/common/collect/Maps.java b/guava/src/com/google/common/collect/Maps.java index 9569a52..4215869 100644 --- a/guava/src/com/google/common/collect/Maps.java +++ b/guava/src/com/google/common/collect/Maps.java @@ -23,6 +23,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.base.Joiner.MapJoiner; import com.google.common.base.Objects; @@ -35,6 +36,7 @@ import com.google.common.primitives.Ints; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -46,25 +48,17 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NavigableSet; import java.util.Properties; import java.util.Set; import java.util.SortedMap; -import java.util.SortedSet; import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; import javax.annotation.Nullable; /** - * Static utility methods pertaining to {@link Map} instances (including instances of - * {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts - * {@link Lists}, {@link Sets} and {@link Queues}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Maps"> - * {@code Maps}</a>. + * Static utility methods pertaining to {@link Map} instances. Also see this + * class's counterparts {@link Lists} and {@link Sets}. * * @author Kevin Bourrillion * @author Mike Bostock @@ -76,60 +70,6 @@ import javax.annotation.Nullable; public final class Maps { private Maps() {} - private enum EntryFunction implements Function<Entry, Object> { - KEY { - @Override - @Nullable - public Object apply(Entry entry) { - return entry.getKey(); - } - }, - VALUE { - @Override - @Nullable - public Object apply(Entry entry) { - return entry.getValue(); - } - }; - } - - @SuppressWarnings("unchecked") - static <K> Function<Entry<K, ?>, K> keyFunction() { - return (Function) EntryFunction.KEY; - } - - static <V> Function<Entry<?, V>, V> valueFunction() { - return (Function) EntryFunction.VALUE; - } - - /** - * Returns an immutable map instance containing the given entries. - * Internally, the returned set will be backed by an {@link EnumMap}. - * - * <p>The iteration order of the returned map follows the enum's iteration - * order, not the order in which the elements appear in the given map. - * - * @param map the map to make an immutable copy of - * @return an immutable map containing those entries - * @since 14.0 - */ - @GwtCompatible(serializable = true) - @Beta - public static <K extends Enum<K>, V> ImmutableMap<K, V> immutableEnumMap( - Map<K, V> map) { - if (map instanceof ImmutableEnumMap) { - return (ImmutableEnumMap<K, V>) map; - } else if (map.isEmpty()) { - return ImmutableMap.of(); - } else { - for (Map.Entry<K, V> entry : map.entrySet()) { - checkNotNull(entry.getKey()); - checkNotNull(entry.getValue()); - } - return ImmutableEnumMap.asImmutable(new EnumMap<K, V>(map)); - } - } - /** * Creates a <i>mutable</i>, empty {@code HashMap} instance. * @@ -285,9 +225,9 @@ public final class Maps { * @param comparator the comparator to sort the keys with * @return a new, empty {@code TreeMap} */ - public static <C, K extends C, V> TreeMap<K, V> newTreeMap( - @Nullable Comparator<C> comparator) { - // Ideally, the extra type parameter "C" shouldn't be necessary. It is a + public static <K, V> TreeMap<K, V> newTreeMap( + @Nullable Comparator<? super K> comparator) { + // Ideally, the "? super" shouldn't be necessary. It is a // work-around of a compiler type inference quirk that prevents the // following code from being compiled: // Comparator<Class<?>> comparator = null; @@ -329,6 +269,38 @@ public final class Maps { } /** + * Returns a synchronized (thread-safe) bimap backed by the specified bimap. + * In order to guarantee serial access, it is critical that <b>all</b> access + * to the backing bimap is accomplished through the returned bimap. + * + * <p>It is imperative that the user manually synchronize on the returned map + * when accessing any of its collection views: <pre> {@code + * + * BiMap<Long, String> map = Maps.synchronizedBiMap( + * HashBiMap.<Long, String>create()); + * ... + * Set<Long> set = map.keySet(); // Needn't be in synchronized block + * ... + * synchronized (map) { // Synchronizing on map, not set! + * Iterator<Long> it = set.iterator(); // Must be in synchronized block + * while (it.hasNext()) { + * foo(it.next()); + * } + * }}</pre> + * + * Failure to follow this advice may result in non-deterministic behavior. + * + * <p>The returned bimap will be serializable if the specified bimap is + * serializable. + * + * @param bimap the bimap to be wrapped in a synchronized view + * @return a sychronized view of the specified bimap + */ + public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) { + return Synchronized.biMap(bimap, null); + } + + /** * Computes the difference between two maps. This difference is an immutable * snapshot of the state of the maps at the time this method is called. It * will never change, even if the maps change at a later time. @@ -352,7 +324,7 @@ public final class Maps { SortedMapDifference<K, V> result = difference(sortedLeft, right); return result; } - return difference(left, right, Equivalence.equals()); + return difference(left, right, Equivalences.equals()); } /** @@ -524,7 +496,7 @@ public final class Maps { } @Override public boolean equals(@Nullable Object object) { - if (object instanceof MapDifference.ValueDifference) { + if (object instanceof MapDifference.ValueDifference<?>) { MapDifference.ValueDifference<?> that = (MapDifference.ValueDifference<?>) object; return Objects.equal(this.left, that.leftValue()) @@ -561,6 +533,7 @@ public final class Maps { * @return the difference between the two maps * @since 11.0 */ + @Beta public static <K, V> SortedMapDifference<K, V> difference( SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) { checkNotNull(left); @@ -645,484 +618,6 @@ public final class Maps { } return (Comparator<E>) Ordering.natural(); } - - /** - * Returns a view of the set as a map, mapping keys from the set according to - * the specified function. - * - * <p>Specifically, for each {@code k} in the backing set, the returned map - * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code - * keySet}, {@code values}, and {@code entrySet} views of the returned map - * iterate in the same order as the backing set. - * - * <p>Modifications to the backing set are read through to the returned map. - * The returned map supports removal operations if the backing set does. - * Removal operations write through to the backing set. The returned map - * does not support put operations. - * - * <p><b>Warning</b>: If the function rejects {@code null}, caution is - * required to make sure the set does not contain {@code null}, because the - * view cannot stop {@code null} from being added to the set. - * - * <p><b>Warning:</b> This method assumes that for any instance {@code k} of - * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also - * of type {@code K}. Using a key type for which this may not hold, such as - * {@code ArrayList}, may risk a {@code ClassCastException} when calling - * methods on the resulting map view. - * - * @since 14.0 - */ - @Beta - public static <K, V> Map<K, V> asMap( - Set<K> set, Function<? super K, V> function) { - if (set instanceof SortedSet) { - return asMap((SortedSet<K>) set, function); - } else { - return new AsMapView<K, V>(set, function); - } - } - - /** - * Returns a view of the sorted set as a map, mapping keys from the set - * according to the specified function. - * - * <p>Specifically, for each {@code k} in the backing set, the returned map - * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code - * keySet}, {@code values}, and {@code entrySet} views of the returned map - * iterate in the same order as the backing set. - * - * <p>Modifications to the backing set are read through to the returned map. - * The returned map supports removal operations if the backing set does. - * Removal operations write through to the backing set. The returned map does - * not support put operations. - * - * <p><b>Warning</b>: If the function rejects {@code null}, caution is - * required to make sure the set does not contain {@code null}, because the - * view cannot stop {@code null} from being added to the set. - * - * <p><b>Warning:</b> This method assumes that for any instance {@code k} of - * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of - * type {@code K}. Using a key type for which this may not hold, such as - * {@code ArrayList}, may risk a {@code ClassCastException} when calling - * methods on the resulting map view. - * - * @since 14.0 - */ - @Beta - public static <K, V> SortedMap<K, V> asMap( - SortedSet<K> set, Function<? super K, V> function) { - return Platform.mapsAsMapSortedSet(set, function); - } - - static <K, V> SortedMap<K, V> asMapSortedIgnoreNavigable(SortedSet<K> set, - Function<? super K, V> function) { - return new SortedAsMapView<K, V>(set, function); - } - - /** - * Returns a view of the navigable set as a map, mapping keys from the set - * according to the specified function. - * - * <p>Specifically, for each {@code k} in the backing set, the returned map - * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code - * keySet}, {@code values}, and {@code entrySet} views of the returned map - * iterate in the same order as the backing set. - * - * <p>Modifications to the backing set are read through to the returned map. - * The returned map supports removal operations if the backing set does. - * Removal operations write through to the backing set. The returned map - * does not support put operations. - * - * <p><b>Warning</b>: If the function rejects {@code null}, caution is - * required to make sure the set does not contain {@code null}, because the - * view cannot stop {@code null} from being added to the set. - * - * <p><b>Warning:</b> This method assumes that for any instance {@code k} of - * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also - * of type {@code K}. Using a key type for which this may not hold, such as - * {@code ArrayList}, may risk a {@code ClassCastException} when calling - * methods on the resulting map view. - * - * @since 14.0 - */ - @Beta - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> asMap( - NavigableSet<K> set, Function<? super K, V> function) { - return new NavigableAsMapView<K, V>(set, function); - } - - private static class AsMapView<K, V> extends ImprovedAbstractMap<K, V> { - - private final Set<K> set; - final Function<? super K, V> function; - - Set<K> backingSet() { - return set; - } - - AsMapView(Set<K> set, Function<? super K, V> function) { - this.set = checkNotNull(set); - this.function = checkNotNull(function); - } - - @Override - public Set<K> keySet() { - // probably not worth caching - return removeOnlySet(backingSet()); - } - - @Override - public Collection<V> values() { - // probably not worth caching - return Collections2.transform(set, function); - } - - @Override - public int size() { - return backingSet().size(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return backingSet().contains(key); - } - - @Override - public V get(@Nullable Object key) { - if (backingSet().contains(key)) { - @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it - K k = (K) key; - return function.apply(k); - } else { - return null; - } - } - - @Override - public V remove(@Nullable Object key) { - if (backingSet().remove(key)) { - @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it - K k = (K) key; - return function.apply(k); - } else { - return null; - } - } - - @Override - public void clear() { - backingSet().clear(); - } - - @Override - protected Set<Entry<K, V>> createEntrySet() { - return new EntrySet<K, V>() { - @Override - Map<K, V> map() { - return AsMapView.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return asSetEntryIterator(backingSet(), function); - } - }; - } - } - - private static <K, V> Iterator<Entry<K, V>> asSetEntryIterator( - Set<K> set, final Function<? super K, V> function) { - return new TransformedIterator<K, Entry<K,V>>(set.iterator()) { - @Override - Entry<K, V> transform(K key) { - return Maps.immutableEntry(key, function.apply(key)); - } - }; - } - - private static class SortedAsMapView<K, V> extends AsMapView<K, V> - implements SortedMap<K, V> { - - SortedAsMapView(SortedSet<K> set, Function<? super K, V> function) { - super(set, function); - } - - @Override - SortedSet<K> backingSet() { - return (SortedSet<K>) super.backingSet(); - } - - @Override - public Comparator<? super K> comparator() { - return backingSet().comparator(); - } - - @Override - public Set<K> keySet() { - return removeOnlySortedSet(backingSet()); - } - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return asMap(backingSet().subSet(fromKey, toKey), function); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return asMap(backingSet().headSet(toKey), function); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return asMap(backingSet().tailSet(fromKey), function); - } - - @Override - public K firstKey() { - return backingSet().first(); - } - - @Override - public K lastKey() { - return backingSet().last(); - } - } - - @GwtIncompatible("NavigableMap") - private static final class NavigableAsMapView<K, V> - extends AbstractNavigableMap<K, V> { - /* - * Using AbstractNavigableMap is simpler than extending SortedAsMapView and rewriting all the - * NavigableMap methods. - */ - - private final NavigableSet<K> set; - private final Function<? super K, V> function; - - NavigableAsMapView(NavigableSet<K> ks, Function<? super K, V> vFunction) { - this.set = checkNotNull(ks); - this.function = checkNotNull(vFunction); - } - - @Override - public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return asMap(set.subSet(fromKey, fromInclusive, toKey, toInclusive), function); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return asMap(set.headSet(toKey, inclusive), function); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return asMap(set.tailSet(fromKey, inclusive), function); - } - - @Override - public Comparator<? super K> comparator() { - return set.comparator(); - } - - @Override - @Nullable - public V get(@Nullable Object key) { - if (set.contains(key)) { - @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it - K k = (K) key; - return function.apply(k); - } else { - return null; - } - } - - @Override - public void clear() { - set.clear(); - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return asSetEntryIterator(set, function); - } - - @Override - Iterator<Entry<K, V>> descendingEntryIterator() { - return descendingMap().entrySet().iterator(); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return removeOnlyNavigableSet(set); - } - - @Override - public int size() { - return set.size(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return asMap(set.descendingSet(), function); - } - } - - private static <E> Set<E> removeOnlySet(final Set<E> set) { - return new ForwardingSet<E>() { - @Override - protected Set<E> delegate() { - return set; - } - - @Override - public boolean add(E element) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection<? extends E> es) { - throw new UnsupportedOperationException(); - } - }; - } - - private static <E> SortedSet<E> removeOnlySortedSet(final SortedSet<E> set) { - return new ForwardingSortedSet<E>() { - @Override - protected SortedSet<E> delegate() { - return set; - } - - @Override - public boolean add(E element) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection<? extends E> es) { - throw new UnsupportedOperationException(); - } - - @Override - public SortedSet<E> headSet(E toElement) { - return removeOnlySortedSet(super.headSet(toElement)); - } - - @Override - public SortedSet<E> subSet(E fromElement, E toElement) { - return removeOnlySortedSet(super.subSet(fromElement, toElement)); - } - - @Override - public SortedSet<E> tailSet(E fromElement) { - return removeOnlySortedSet(super.tailSet(fromElement)); - } - }; - } - - @GwtIncompatible("NavigableSet") - private static <E> NavigableSet<E> removeOnlyNavigableSet(final NavigableSet<E> set) { - return new ForwardingNavigableSet<E>() { - @Override - protected NavigableSet<E> delegate() { - return set; - } - - @Override - public boolean add(E element) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection<? extends E> es) { - throw new UnsupportedOperationException(); - } - - @Override - public SortedSet<E> headSet(E toElement) { - return removeOnlySortedSet(super.headSet(toElement)); - } - - @Override - public SortedSet<E> subSet(E fromElement, E toElement) { - return removeOnlySortedSet( - super.subSet(fromElement, toElement)); - } - - @Override - public SortedSet<E> tailSet(E fromElement) { - return removeOnlySortedSet(super.tailSet(fromElement)); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return removeOnlyNavigableSet(super.headSet(toElement, inclusive)); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return removeOnlyNavigableSet(super.tailSet(fromElement, inclusive)); - } - - @Override - public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, - E toElement, boolean toInclusive) { - return removeOnlyNavigableSet(super.subSet( - fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<E> descendingSet() { - return removeOnlyNavigableSet(super.descendingSet()); - } - }; - } - - /** - * Returns an immutable map for which the given {@code keys} are mapped to - * values by the given function in the order they appear in the original - * iterable. If {@code keys} contains duplicate elements, the returned map - * will contain each distinct key once in the order it first appears in - * {@code keys}. - * - * @throws NullPointerException if any element of {@code keys} is - * {@code null}, or if {@code valueFunction} produces {@code null} - * for any key - * @since 14.0 - */ - @Beta - public static <K, V> ImmutableMap<K, V> toMap(Iterable<K> keys, - Function<? super K, V> valueFunction) { - return toMap(keys.iterator(), valueFunction); - } - - /** - * Returns an immutable map for which the given {@code keys} are mapped to - * values by the given function in the order they appear in the original - * iterator. If {@code keys} contains duplicate elements, the returned map - * will contain each distinct key once in the order it first appears in - * {@code keys}. - * - * @throws NullPointerException if any element of {@code keys} is - * {@code null}, or if {@code valueFunction} produces {@code null} - * for any key - * @since 14.0 - */ - @Beta - public static <K, V> ImmutableMap<K, V> toMap(Iterator<K> keys, - Function<? super K, V> valueFunction) { - checkNotNull(valueFunction); - // Using LHM instead of a builder so as not to fail on duplicate keys - Map<K, V> builder = newLinkedHashMap(); - while (keys.hasNext()) { - K key = keys.next(); - builder.put(key, valueFunction.apply(key)); - } - return ImmutableMap.copyOf(builder); - } - /** * Returns an immutable map for which the {@link Map#values} are the given * elements in the given order, and each key is the product of invoking a @@ -1143,6 +638,24 @@ public final class Maps { } /** + * <b>Deprecated.</b> + * + * @since 10.0 + * @deprecated use {@link #uniqueIndex(Iterator, Function)} by casting {@code + * values} to {@code Iterator<V>}, or better yet, by implementing only + * {@code Iterator} and not {@code Iterable}. <b>This method is scheduled + * for deletion in March 2012.</b> + */ + @Beta + @Deprecated + public static <K, V, I extends Object & Iterable<V> & Iterator<V>> + ImmutableMap<K, V> uniqueIndex( + I values, Function<? super V, K> keyFunction) { + Iterable<V> valuesIterable = checkNotNull(values); + return uniqueIndex(valuesIterable, keyFunction); + } + + /** * Returns an immutable map for which the {@link Map#values} are the given * elements in the given order, and each key is the product of invoking a * supplied function on its corresponding value. @@ -1330,38 +843,6 @@ public final class Maps { } /** - * Returns a synchronized (thread-safe) bimap backed by the specified bimap. - * In order to guarantee serial access, it is critical that <b>all</b> access - * to the backing bimap is accomplished through the returned bimap. - * - * <p>It is imperative that the user manually synchronize on the returned map - * when accessing any of its collection views: <pre> {@code - * - * BiMap<Long, String> map = Maps.synchronizedBiMap( - * HashBiMap.<Long, String>create()); - * ... - * Set<Long> set = map.keySet(); // Needn't be in synchronized block - * ... - * synchronized (map) { // Synchronizing on map, not set! - * Iterator<Long> it = set.iterator(); // Must be in synchronized block - * while (it.hasNext()) { - * foo(it.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned bimap will be serializable if the specified bimap is - * serializable. - * - * @param bimap the bimap to be wrapped in a synchronized view - * @return a sychronized view of the specified bimap - */ - public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) { - return Synchronized.biMap(bimap, null); - } - - /** * Returns an unmodifiable view of the specified bimap. This method allows * modules to provide users with "read-only" access to internal bimaps. Query * operations on the returned bimap "read through" to the specified bimap, and @@ -1384,7 +865,7 @@ public final class Maps { extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable { final Map<K, V> unmodifiableMap; final BiMap<? extends K, ? extends V> delegate; - BiMap<V, K> inverse; + transient BiMap<V, K> inverse; transient Set<V> values; UnmodifiableBiMap(BiMap<? extends K, ? extends V> delegate, @@ -1458,8 +939,16 @@ public final class Maps { * a view, copy the returned map into a new map of your choosing. */ public static <K, V1, V2> Map<K, V2> transformValues( - Map<K, V1> fromMap, Function<? super V1, V2> function) { - return transformEntries(fromMap, asEntryTransformer(function)); + Map<K, V1> fromMap, final Function<? super V1, V2> function) { + checkNotNull(function); + EntryTransformer<K, V1, V2> transformer = + new EntryTransformer<K, V1, V2>() { + @Override + public V2 transformEntry(K key, V1 value) { + return function.apply(value); + } + }; + return transformEntries(fromMap, transformer); } /** @@ -1501,67 +990,18 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V1, V2> SortedMap<K, V2> transformValues( - SortedMap<K, V1> fromMap, Function<? super V1, V2> function) { - return transformEntries(fromMap, asEntryTransformer(function)); - } - - /** - * Returns a view of a navigable map where each value is transformed by a - * function. All other properties of the map, such as iteration order, are - * left intact. For example, the code: <pre> {@code - * - * NavigableMap<String, Integer> map = Maps.newTreeMap(); - * map.put("a", 4); - * map.put("b", 9); - * Function<Integer, Double> sqrt = - * new Function<Integer, Double>() { - * public Double apply(Integer in) { - * return Math.sqrt((int) in); - * } - * }; - * NavigableMap<String, Double> transformed = - * Maps.transformNavigableValues(map, sqrt); - * System.out.println(transformed);}</pre> - * - * ... prints {@code {a=2.0, b=3.0}}. - * - * Changes in the underlying map are reflected in this view. - * Conversely, this view supports removal operations, and these are reflected - * in the underlying map. - * - * <p>It's acceptable for the underlying map to contain null keys, and even - * null values provided that the function is capable of accepting null input. - * The transformed map might contain null values, if the function sometimes - * gives a null result. - * - * <p>The returned map is not thread-safe or serializable, even if the - * underlying map is. - * - * <p>The function is applied lazily, invoked when needed. This is necessary - * for the returned map to be a view, but it means that the function will be - * applied many times for bulk operations like {@link Map#containsValue} and - * {@code Map.toString()}. For this to perform well, {@code function} should - * be fast. To avoid lazy evaluation when the returned map doesn't need to be - * a view, copy the returned map into a new map of your choosing. - * - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V1, V2> NavigableMap<K, V2> transformValues( - NavigableMap<K, V1> fromMap, Function<? super V1, V2> function) { - return transformEntries(fromMap, asEntryTransformer(function)); - } - - private static <K, V1, V2> EntryTransformer<K, V1, V2> - asEntryTransformer(final Function<? super V1, V2> function) { + SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) { checkNotNull(function); - return new EntryTransformer<K, V1, V2>() { - @Override - public V2 transformEntry(K key, V1 value) { - return function.apply(value); - } - }; + EntryTransformer<K, V1, V2> transformer = + new EntryTransformer<K, V1, V2>() { + @Override + public V2 transformEntry(K key, V1 value) { + return function.apply(value); + } + }; + return transformEntries(fromMap, transformer); } /** @@ -1676,74 +1116,9 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V1, V2> SortedMap<K, V2> transformEntries( - SortedMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - return Platform.mapsTransformEntriesSortedMap(fromMap, transformer); - } - - /** - * Returns a view of a navigable map whose values are derived from the - * original navigable map's entries. In contrast to {@link - * #transformValues}, this method's entry-transformation logic may - * depend on the key as well as the value. - * - * <p>All other properties of the transformed map, such as iteration order, - * are left intact. For example, the code: <pre> {@code - * - * NavigableMap<String, Boolean> options = Maps.newTreeMap(); - * options.put("verbose", false); - * options.put("sort", true); - * EntryTransformer<String, Boolean, String> flagPrefixer = - * new EntryTransformer<String, Boolean, String>() { - * public String transformEntry(String key, Boolean value) { - * return value ? key : ("yes" + key); - * } - * }; - * NavigableMap<String, String> transformed = - * LabsMaps.transformNavigableEntries(options, flagPrefixer); - * System.out.println(transformed);}</pre> - * - * ... prints {@code {sort=yessort, verbose=verbose}}. - * - * <p>Changes in the underlying map are reflected in this view. - * Conversely, this view supports removal operations, and these are reflected - * in the underlying map. - * - * <p>It's acceptable for the underlying map to contain null keys and null - * values provided that the transformer is capable of accepting null inputs. - * The transformed map might contain null values if the transformer sometimes - * gives a null result. - * - * <p>The returned map is not thread-safe or serializable, even if the - * underlying map is. - * - * <p>The transformer is applied lazily, invoked when needed. This is - * necessary for the returned map to be a view, but it means that the - * transformer will be applied many times for bulk operations like {@link - * Map#containsValue} and {@link Object#toString}. For this to perform well, - * {@code transformer} should be fast. To avoid lazy evaluation when the - * returned map doesn't need to be a view, copy the returned map into a new - * map of your choosing. - * - * <p><b>Warning:</b> This method assumes that for any instance {@code k} of - * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies - * that {@code k2} is also of type {@code K}. Using an {@code - * EntryTransformer} key type for which this may not hold, such as {@code - * ArrayList}, may risk a {@code ClassCastException} when calling methods on - * the transformed map. - * - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V1, V2> NavigableMap<K, V2> transformEntries( - final NavigableMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - return new TransformedEntriesNavigableMap<K, V1, V2>(fromMap, transformer); - } - - static <K, V1, V2> SortedMap<K, V2> transformEntriesIgnoreNavigable( - SortedMap<K, V1> fromMap, + final SortedMap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) { return new TransformedEntriesSortedMap<K, V1, V2>(fromMap, transformer); } @@ -1835,23 +1210,17 @@ public final class Maps { } @Override public Iterator<Entry<K, V2>> iterator() { - return new TransformedIterator<Entry<K, V1>, Entry<K, V2>>( - fromMap.entrySet().iterator()) { - @Override - Entry<K, V2> transform(final Entry<K, V1> entry) { - return new AbstractMapEntry<K, V2>() { - @Override - public K getKey() { - return entry.getKey(); + final Iterator<Entry<K, V1>> backingIterator = + fromMap.entrySet().iterator(); + return Iterators.transform(backingIterator, + new Function<Entry<K, V1>, Entry<K, V2>>() { + @Override public Entry<K, V2> apply(Entry<K, V1> entry) { + return immutableEntry( + entry.getKey(), + transformer.transformEntry(entry.getKey(), + entry.getValue())); } - - @Override - public V2 getValue() { - return transformer.transformEntry(entry.getKey(), entry.getValue()); - } - }; - } - }; + }); } }; } @@ -1909,144 +1278,7 @@ public final class Maps { @Override public SortedMap<K, V2> tailMap(K fromKey) { return transformEntries(fromMap().tailMap(fromKey), transformer); } - } - - @GwtIncompatible("NavigableMap") - private static class TransformedEntriesNavigableMap<K, V1, V2> - extends TransformedEntriesSortedMap<K, V1, V2> - implements NavigableMap<K, V2> { - - TransformedEntriesNavigableMap(NavigableMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - super(fromMap, transformer); - } - - @Override public Entry<K, V2> ceilingEntry(K key) { - return transformEntry(fromMap().ceilingEntry(key)); - } - - @Override public K ceilingKey(K key) { - return fromMap().ceilingKey(key); - } - - @Override public NavigableSet<K> descendingKeySet() { - return fromMap().descendingKeySet(); - } - - @Override public NavigableMap<K, V2> descendingMap() { - return transformEntries(fromMap().descendingMap(), transformer); - } - - @Override public Entry<K, V2> firstEntry() { - return transformEntry(fromMap().firstEntry()); - } - @Override public Entry<K, V2> floorEntry(K key) { - return transformEntry(fromMap().floorEntry(key)); - } - - @Override public K floorKey(K key) { - return fromMap().floorKey(key); - } - - @Override public NavigableMap<K, V2> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override public NavigableMap<K, V2> headMap(K toKey, boolean inclusive) { - return transformEntries( - fromMap().headMap(toKey, inclusive), transformer); - } - - @Override public Entry<K, V2> higherEntry(K key) { - return transformEntry(fromMap().higherEntry(key)); - } - - @Override public K higherKey(K key) { - return fromMap().higherKey(key); - } - - @Override public Entry<K, V2> lastEntry() { - return transformEntry(fromMap().lastEntry()); - } - - @Override public Entry<K, V2> lowerEntry(K key) { - return transformEntry(fromMap().lowerEntry(key)); - } - @Override public K lowerKey(K key) { - return fromMap().lowerKey(key); - } - - @Override public NavigableSet<K> navigableKeySet() { - return fromMap().navigableKeySet(); - } - - @Override public Entry<K, V2> pollFirstEntry() { - return transformEntry(fromMap().pollFirstEntry()); - } - - @Override public Entry<K, V2> pollLastEntry() { - return transformEntry(fromMap().pollLastEntry()); - } - - @Override public NavigableMap<K, V2> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return transformEntries( - fromMap().subMap(fromKey, fromInclusive, toKey, toInclusive), - transformer); - } - - @Override public NavigableMap<K, V2> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override public NavigableMap<K, V2> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override public NavigableMap<K, V2> tailMap(K fromKey, boolean inclusive) { - return transformEntries( - fromMap().tailMap(fromKey, inclusive), transformer); - } - - private Entry<K, V2> transformEntry(Entry<K, V1> entry) { - if (entry == null) { - return null; - } - K key = entry.getKey(); - V2 v2 = transformer.transformEntry(key, entry.getValue()); - return Maps.immutableEntry(key, v2); - } - - @Override protected NavigableMap<K, V1> fromMap() { - return (NavigableMap<K, V1>) super.fromMap(); - } - } - - private static final class KeyPredicate<K, V> implements Predicate<Entry<K, V>> { - private final Predicate<? super K> keyPredicate; - - KeyPredicate(Predicate<? super K> keyPredicate) { - this.keyPredicate = checkNotNull(keyPredicate); - } - - @Override - public boolean apply(Entry<K, V> input) { - return keyPredicate.apply(input.getKey()); - } - } - - private static final class ValuePredicate<K, V> implements Predicate<Entry<K, V>> { - private final Predicate<? super V> valuePredicate; - - ValuePredicate(Predicate<? super V> valuePredicate) { - this.valuePredicate = checkNotNull(valuePredicate); - } - - @Override - public boolean apply(Entry<K, V> input) { - return valuePredicate.apply(input.getValue()); - } } /** @@ -2081,11 +1313,15 @@ public final class Maps { Map<K, V> unfiltered, final Predicate<? super K> keyPredicate) { if (unfiltered instanceof SortedMap) { return filterKeys((SortedMap<K, V>) unfiltered, keyPredicate); - } else if (unfiltered instanceof BiMap) { - return filterKeys((BiMap<K, V>) unfiltered, keyPredicate); } checkNotNull(keyPredicate); - Predicate<Entry<K, V>> entryPredicate = new KeyPredicate<K, V>(keyPredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; return (unfiltered instanceof AbstractFilteredMap) ? filterFiltered((AbstractFilteredMap<K, V>) unfiltered, entryPredicate) : new FilteredKeyMap<K, V>( @@ -2122,80 +1358,19 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V> SortedMap<K, V> filterKeys( SortedMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { - // TODO(user): Return a subclass of Maps.FilteredKeyMap for slightly better - // performance. - return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate)); - } - - /** - * Returns a navigable map containing the mappings in {@code unfiltered} whose - * keys satisfy a predicate. The returned map is a live view of {@code - * unfiltered}; changes to one affect the other. - * - * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code - * values()} views have iterators that don't support {@code remove()}, but all - * other methods are supported by the map and its views. When given a key that - * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} - * methods throw an {@link IllegalArgumentException}. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called - * on the filtered map or its views, only mappings whose keys satisfy the - * filter will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map and use the copy. - * - * <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with - * equals</i>, as documented at {@link Predicate#apply}. Do not provide a - * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is - * inconsistent with equals. - * - * @since 14.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterKeys( - NavigableMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { - // TODO(user): Return a subclass of Maps.FilteredKeyMap for slightly better + // TODO: Return a subclass of Maps.FilteredKeyMap for slightly better // performance. - return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate)); - } - - /** - * Returns a bimap containing the mappings in {@code unfiltered} whose keys satisfy a predicate. - * The returned bimap is a live view of {@code unfiltered}; changes to one affect the other. - * - * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have - * iterators that don't support {@code remove()}, but all other methods are supported by the - * bimap and its views. When given a key that doesn't satisfy the predicate, the bimap's {@code - * put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link - * IllegalArgumentException}. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * bimap or its views, only mappings that satisfy the filter will be removed from the underlying - * bimap. - * - * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every key in - * the underlying bimap and determine which satisfy the filter. When a live view is <i>not</i> - * needed, it may be faster to copy the filtered bimap and use the copy. - * - * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as - * documented at {@link Predicate#apply}. - * - * @since 14.0 - */ - public static <K, V> BiMap<K, V> filterKeys( - BiMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { checkNotNull(keyPredicate); - return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate)); + Predicate<Entry<K, V>> entryPredicate = new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2231,10 +1406,16 @@ public final class Maps { Map<K, V> unfiltered, final Predicate<? super V> valuePredicate) { if (unfiltered instanceof SortedMap) { return filterValues((SortedMap<K, V>) unfiltered, valuePredicate); - } else if (unfiltered instanceof BiMap) { - return filterValues((BiMap<K, V>) unfiltered, valuePredicate); } - return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate)); + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2268,79 +1449,18 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V> SortedMap<K, V> filterValues( SortedMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { - return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate)); - } - - /** - * Returns a navigable map containing the mappings in {@code unfiltered} whose - * values satisfy a predicate. The returned map is a live view of {@code - * unfiltered}; changes to one affect the other. - * - * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code - * values()} views have iterators that don't support {@code remove()}, but all - * other methods are supported by the map and its views. When given a value - * that doesn't satisfy the predicate, the map's {@code put()}, {@code - * putAll()}, and {@link Entry#setValue} methods throw an {@link - * IllegalArgumentException}. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called - * on the filtered map or its views, only mappings whose values satisfy the - * filter will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map and use the copy. - * - * <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with - * equals</i>, as documented at {@link Predicate#apply}. Do not provide a - * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is - * inconsistent with equals. - * - * @since 14.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterValues( - NavigableMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { - return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate)); - } - - /** - * Returns a bimap containing the mappings in {@code unfiltered} whose values satisfy a - * predicate. The returned bimap is a live view of {@code unfiltered}; changes to one affect the - * other. - * - * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have - * iterators that don't support {@code remove()}, but all other methods are supported by the - * bimap and its views. When given a value that doesn't satisfy the predicate, the bimap's - * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link - * IllegalArgumentException}. Similarly, the map's entries have a {@link Entry#setValue} method - * that throws an {@link IllegalArgumentException} when the provided value doesn't satisfy the - * predicate. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * bimap or its views, only mappings that satisfy the filter will be removed from the underlying - * bimap. - * - * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every value in - * the underlying bimap and determine which satisfy the filter. When a live view is <i>not</i> - * needed, it may be faster to copy the filtered bimap and use the copy. - * - * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as - * documented at {@link Predicate#apply}. - * - * @since 14.0 - */ - public static <K, V> BiMap<K, V> filterValues( - BiMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { - return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate)); + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2376,8 +1496,6 @@ public final class Maps { Map<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { if (unfiltered instanceof SortedMap) { return filterEntries((SortedMap<K, V>) unfiltered, entryPredicate); - } else if (unfiltered instanceof BiMap) { - return filterEntries((BiMap<K, V>) unfiltered, entryPredicate); } checkNotNull(entryPredicate); return (unfiltered instanceof AbstractFilteredMap) @@ -2416,15 +1534,10 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V> SortedMap<K, V> filterEntries( SortedMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { - return Platform.mapsFilterSortedMap(unfiltered, entryPredicate); - } - - static <K, V> SortedMap<K, V> filterSortedIgnoreNavigable( - SortedMap<K, V> unfiltered, - Predicate<? super Entry<K, V>> entryPredicate) { checkNotNull(entryPredicate); return (unfiltered instanceof FilteredEntrySortedMap) ? filterFiltered((FilteredEntrySortedMap<K, V>) unfiltered, entryPredicate) @@ -2432,83 +1545,6 @@ public final class Maps { } /** - * Returns a sorted map containing the mappings in {@code unfiltered} that - * satisfy a predicate. The returned map is a live view of {@code unfiltered}; - * changes to one affect the other. - * - * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code - * values()} views have iterators that don't support {@code remove()}, but all - * other methods are supported by the map and its views. When given a - * key/value pair that doesn't satisfy the predicate, the map's {@code put()} - * and {@code putAll()} methods throw an {@link IllegalArgumentException}. - * Similarly, the map's entries have a {@link Entry#setValue} method that - * throws an {@link IllegalArgumentException} when the existing key and the - * provided value don't satisfy the predicate. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called - * on the filtered map or its views, only mappings that satisfy the filter - * will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map and use the copy. - * - * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with - * equals</i>, as documented at {@link Predicate#apply}. - * - * @since 14.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterEntries( - NavigableMap<K, V> unfiltered, - Predicate<? super Entry<K, V>> entryPredicate) { - checkNotNull(entryPredicate); - return (unfiltered instanceof FilteredEntryNavigableMap) - ? filterFiltered((FilteredEntryNavigableMap<K, V>) unfiltered, entryPredicate) - : new FilteredEntryNavigableMap<K, V>(checkNotNull(unfiltered), entryPredicate); - } - - /** - * Returns a bimap containing the mappings in {@code unfiltered} that satisfy a predicate. The - * returned bimap is a live view of {@code unfiltered}; changes to one affect the other. - * - * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have - * iterators that don't support {@code remove()}, but all other methods are supported by the bimap - * and its views. When given a key/value pair that doesn't satisfy the predicate, the bimap's - * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an - * {@link IllegalArgumentException}. Similarly, the map's entries have an {@link Entry#setValue} - * method that throws an {@link IllegalArgumentException} when the existing key and the provided - * value don't satisfy the predicate. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * bimap or its views, only mappings that satisfy the filter will be removed from the underlying - * bimap. - * - * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying bimap and determine which satisfy the filter. When a live - * view is <i>not</i> needed, it may be faster to copy the filtered bimap and use the copy. - * - * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as - * documented at {@link Predicate#apply}. - * - * @since 14.0 - */ - public static <K, V> BiMap<K, V> filterEntries( - BiMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { - checkNotNull(unfiltered); - checkNotNull(entryPredicate); - return (unfiltered instanceof FilteredEntryBiMap) - ? filterFiltered((FilteredEntryBiMap<K, V>) unfiltered, entryPredicate) - : new FilteredEntryBiMap<K, V>(unfiltered, entryPredicate); - } - - /** * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when * filtering a filtered map. */ @@ -2653,7 +1689,6 @@ public final class Maps { } } } - /** * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when * filtering a filtered sorted map. @@ -2714,251 +1749,6 @@ public final class Maps { } } - /** - * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when - * filtering a filtered navigable map. - */ - @GwtIncompatible("NavigableMap") - private static <K, V> NavigableMap<K, V> filterFiltered( - FilteredEntryNavigableMap<K, V> map, - Predicate<? super Entry<K, V>> entryPredicate) { - Predicate<Entry<K, V>> predicate - = Predicates.and(map.predicate, entryPredicate); - return new FilteredEntryNavigableMap<K, V>(map.sortedMap(), predicate); - } - - @GwtIncompatible("NavigableMap") - private static class FilteredEntryNavigableMap<K, V> extends FilteredEntrySortedMap<K, V> - implements NavigableMap<K, V> { - - FilteredEntryNavigableMap( - NavigableMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { - super(unfiltered, entryPredicate); - } - - @Override - NavigableMap<K, V> sortedMap() { - return (NavigableMap<K, V>) super.sortedMap(); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return Iterables.getFirst(entrySet(), null); - } - - @Override - public Entry<K, V> lastEntry() { - return Iterables.getFirst(descendingMap().entrySet(), null); - } - - @Override - public Entry<K, V> pollFirstEntry() { - return pollFirstSatisfyingEntry(sortedMap().entrySet().iterator()); - } - - @Override - public Entry<K, V> pollLastEntry() { - return pollFirstSatisfyingEntry(sortedMap().descendingMap().entrySet().iterator()); - } - - @Nullable - Entry<K, V> pollFirstSatisfyingEntry(Iterator<Entry<K, V>> entryIterator) { - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (predicate.apply(entry)) { - entryIterator.remove(); - return entry; - } - } - return null; - } - - @Override - public NavigableMap<K, V> descendingMap() { - return filterEntries(sortedMap().descendingMap(), predicate); - } - - @Override - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - NavigableSet<K> createKeySet() { - return new NavigableKeySet<K, V>(this) { - @Override - public boolean removeAll(Collection<?> c) { - boolean changed = false; - Iterator<Entry<K, V>> entryIterator = sortedMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (c.contains(entry.getKey()) && predicate.apply(entry)) { - entryIterator.remove(); - changed = true; - } - } - return changed; - } - - @Override - public boolean retainAll(Collection<?> c) { - boolean changed = false; - Iterator<Entry<K, V>> entryIterator = sortedMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (!c.contains(entry.getKey()) && predicate.apply(entry)) { - entryIterator.remove(); - changed = true; - } - } - return changed; - } - }; - } - - @Override - public NavigableSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return filterEntries( - sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive), predicate); - } - - @Override - public NavigableMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return filterEntries(sortedMap().headMap(toKey, inclusive), predicate); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return filterEntries(sortedMap().tailMap(fromKey, inclusive), predicate); - } - } - - /** - * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when - * filtering a filtered map. - */ - private static <K, V> BiMap<K, V> filterFiltered( - FilteredEntryBiMap<K, V> map, Predicate<? super Entry<K, V>> entryPredicate) { - Predicate<Entry<K, V>> predicate = Predicates.and(map.predicate, entryPredicate); - return new FilteredEntryBiMap<K, V>(map.unfiltered(), predicate); - } - - static final class FilteredEntryBiMap<K, V> extends FilteredEntryMap<K, V> - implements BiMap<K, V> { - private final BiMap<V, K> inverse; - - private static <K, V> Predicate<Entry<V, K>> inversePredicate( - final Predicate<? super Entry<K, V>> forwardPredicate) { - return new Predicate<Entry<V, K>>() { - @Override - public boolean apply(Entry<V, K> input) { - return forwardPredicate.apply( - Maps.immutableEntry(input.getValue(), input.getKey())); - } - }; - } - - FilteredEntryBiMap(BiMap<K, V> delegate, - Predicate<? super Entry<K, V>> predicate) { - super(delegate, predicate); - this.inverse = new FilteredEntryBiMap<V, K>( - delegate.inverse(), inversePredicate(predicate), this); - } - - private FilteredEntryBiMap( - BiMap<K, V> delegate, Predicate<? super Entry<K, V>> predicate, - BiMap<V, K> inverse) { - super(delegate, predicate); - this.inverse = inverse; - } - - BiMap<K, V> unfiltered() { - return (BiMap<K, V>) unfiltered; - } - - @Override - public V forcePut(@Nullable K key, @Nullable V value) { - checkArgument(predicate.apply(Maps.immutableEntry(key, value))); - return unfiltered().forcePut(key, value); - } - - @Override - public BiMap<V, K> inverse() { - return inverse; - } - - @Override - public Set<V> values() { - return inverse.keySet(); - } - } - private static class FilteredKeyMap<K, V> extends AbstractFilteredMap<K, V> { Predicate<? super K> keyPredicate; @@ -3050,14 +1840,10 @@ public final class Maps { @Override public Set<K> keySet() { Set<K> result = keySet; - return (result == null) ? keySet = createKeySet() : result; + return (result == null) ? keySet = new KeySet() : result; } - Set<K> createKeySet() { - return new KeySet(); - } - - private class KeySet extends Sets.ImprovedAbstractSet<K> { + private class KeySet extends AbstractSet<K> { @Override public Iterator<K> iterator() { final Iterator<Entry<K, V>> iterator = filteredEntrySet.iterator(); return new UnmodifiableIterator<K>() { @@ -3093,13 +1879,22 @@ public final class Maps { return false; } + @Override public boolean removeAll(Collection<?> collection) { + checkNotNull(collection); // for GWT + boolean changed = false; + for (Object obj : collection) { + changed |= remove(obj); + } + return changed; + } + @Override public boolean retainAll(Collection<?> collection) { checkNotNull(collection); // for GWT boolean changed = false; Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator(); while (iterator.hasNext()) { Entry<K, V> entry = iterator.next(); - if (predicate.apply(entry) && !collection.contains(entry.getKey())) { + if (!collection.contains(entry.getKey()) && predicate.apply(entry)) { iterator.remove(); changed = true; } @@ -3119,224 +1914,6 @@ public final class Maps { } /** - * Returns an unmodifiable view of the specified navigable map. Query operations on the returned - * map read through to the specified map, and attempts to modify the returned map, whether direct - * or via its views, result in an {@code UnsupportedOperationException}. - * - * <p>The returned navigable map will be serializable if the specified navigable map is - * serializable. - * - * @param map the navigable map for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified navigable map - * @since 12.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> unmodifiableNavigableMap(NavigableMap<K, V> map) { - checkNotNull(map); - if (map instanceof UnmodifiableNavigableMap) { - return map; - } else { - return new UnmodifiableNavigableMap<K, V>(map); - } - } - - @Nullable private static <K, V> Entry<K, V> unmodifiableOrNull(@Nullable Entry<K, V> entry) { - return (entry == null) ? null : Maps.unmodifiableEntry(entry); - } - - @GwtIncompatible("NavigableMap") - static class UnmodifiableNavigableMap<K, V> - extends ForwardingSortedMap<K, V> implements NavigableMap<K, V>, Serializable { - private final NavigableMap<K, V> delegate; - - UnmodifiableNavigableMap(NavigableMap<K, V> delegate) { - this.delegate = delegate; - } - - @Override - protected SortedMap<K, V> delegate() { - return Collections.unmodifiableSortedMap(delegate); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return unmodifiableOrNull(delegate.lowerEntry(key)); - } - - @Override - public K lowerKey(K key) { - return delegate.lowerKey(key); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return unmodifiableOrNull(delegate.floorEntry(key)); - } - - @Override - public K floorKey(K key) { - return delegate.floorKey(key); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return unmodifiableOrNull(delegate.ceilingEntry(key)); - } - - @Override - public K ceilingKey(K key) { - return delegate.ceilingKey(key); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return unmodifiableOrNull(delegate.higherEntry(key)); - } - - @Override - public K higherKey(K key) { - return delegate.higherKey(key); - } - - @Override - public Entry<K, V> firstEntry() { - return unmodifiableOrNull(delegate.firstEntry()); - } - - @Override - public Entry<K, V> lastEntry() { - return unmodifiableOrNull(delegate.lastEntry()); - } - - @Override - public final Entry<K, V> pollFirstEntry() { - throw new UnsupportedOperationException(); - } - - @Override - public final Entry<K, V> pollLastEntry() { - throw new UnsupportedOperationException(); - } - - private transient UnmodifiableNavigableMap<K, V> descendingMap; - - @Override - public NavigableMap<K, V> descendingMap() { - UnmodifiableNavigableMap<K, V> result = descendingMap; - if (result == null) { - descendingMap = result = new UnmodifiableNavigableMap<K, V>(delegate.descendingMap()); - result.descendingMap = this; - } - return result; - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return Sets.unmodifiableNavigableSet(delegate.navigableKeySet()); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return Sets.unmodifiableNavigableSet(delegate.descendingKeySet()); - } - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public - NavigableMap<K, V> - subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return Maps.unmodifiableNavigableMap(delegate.subMap( - fromKey, - fromInclusive, - toKey, - toInclusive)); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return Maps.unmodifiableNavigableMap(delegate.headMap(toKey, inclusive)); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return Maps.unmodifiableNavigableMap(delegate.tailMap(fromKey, inclusive)); - } - } - - /** - * Returns a synchronized (thread-safe) navigable map backed by the specified - * navigable map. In order to guarantee serial access, it is critical that - * <b>all</b> access to the backing navigable map is accomplished - * through the returned navigable map (or its views). - * - * <p>It is imperative that the user manually synchronize on the returned - * navigable map when iterating over any of its collection views, or the - * collections views of any of its {@code descendingMap}, {@code subMap}, - * {@code headMap} or {@code tailMap} views. <pre> {@code - * - * NavigableMap<K, V> map = synchronizedNavigableMap(new TreeMap<K, V>()); - * - * // Needn't be in synchronized block - * NavigableSet<K> set = map.navigableKeySet(); - * - * synchronized (map) { // Synchronizing on map, not set! - * Iterator<K> it = set.iterator(); // Must be in synchronized block - * while (it.hasNext()){ - * foo(it.next()); - * } - * }}</pre> - * - * or: <pre> {@code - * - * NavigableMap<K, V> map = synchronizedNavigableMap(new TreeMap<K, V>()); - * NavigableMap<K, V> map2 = map.subMap(foo, false, bar, true); - * - * // Needn't be in synchronized block - * NavigableSet<K> set2 = map2.descendingKeySet(); - * - * synchronized (map) { // Synchronizing on map, not map2 or set2! - * Iterator<K> it = set2.iterator(); // Must be in synchronized block - * while (it.hasNext()){ - * foo(it.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned navigable map will be serializable if the specified - * navigable map is serializable. - * - * @param navigableMap the navigable map to be "wrapped" in a synchronized - * navigable map. - * @return a synchronized view of the specified navigable map. - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> synchronizedNavigableMap( - NavigableMap<K, V> navigableMap) { - return Synchronized.navigableMap(navigableMap); - } - - /** * {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code * entrySet().isEmpty()} instead of {@code size() == 0} to speed up * implementations where {@code size()} is O(n), and it delegates the {@code @@ -3344,7 +1921,7 @@ public final class Maps { * implementation. */ @GwtCompatible - abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> { + static abstract class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> { /** * Creates the entry set to be returned by {@link #entrySet()}. This method * is invoked at most once on a given map, at the time when {@code entrySet} @@ -3381,7 +1958,7 @@ public final class Maps { @Override public Collection<V> values() { Collection<V> result = values; if (result == null) { - return values = new Values<K, V>() { + return values = new Values<K, V>(){ @Override Map<K, V> map() { return ImprovedAbstractMap.this; } @@ -3389,6 +1966,17 @@ public final class Maps { } return result; } + + /** + * Returns {@code true} if this map contains no key-value mappings. + * + * <p>The implementation returns {@code entrySet().isEmpty()}. + * + * @return {@code true} if this map contains no key-value mappings + */ + @Override public boolean isEmpty() { + return entrySet().isEmpty(); + } } static final MapJoiner STANDARD_JOINER = @@ -3396,46 +1984,25 @@ public final class Maps { /** * Delegates to {@link Map#get}. Returns {@code null} on {@code - * ClassCastException} and {@code NullPointerException}. + * ClassCastException}. */ static <V> V safeGet(Map<?, V> map, Object key) { - checkNotNull(map); try { return map.get(key); } catch (ClassCastException e) { return null; - } catch (NullPointerException e) { - return null; } } /** * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code - * ClassCastException} and {@code NullPointerException}. + * ClassCastException} */ static boolean safeContainsKey(Map<?, ?> map, Object key) { - checkNotNull(map); try { return map.containsKey(key); } catch (ClassCastException e) { return false; - } catch (NullPointerException e) { - return false; - } - } - - /** - * Delegates to {@link Map#remove}. Returns {@code null} on {@code - * ClassCastException} and {@code NullPointerException}. - */ - static <V> V safeRemove(Map<?, V> map, Object key) { - checkNotNull(map); - try { - return map.remove(key); - } catch (ClassCastException e) { - return null; - } catch (NullPointerException e) { - return null; } } @@ -3494,6 +2061,13 @@ public final class Maps { } /** + * An implementation of {@link Map#hashCode}. + */ + static int hashCodeImpl(Map<?, ?> map) { + return Sets.hashCodeImpl(map.entrySet()); + } + + /** * An implementation of {@link Map#toString}. */ static String toStringImpl(Map<?, ?> map) { @@ -3517,30 +2091,36 @@ public final class Maps { * An admittedly inefficient implementation of {@link Map#containsKey}. */ static boolean containsKeyImpl(Map<?, ?> map, @Nullable Object key) { - return Iterators.contains(keyIterator(map.entrySet().iterator()), key); + for (Entry<?, ?> entry : map.entrySet()) { + if (Objects.equal(entry.getKey(), key)) { + return true; + } + } + return false; } /** * An implementation of {@link Map#containsValue}. */ static boolean containsValueImpl(Map<?, ?> map, @Nullable Object value) { - return Iterators.contains(valueIterator(map.entrySet().iterator()), value); - } - - static <K, V> Iterator<K> keyIterator(Iterator<Entry<K, V>> entryIterator) { - return new TransformedIterator<Entry<K, V>, K>(entryIterator) { - @Override - K transform(Entry<K, V> entry) { - return entry.getKey(); + for (Entry<?, ?> entry : map.entrySet()) { + if (Objects.equal(entry.getValue(), value)) { + return true; } - }; + } + return false; } - abstract static class KeySet<K, V> extends Sets.ImprovedAbstractSet<K> { + abstract static class KeySet<K, V> extends AbstractSet<K> { abstract Map<K, V> map(); @Override public Iterator<K> iterator() { - return keyIterator(map().entrySet().iterator()); + return Iterators.transform(map().entrySet().iterator(), + new Function<Map.Entry<K, V>, K>() { + @Override public K apply(Entry<K, V> entry) { + return entry.getKey(); + } + }); } @Override public int size() { @@ -3563,153 +2143,27 @@ public final class Maps { return false; } - @Override public void clear() { - map().clear(); - } - } - - @Nullable - static <K> K keyOrNull(@Nullable Entry<K, ?> entry) { - return (entry == null) ? null : entry.getKey(); - } - - @Nullable - static <V> V valueOrNull(@Nullable Entry<?, V> entry) { - return (entry == null) ? null : entry.getValue(); - } - - @GwtIncompatible("NavigableMap") - static class NavigableKeySet<K, V> extends KeySet<K, V> implements NavigableSet<K> { - private final NavigableMap<K, V> map; - - NavigableKeySet(NavigableMap<K, V> map) { - this.map = checkNotNull(map); - } - - @Override - NavigableMap<K, V> map() { - return map; - } - - @Override - public Comparator<? super K> comparator() { - return map().comparator(); - } - - @Override - public K first() { - return map().firstKey(); - } - - @Override - public K last() { - return map().lastKey(); - } - - @Override - public K lower(K e) { - return map().lowerKey(e); - } - - @Override - public K floor(K e) { - return map().floorKey(e); - } - - @Override - public K ceiling(K e) { - return map().ceilingKey(e); - } - - @Override - public K higher(K e) { - return map().higherKey(e); - } - - @Override - public K pollFirst() { - return keyOrNull(map().pollFirstEntry()); - } - - @Override - public K pollLast() { - return keyOrNull(map().pollLastEntry()); - } - - @Override - public NavigableSet<K> descendingSet() { - return map().descendingKeySet(); - } - - @Override - public Iterator<K> descendingIterator() { - return descendingSet().iterator(); - } - - @Override - public NavigableSet<K> subSet( - K fromElement, - boolean fromInclusive, - K toElement, - boolean toInclusive) { - return map().subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); - } - - @Override - public NavigableSet<K> headSet(K toElement, boolean inclusive) { - return map().headMap(toElement, inclusive).navigableKeySet(); - } - - @Override - public NavigableSet<K> tailSet(K fromElement, boolean inclusive) { - return map().tailMap(fromElement, inclusive).navigableKeySet(); - } - - @Override - public SortedSet<K> subSet(K fromElement, K toElement) { - return subSet(fromElement, true, toElement, false); - } - @Override - public SortedSet<K> headSet(K toElement) { - return headSet(toElement, false); + public boolean removeAll(Collection<?> c) { + // TODO(user): find out why this is necessary to make GWT tests pass. + return super.removeAll(checkNotNull(c)); } - @Override - public SortedSet<K> tailSet(K fromElement) { - return tailSet(fromElement, true); + @Override public void clear() { + map().clear(); } } - static <K, V> Iterator<V> valueIterator(Iterator<Entry<K, V>> entryIterator) { - return new TransformedIterator<Entry<K, V>, V>(entryIterator) { - @Override - V transform(Entry<K, V> entry) { - return entry.getValue(); - } - }; - } - - static <K, V> UnmodifiableIterator<V> valueIterator( - final UnmodifiableIterator<Entry<K, V>> entryIterator) { - return new UnmodifiableIterator<V>() { - @Override - public boolean hasNext() { - return entryIterator.hasNext(); - } - - @Override - public V next() { - return entryIterator.next().getValue(); - } - }; - } - abstract static class Values<K, V> extends AbstractCollection<V> { abstract Map<K, V> map(); @Override public Iterator<V> iterator() { - return valueIterator(map().entrySet().iterator()); + return Iterators.transform(map().entrySet().iterator(), + new Function<Entry<K, V>, V>() { + @Override public V apply(Entry<K, V> entry) { + return entry.getValue(); + } + }); } @Override public boolean remove(Object o) { @@ -3771,8 +2225,7 @@ public final class Maps { } } - abstract static class EntrySet<K, V> - extends Sets.ImprovedAbstractSet<Entry<K, V>> { + abstract static class EntrySet<K, V> extends AbstractSet<Entry<K, V>> { abstract Map<K, V> map(); @Override public int size() { @@ -3835,197 +2288,4 @@ public final class Maps { } } } - - @GwtIncompatible("NavigableMap") - abstract static class DescendingMap<K, V> extends ForwardingMap<K, V> - implements NavigableMap<K, V> { - - abstract NavigableMap<K, V> forward(); - - @Override - protected final Map<K, V> delegate() { - return forward(); - } - - private transient Comparator<? super K> comparator; - - @SuppressWarnings("unchecked") - @Override - public Comparator<? super K> comparator() { - Comparator<? super K> result = comparator; - if (result == null) { - Comparator<? super K> forwardCmp = forward().comparator(); - if (forwardCmp == null) { - forwardCmp = (Comparator) Ordering.natural(); - } - result = comparator = reverse(forwardCmp); - } - return result; - } - - // If we inline this, we get a javac error. - private static <T> Ordering<T> reverse(Comparator<T> forward) { - return Ordering.from(forward).reverse(); - } - - @Override - public K firstKey() { - return forward().lastKey(); - } - - @Override - public K lastKey() { - return forward().firstKey(); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return forward().higherEntry(key); - } - - @Override - public K lowerKey(K key) { - return forward().higherKey(key); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return forward().ceilingEntry(key); - } - - @Override - public K floorKey(K key) { - return forward().ceilingKey(key); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return forward().floorEntry(key); - } - - @Override - public K ceilingKey(K key) { - return forward().floorKey(key); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return forward().lowerEntry(key); - } - - @Override - public K higherKey(K key) { - return forward().lowerKey(key); - } - - @Override - public Entry<K, V> firstEntry() { - return forward().lastEntry(); - } - - @Override - public Entry<K, V> lastEntry() { - return forward().firstEntry(); - } - - @Override - public Entry<K, V> pollFirstEntry() { - return forward().pollLastEntry(); - } - - @Override - public Entry<K, V> pollLastEntry() { - return forward().pollFirstEntry(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return forward(); - } - - private transient Set<Entry<K, V>> entrySet; - - @Override - public Set<Entry<K, V>> entrySet() { - Set<Entry<K, V>> result = entrySet; - return (result == null) ? entrySet = createEntrySet() : result; - } - - abstract Iterator<Entry<K, V>> entryIterator(); - - Set<Entry<K, V>> createEntrySet() { - return new EntrySet<K, V>() { - - @Override - Map<K, V> map() { - return DescendingMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - }; - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - private transient NavigableSet<K> navigableKeySet; - - @Override - public NavigableSet<K> navigableKeySet() { - NavigableSet<K> result = navigableKeySet; - return (result == null) ? navigableKeySet = new NavigableKeySet<K, V>(this) : result; - } - - @Override - public NavigableSet<K> descendingKeySet() { - return forward().navigableKeySet(); - } - - @Override - public - NavigableMap<K, V> - subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return forward().subMap(toKey, toInclusive, fromKey, fromInclusive).descendingMap(); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return forward().tailMap(toKey, inclusive).descendingMap(); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return forward().headMap(fromKey, inclusive).descendingMap(); - } - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public Collection<V> values() { - return new Values<K, V>() { - @Override - Map<K, V> map() { - return DescendingMap.this; - } - }; - } - } } diff --git a/guava/src/com/google/common/collect/MinMaxPriorityQueue.java b/guava/src/com/google/common/collect/MinMaxPriorityQueue.java index f9c2d92..4429c34 100644 --- a/guava/src/com/google/common/collect/MinMaxPriorityQueue.java +++ b/guava/src/com/google/common/collect/MinMaxPriorityQueue.java @@ -26,13 +26,13 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.math.IntMath; import java.util.AbstractQueue; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.PriorityQueue; @@ -85,7 +85,7 @@ import java.util.Queue; * @author Torbjorn Gannholm * @since 8.0 */ -// TODO(kevinb): GWT compatibility +// TODO(kevinb): @GwtCompatible @Beta public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { @@ -747,6 +747,7 @@ public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { private class QueueIterator implements Iterator<E> { private int cursor = -1; private int expectedModCount = modCount; + // TODO(user): Switch to ArrayDeque once Guava supports it. private Queue<E> forgetMeNot; private List<E> skipMe; private E lastFromForgetMeNot; @@ -787,7 +788,7 @@ public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { MoveDesc<E> moved = removeAt(cursor); if (moved != null) { if (forgetMeNot == null) { - forgetMeNot = new ArrayDeque<E>(); + forgetMeNot = new LinkedList<E>(); skipMe = new ArrayList<E>(3); } forgetMeNot.add(moved.toTrickle); diff --git a/guava/src/com/google/common/collect/Multimap.java b/guava/src/com/google/common/collect/Multimap.java index ed5afb3..900f820 100644 --- a/guava/src/com/google/common/collect/Multimap.java +++ b/guava/src/com/google/common/collect/Multimap.java @@ -19,139 +19,40 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** - * A collection that maps keys to values, similar to {@link Map}, but in which - * each key may be associated with <i>multiple</i> values. You can visualize the - * contents of a multimap either as a map from keys to <i>nonempty</i> - * collections of values: + * A collection similar to a {@code Map}, but which may associate multiple + * values with a single key. If you call {@link #put} twice, with the same key + * but different values, the multimap contains mappings from the key to both + * values. * - * <ul> - * <li>a → 1, 2 - * <li>b → 3 - * </ul> + * <p>The methods {@link #get}, {@link #keySet}, {@link #keys}, {@link #values}, + * {@link #entries}, and {@link #asMap} return collections that are views of the + * multimap. If the multimap is modifiable, updating it can change the contents + * of those collections, and updating the collections will change the multimap. + * In contrast, {@link #replaceValues} and {@link #removeAll} return collections + * that are independent of subsequent multimap changes. * - * ... or as a single "flattened" collection of key-value pairs: + * <p>Depending on the implementation, a multimap may or may not allow duplicate + * key-value pairs. In other words, the multimap contents after adding the same + * key and value twice varies between implementations. In multimaps allowing + * duplicates, the multimap will contain two mappings, and {@code get} will + * return a collection that includes the value twice. In multimaps not + * supporting duplicates, the multimap will contain a single mapping from the + * key to the value, and {@code get} will return a collection that includes the + * value once. * - * <ul> - * <li>a → 1 - * <li>a → 2 - * <li>b → 3 - * </ul> - * - * <p><b>Important:</b> although the first interpretation resembles how most - * multimaps are <i>implemented</i>, the design of the {@code Multimap} API is - * based on the <i>second</i> form. So, using the multimap shown above as an - * example, the {@link #size} is {@code 3}, not {@code 2}, and the {@link - * #values} collection is {@code [1, 2, 3]}, not {@code [[1, 2], [3]]}. For - * those times when the first style is more useful, use the multimap's {@link - * #asMap} view (or create a {@code Map<K, Collection<V>>} in the first place). - * - * <h3>Example</h3> - * - * <p>The following code: <pre> {@code - * - * ListMultimap<String, String> multimap = ArrayListMultimap.create(); - * for (President pres : US_PRESIDENTS_IN_ORDER) { - * multimap.put(pres.firstName(), pres.lastName()); - * } - * for (String firstName : multimap.keySet()) { - * List<String> lastNames = multimap.get(firstName); - * out.println(firstName + ": " + lastNames); - * }}</pre> - * - * ... produces output such as: <pre> {@code - * - * Zachary: [Taylor] - * John: [Adams, Adams, Tyler, Kennedy] - * George: [Washington, Bush, Bush] - * Grover: [Cleveland] - * ...}</pre> - * - * <h3>Views</h3> - * - * <p>Much of the power of the multimap API comes from the <i>view - * collections</i> it provides. These always reflect the latest state of the - * multimap itself. When they support modification, the changes are - * <i>write-through</i> (they automatically update the backing multimap). These - * view collections are: - * - * <ul> - * <li>{@link #asMap}, mentioned above</li> - * <li>{@link #keys}, {@link #keySet}, {@link #values}, {@link #entries}, which - * are similar to the corresponding view collections of {@link Map} - * <li>and, notably, even the collection returned by {@link #get get(key)} is an - * active view of the values corresponding to {@code key} - * </ul> - * - * <p>The collections returned by the {@link #replaceValues replaceValues} and - * {@link #removeAll removeAll} methods, which contain values that have just - * been removed from the multimap, are naturally <i>not</i> views. - * - * <h3>Subinterfaces</h3> - * - * <p>Instead of using the {@code Multimap} interface directly, prefer the - * subinterfaces {@link ListMultimap} and {@link SetMultimap}. These take their - * names from the fact that the collections they return from {@code get} behave - * like (and, of course, implement) {@link List} and {@link Set}, respectively. - * - * <p>For example, the "presidents" code snippet above used a {@code - * ListMultimap}; if it had used a {@code SetMultimap} instead, two presidents - * would have vanished, and last names might or might not appear in - * chronological order. - * - * <p><b>Warning:</b> instances of type {@code Multimap} may not implement - * {@link Object#equals} in the way you expect (multimaps containing the same - * key-value pairs, even in the same order, may or may not be equal). The - * recommended subinterfaces provide a much stronger guarantee. - * - * <h3>Comparison to a map of collections</h3> - * - * <p>Multimaps are commonly used in places where a {@code Map<K, - * Collection<V>>} would otherwise have appeared. The differences include: - * - * <ul> - * <li>There is no need to populate an empty collection before adding an entry - * with {@link #put put}. - * <li>{@code get} never returns {@code null}, only an empty collection. - * <li>A key contained in the multimap always maps to at least one value. Any - * operation that causes a key to have zero associated values has the effect - * of <i>removing</i> that key from the multimap. - * <li>The total entry count is available as {@link #size}. - * <li>Many complex operations become easier; for example, {@code - * Collections.min(multimap.values())} finds the smallest value across all - * keys. - * </ul> - * - * <h3>Implementations</h3> - * - * <p>As always, prefer the immutable implementations, {@link - * ImmutableListMultimap} and {@link ImmutableSetMultimap}. General-purpose - * mutable implementations are listed above under "All Known Implementing - * Classes". You can also create a <i>custom</i> multimap, backed by any {@code - * Map} and {@link Collection} types, using the {@link Multimaps#newMultimap - * Multimaps.newMultimap} family of methods. Finally, another popular way to - * obtain a multimap is using {@link Multimaps#index Multimaps.index}. See - * the {@link Multimaps} class for these and other static utilities related - * to multimaps. - * - * <h3>Other Notes</h3> - * - * <p>All methods that modify the multimap are optional. The view collections - * returned by the multimap may or may not be modifiable. Any modification - * method that is not supported will throw {@link - * UnsupportedOperationException}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. + * <p>All methods that alter the multimap are optional, and the views returned + * by the multimap may or may not be modifiable. When modification isn't + * supported, those methods will throw an {@link UnsupportedOperationException}. * * @author Jared Levy + * @param <K> the type of keys maintained by this multimap + * @param <V> the type of mapped values * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible @@ -207,7 +108,7 @@ public interface Multimap<K, V> { boolean put(@Nullable K key, @Nullable V value); /** - * Removes a single key-value pair from the multimap. + * Removes a key-value pair from the multimap. * * @param key key of entry to remove from the multimap * @param value value of entry to remove the multimap @@ -268,17 +169,15 @@ public interface Multimap<K, V> { // Views /** - * Returns a collection view containing the values associated with {@code key} - * in this multimap, if any. Note that even when ({@code containsKey(key)} is - * false, {@code get(key)} still returns an empty collection, not {@code - * null}. + * Returns a collection view of all values associated with a key. If no + * mappings in the multimap have the provided key, an empty collection is + * returned. * * <p>Changes to the returned collection will update the underlying multimap, * and vice versa. * * @param key key to search for in multimap - * @return a view collection containing the zero or more values that the key - * maps to + * @return the collection of values that the key maps to */ Collection<V> get(@Nullable K key); diff --git a/guava/src/com/google/common/collect/Multimaps.java b/guava/src/com/google/common/collect/Multimaps.java index 92e5d06..e2f593e 100644 --- a/guava/src/com/google/common/collect/Multimaps.java +++ b/guava/src/com/google/common/collect/Multimaps.java @@ -20,14 +20,17 @@ 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 com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Joiner.MapJoiner; +import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; +import com.google.common.collect.Collections2.TransformedCollection; import com.google.common.collect.Maps.EntryTransformer; import java.io.IOException; @@ -35,6 +38,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -52,10 +56,6 @@ import javax.annotation.Nullable; /** * Provides static methods acting on or generating a {@code Multimap}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multimaps"> - * {@code Multimaps}</a>. - * * @author Jared Levy * @author Robert Konigsberg * @author Mike Bostock @@ -67,14 +67,9 @@ public final class Multimaps { private Multimaps() {} /** - * Creates a new {@code Multimap} backed by {@code map}, whose internal value - * collections are generated by {@code factory}. - * - * <b>Warning: do not use</b> this method when the collections returned by - * {@code factory} implement either {@link List} or {@code Set}! Use the more - * specific method {@link #newListMultimap}, {@link #newSetMultimap} or {@link - * #newSortedSetMultimap} instead, to avoid very surprising behavior from - * {@link Multimap#equals}. + * Creates a new {@code Multimap} that uses the provided map and factory. It + * can generate a multimap based on arbitrary {@link Map} and + * {@link Collection} classes. * * <p>The {@code factory}-generated and {@code map} classes determine the * multimap iteration order. They also specify the behavior of the @@ -114,7 +109,7 @@ public final class Multimaps { return new CustomMultimap<K, V>(map, factory); } - private static class CustomMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { + private static class CustomMultimap<K, V> extends AbstractMultimap<K, V> { transient Supplier<? extends Collection<V>> factory; CustomMultimap(Map<K, Collection<V>> map, @@ -423,13 +418,13 @@ public final class Multimaps { * <p>It is imperative that the user manually synchronize on the returned * multimap when accessing any of its collection views: <pre> {@code * - * Multimap<K, V> multimap = Multimaps.synchronizedMultimap( + * Multimap<K, V> m = Multimaps.synchronizedMultimap( * HashMultimap.<K, V>create()); * ... - * Collection<V> values = multimap.get(key); // Needn't be in synchronized block + * Set<K> s = m.keySet(); // Needn't be in synchronized block * ... - * synchronized (multimap) { // Synchronizing on multimap, not values! - * Iterator<V> i = values.iterator(); // Must be in synchronized block + * synchronized (m) { // Synchronizing on m, not s! + * Iterator<K> i = s.iterator(); // Must be in synchronized block * while (i.hasNext()) { * foo(i.next()); * } @@ -630,7 +625,7 @@ public final class Multimaps { } @Override public Iterator<Collection<V>> iterator() { final Iterator<Collection<V>> iterator = delegate.iterator(); - return new UnmodifiableIterator<Collection<V>>() { + return new Iterator<Collection<V>>() { @Override public boolean hasNext() { return iterator.hasNext(); @@ -639,6 +634,10 @@ public final class Multimaps { public Collection<V> next() { return unmodifiableValueCollection(iterator.next()); } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } }; } @Override public Object[] toArray() { @@ -1061,7 +1060,7 @@ public final class Multimaps { @Override public Set<V> get(final K key) { - return new Sets.ImprovedAbstractSet<V>() { + return new AbstractSet<V>() { @Override public Iterator<V> iterator() { return new Iterator<V>() { int i; @@ -1142,7 +1141,7 @@ public final class Multimaps { @Override public Multiset<K> keys() { - return new Multimaps.Keys<K, V>(this); + return Multisets.forSet(map.keySet()); } @Override @@ -1193,27 +1192,35 @@ public final class Multimaps { } /** @see MapMultimap#asMap */ - class AsMapEntries extends Sets.ImprovedAbstractSet<Entry<K, Collection<V>>> { + class AsMapEntries extends AbstractSet<Entry<K, Collection<V>>> { @Override public int size() { return map.size(); } @Override public Iterator<Entry<K, Collection<V>>> iterator() { - return new TransformedIterator<K, Entry<K, Collection<V>>>(map.keySet().iterator()) { + return new Iterator<Entry<K, Collection<V>>>() { + final Iterator<K> keys = map.keySet().iterator(); + @Override - Entry<K, Collection<V>> transform(final K key) { + public boolean hasNext() { + return keys.hasNext(); + } + @Override + public Entry<K, Collection<V>> next() { + final K key = keys.next(); return new AbstractMapEntry<K, Collection<V>>() { - @Override - public K getKey() { + @Override public K getKey() { return key; } - - @Override - public Collection<V> getValue() { + @Override public Collection<V> getValue() { return get(key); } }; } + @Override + public void remove() { + keys.remove(); + } }; } @@ -1315,6 +1322,7 @@ public final class Multimaps { * * @since 7.0 */ + @Beta public static <K, V1, V2> Multimap<K, V2> transformValues( Multimap<K, V1> fromMultimap, final Function<? super V1, V2> function) { checkNotNull(function); @@ -1383,29 +1391,15 @@ public final class Multimaps { * * @since 7.0 */ + @Beta public static <K, V1, V2> Multimap<K, V2> transformEntries( Multimap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) { return new TransformedEntriesMultimap<K, V1, V2>(fromMap, transformer); } - - static final class ValueFunction<K, V1, V2> implements Function<V1, V2> { - private final K key; - private final EntryTransformer<? super K, ? super V1, V2> transformer; - - ValueFunction(K key, EntryTransformer<? super K, ? super V1, V2> transformer) { - this.key = key; - this.transformer = transformer; - } - - @Override - public V2 apply(@Nullable V1 value) { - return transformer.transformEntry(key, value); - } - } private static class TransformedEntriesMultimap<K, V1, V2> - extends AbstractMultimap<K, V2> { + implements Multimap<K, V2> { final Multimap<K, V1> fromMultimap; final EntryTransformer<? super K, ? super V1, V2> transformer; @@ -1415,25 +1409,30 @@ public final class Multimaps { this.transformer = checkNotNull(transformer); } - Collection<V2> transform(K key, Collection<V1> values) { - Function<V1, V2> function = new ValueFunction<K, V1, V2>(key, transformer); - if (values instanceof List) { - return Lists.transform((List<V1>) values, function); - } else { - return Collections2.transform(values, function); - } + Collection<V2> transform(final K key, Collection<V1> values) { + return Collections2.transform(values, new Function<V1, V2>() { + @Override public V2 apply(V1 value) { + return transformer.transformEntry(key, value); + } + }); } - @Override - Map<K, Collection<V2>> createAsMap() { - return Maps.transformEntries(fromMultimap.asMap(), - new EntryTransformer<K, Collection<V1>, Collection<V2>>() { + private transient Map<K, Collection<V2>> asMap; - @Override public Collection<V2> transformEntry( - K key, Collection<V1> value) { - return transform(key, value); - } - }); + @Override public Map<K, Collection<V2>> asMap() { + if (asMap == null) { + Map<K, Collection<V2>> aM = Maps.transformEntries(fromMultimap.asMap(), + new EntryTransformer<K, Collection<V1>, Collection<V2>>() { + + @Override public Collection<V2> transformEntry( + K key, Collection<V1> value) { + return transform(key, value); + } + }); + asMap = aM; + return aM; + } + return asMap; } @Override public void clear() { @@ -1454,25 +1453,58 @@ public final class Multimaps { return values().contains(value); } - @Override - Iterator<Entry<K, V2>> entryIterator() { - return Iterators.transform( - fromMultimap.entries().iterator(), new Function<Entry<K, V1>, Entry<K, V2>>() { - @Override - public Entry<K, V2> apply(final Entry<K, V1> entry) { - return new AbstractMapEntry<K, V2>() { - @Override - public K getKey() { - return entry.getKey(); - } + private transient Collection<Entry<K, V2>> entries; + + @Override public Collection<Entry<K, V2>> entries() { + if (entries == null) { + Collection<Entry<K, V2>> es = new TransformedEntries(transformer); + entries = es; + return es; + } + return entries; + } + + private class TransformedEntries + extends TransformedCollection<Entry<K, V1>, Entry<K, V2>> { + + TransformedEntries( + final EntryTransformer<? super K, ? super V1, V2> transformer) { + super(fromMultimap.entries(), + new Function<Entry<K, V1>, Entry<K, V2>>() { + @Override public Entry<K, V2> apply(final Entry<K, V1> entry) { + return new AbstractMapEntry<K, V2>() { + + @Override public K getKey() { + return entry.getKey(); + } + + @Override public V2 getValue() { + return transformer.transformEntry( + entry.getKey(), entry.getValue()); + } + }; + } + }); + } + + @Override public boolean contains(Object o) { + if (o instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) o; + return containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @SuppressWarnings("unchecked") + @Override public boolean remove(Object o) { + if (o instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) o; + Collection<V2> values = get((K) entry.getKey()); + return values.remove(entry.getValue()); + } + return false; + } - @Override - public V2 getValue() { - return transformer.transformEntry(entry.getKey(), entry.getValue()); - } - }; - } - }); } @Override public Collection<V2> get(final K key) { @@ -1522,16 +1554,39 @@ public final class Multimaps { @Override public int size() { return fromMultimap.size(); } - - @Override - Collection<V2> createValues() { - return Collections2.transform( - fromMultimap.entries(), new Function<Entry<K, V1>, V2>() { - @Override public V2 apply(Entry<K, V1> entry) { - return transformer.transformEntry( - entry.getKey(), entry.getValue()); - } - }); + + private transient Collection<V2> values; + + @Override public Collection<V2> values() { + if (values == null) { + Collection<V2> vs = Collections2.transform( + fromMultimap.entries(), new Function<Entry<K, V1>, V2>() { + + @Override public V2 apply(Entry<K, V1> entry) { + return transformer.transformEntry( + entry.getKey(), entry.getValue()); + } + }); + values = vs; + return vs; + } + return values; + } + + @Override public boolean equals(Object obj) { + if (obj instanceof Multimap) { + Multimap<?, ?> other = (Multimap<?, ?>) obj; + return asMap().equals(other.asMap()); + } + return false; + } + + @Override public int hashCode() { + return asMap().hashCode(); + } + + @Override public String toString() { + return asMap().toString(); } } @@ -1576,6 +1631,7 @@ public final class Multimaps { * * @since 7.0 */ + @Beta public static <K, V1, V2> ListMultimap<K, V2> transformValues( ListMultimap<K, V1> fromMultimap, final Function<? super V1, V2> function) { @@ -1642,6 +1698,7 @@ public final class Multimaps { * * @since 7.0 */ + @Beta public static <K, V1, V2> ListMultimap<K, V2> transformEntries( ListMultimap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) { @@ -1728,6 +1785,24 @@ public final class Multimaps { } /** + * <b>Deprecated.</b> + * + * @since 10.0 + * @deprecated use {@link #index(Iterator, Function)} by casting {@code + * values} to {@code Iterator<V>}, or better yet, by implementing only + * {@code Iterator} and not {@code Iterable}. <b>This method is scheduled + * for deletion in March 2012.</b> + */ + @Beta + @Deprecated + public static <K, V, I extends Object & Iterable<V> & Iterator<V>> + ImmutableListMultimap<K, V> index( + I values, Function<? super V, K> keyFunction) { + Iterable<V> valuesIterable = checkNotNull(values); + return index(valuesIterable, keyFunction); + } + + /** * Creates an index {@code ImmutableListMultimap} that contains the results of * applying a specified function to each item in an {@code Iterator} of * values. Each value will be stored as a value in the resulting multimap, @@ -1783,36 +1858,39 @@ public final class Multimaps { return builder.build(); } - static class Keys<K, V> extends AbstractMultiset<K> { - final Multimap<K, V> multimap; - - Keys(Multimap<K, V> multimap) { - this.multimap = multimap; - } + static abstract class Keys<K, V> extends AbstractMultiset<K> { + abstract Multimap<K, V> multimap(); @Override Iterator<Multiset.Entry<K>> entryIterator() { - return new TransformedIterator<Map.Entry<K, Collection<V>>, Multiset.Entry<K>>( - multimap.asMap().entrySet().iterator()) { - @Override - Multiset.Entry<K> transform( - final Map.Entry<K, Collection<V>> backingEntry) { + final Iterator<Map.Entry<K, Collection<V>>> backingIterator = + multimap().asMap().entrySet().iterator(); + return new Iterator<Multiset.Entry<K>>() { + @Override public boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override public Multiset.Entry<K> next() { + final Map.Entry<K, Collection<V>> backingEntry = + backingIterator.next(); return new Multisets.AbstractEntry<K>() { - @Override - public K getElement() { + @Override public K getElement() { return backingEntry.getKey(); } - @Override - public int getCount() { + @Override public int getCount() { return backingEntry.getValue().size(); } }; } + + @Override public void remove() { + backingIterator.remove(); + } }; } @Override int distinctElements() { - return multimap.asMap().size(); + return multimap().asMap().size(); } @Override Set<Multiset.Entry<K>> createEntrySet() { @@ -1833,22 +1911,22 @@ public final class Multimaps { } @Override public boolean isEmpty() { - return multimap.isEmpty(); + return multimap().isEmpty(); } @Override public boolean contains(@Nullable Object o) { - if (o instanceof Multiset.Entry) { + if (o instanceof Multiset.Entry<?>) { Multiset.Entry<?> entry = (Multiset.Entry<?>) o; - Collection<V> collection = multimap.asMap().get(entry.getElement()); + Collection<V> collection = multimap().asMap().get(entry.getElement()); return collection != null && collection.size() == entry.getCount(); } return false; } @Override public boolean remove(@Nullable Object o) { - if (o instanceof Multiset.Entry) { + if (o instanceof Multiset.Entry<?>) { Multiset.Entry<?> entry = (Multiset.Entry<?>) o; - Collection<V> collection = multimap.asMap().get(entry.getElement()); + Collection<V> collection = multimap().asMap().get(entry.getElement()); if (collection != null && collection.size() == entry.getCount()) { collection.clear(); return true; @@ -1859,16 +1937,30 @@ public final class Multimaps { } @Override public boolean contains(@Nullable Object element) { - return multimap.containsKey(element); + return multimap().containsKey(element); } @Override public Iterator<K> iterator() { - return Maps.keyIterator(multimap.entries().iterator()); + return Iterators.transform(multimap().entries().iterator(), + new Function<Map.Entry<K, V>, K>() { + @Override public K apply(Map.Entry<K, V> entry) { + return entry.getKey(); + } + }); } @Override public int count(@Nullable Object element) { - Collection<V> values = Maps.safeGet(multimap.asMap(), element); - return (values == null) ? 0 : values.size(); + try { + if (multimap().containsKey(element)) { + Collection<V> values = multimap().asMap().get(element); + return (values == null) ? 0 : values.size(); + } + return 0; + } catch (ClassCastException e) { + return 0; + } catch (NullPointerException e) { + return 0; + } } @Override public int remove(@Nullable Object element, int occurrences) { @@ -1877,7 +1969,14 @@ public final class Multimaps { return count(element); } - Collection<V> values = Maps.safeGet(multimap.asMap(), element); + Collection<V> values; + try { + values = multimap().asMap().get(element); + } catch (ClassCastException e) { + return 0; + } catch (NullPointerException e) { + return 0; + } if (values == null) { return 0; @@ -1897,35 +1996,45 @@ public final class Multimaps { } @Override public void clear() { - multimap.clear(); + multimap().clear(); } @Override public Set<K> elementSet() { - return multimap.keySet(); + return multimap().keySet(); } } - static class Values<K, V> extends AbstractCollection<V> { - final Multimap<K, V> multimap; - - Values(Multimap<K, V> multimap) { - this.multimap = multimap; - } + static abstract class Values<K, V> extends AbstractCollection<V> { + abstract Multimap<K, V> multimap(); @Override public Iterator<V> iterator() { - return Maps.valueIterator(multimap.entries().iterator()); + final Iterator<Map.Entry<K, V>> backingIterator = + multimap().entries().iterator(); + return new Iterator<V>() { + @Override public boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override public V next() { + return backingIterator.next().getValue(); + } + + @Override public void remove() { + backingIterator.remove(); + } + }; } @Override public int size() { - return multimap.size(); + return multimap().size(); } @Override public boolean contains(@Nullable Object o) { - return multimap.containsValue(o); + return multimap().containsValue(o); } @Override public void clear() { - multimap.clear(); + multimap().clear(); } } @@ -1941,7 +2050,7 @@ public final class Multimaps { } @Override public boolean contains(@Nullable Object o) { - if (o instanceof Map.Entry) { + if (o instanceof Map.Entry<?, ?>) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; return multimap().containsEntry(entry.getKey(), entry.getValue()); } @@ -1949,7 +2058,7 @@ public final class Multimaps { } @Override public boolean remove(@Nullable Object o) { - if (o instanceof Map.Entry) { + if (o instanceof Map.Entry<?, ?>) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o; return multimap().remove(entry.getKey(), entry.getValue()); } @@ -2047,8 +2156,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a key that doesn't satisfy the predicate, the - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose keys satisfy the @@ -2069,21 +2178,19 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterKeys( - Multimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { - if (unfiltered instanceof FilteredKeyMultimap) { - FilteredKeyMultimap<K, V> prev = (FilteredKeyMultimap<K, V>) unfiltered; - return new FilteredKeyMultimap<K, V>(prev.unfiltered, - Predicates.and(prev.keyPredicate, keyPredicate)); - } else if (unfiltered instanceof FilteredMultimap) { - FilteredMultimap<K, V> prev = (FilteredMultimap<K, V>) unfiltered; - return new FilteredEntryMultimap<K, V>(prev.unfiltered, - Predicates.<Entry<K, V>>and(prev.entryPredicate(), - Predicates.compose(keyPredicate, Maps.<K>keyFunction()))); - } else { - return new FilteredKeyMultimap<K, V>(unfiltered, keyPredicate); - } + Multimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { + checkNotNull(keyPredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2094,8 +2201,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a value that doesn't satisfy the predicate, the - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose value satisfy the @@ -2116,10 +2223,19 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterValues( - Multimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { - return filterEntries(unfiltered, Predicates.compose(valuePredicate, Maps.<V>valueFunction())); + Multimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2130,8 +2246,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a key/value pair that doesn't satisfy the predicate, - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose keys satisfy the @@ -2150,27 +2266,489 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterEntries( - Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { + Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { checkNotNull(entryPredicate); return (unfiltered instanceof FilteredMultimap) ? filterFiltered((FilteredMultimap<K, V>) unfiltered, entryPredicate) - : new FilteredEntryMultimap<K, V>(checkNotNull(unfiltered), entryPredicate); + : new FilteredMultimap<K, V>(checkNotNull(unfiltered), entryPredicate); } /** * Support removal operations when filtering a filtered multimap. Since a * filtered multimap has iterators that don't support remove, passing one to - * the FilteredEntryMultimap constructor would lead to a multimap whose removal + * the FilteredMultimap constructor would lead to a multimap whose removal * operations would fail. This method combines the predicates to avoid that * problem. */ - private static <K, V> Multimap<K, V> filterFiltered(FilteredMultimap<K, V> multimap, + private static <K, V> Multimap<K, V> filterFiltered(FilteredMultimap<K, V> map, Predicate<? super Entry<K, V>> entryPredicate) { Predicate<Entry<K, V>> predicate - = Predicates.and(multimap.entryPredicate(), entryPredicate); - return new FilteredEntryMultimap<K, V>(multimap.unfiltered, predicate); + = Predicates.and(map.predicate, entryPredicate); + return new FilteredMultimap<K, V>(map.unfiltered, predicate); + } + + private static class FilteredMultimap<K, V> implements Multimap<K, V> { + final Multimap<K, V> unfiltered; + final Predicate<? super Entry<K, V>> predicate; + + FilteredMultimap(Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + @Override public int size() { + return entries().size(); + } + + @Override public boolean isEmpty() { + return entries().isEmpty(); + } + + @Override public boolean containsKey(Object key) { + return asMap().containsKey(key); + } + + @Override public boolean containsValue(Object value) { + return values().contains(value); + } + + // This method should be called only when key is a K and value is a V. + @SuppressWarnings("unchecked") + boolean satisfiesPredicate(Object key, Object value) { + return predicate.apply(Maps.immutableEntry((K) key, (V) value)); + } + + @Override public boolean containsEntry(Object key, Object value) { + return unfiltered.containsEntry(key, value) && satisfiesPredicate(key, value); + } + + @Override public boolean put(K key, V value) { + checkArgument(satisfiesPredicate(key, value)); + return unfiltered.put(key, value); + } + + @Override public boolean remove(Object key, Object value) { + return containsEntry(key, value) ? unfiltered.remove(key, value) : false; + } + + @Override public boolean putAll(K key, Iterable<? extends V> values) { + for (V value : values) { + checkArgument(satisfiesPredicate(key, value)); + } + return unfiltered.putAll(key, values); + } + + @Override public boolean putAll(Multimap<? extends K, ? extends V> multimap) { + for (Entry<? extends K, ? extends V> entry : multimap.entries()) { + checkArgument(satisfiesPredicate(entry.getKey(), entry.getValue())); + } + return unfiltered.putAll(multimap); + } + + @Override public Collection<V> replaceValues(K key, Iterable<? extends V> values) { + for (V value : values) { + checkArgument(satisfiesPredicate(key, value)); + } + // Not calling unfiltered.replaceValues() since values that don't satisify + // the filter should remain in the multimap. + Collection<V> oldValues = removeAll(key); + unfiltered.putAll(key, values); + return oldValues; + } + + @Override public Collection<V> removeAll(Object key) { + List<V> removed = Lists.newArrayList(); + Collection<V> values = unfiltered.asMap().get(key); + if (values != null) { + Iterator<V> iterator = values.iterator(); + while (iterator.hasNext()) { + V value = iterator.next(); + if (satisfiesPredicate(key, value)) { + removed.add(value); + iterator.remove(); + } + } + } + if (unfiltered instanceof SetMultimap) { + return Collections.unmodifiableSet(Sets.newLinkedHashSet(removed)); + } else { + return Collections.unmodifiableList(removed); + } + } + + @Override public void clear() { + entries().clear(); + } + + @Override public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof Multimap) { + Multimap<?, ?> that = (Multimap<?, ?>) object; + return asMap().equals(that.asMap()); + } + return false; + } + + @Override public int hashCode() { + return asMap().hashCode(); + } + + @Override public String toString() { + return asMap().toString(); + } + + class ValuePredicate implements Predicate<V> { + final K key; + ValuePredicate(K key) { + this.key = key; + } + @Override public boolean apply(V value) { + return satisfiesPredicate(key, value); + } + } + + Collection<V> filterCollection(Collection<V> collection, Predicate<V> predicate) { + if (collection instanceof Set) { + return Sets.filter((Set<V>) collection, predicate); + } else { + return Collections2.filter(collection, predicate); + } + } + + @Override public Collection<V> get(K key) { + return filterCollection(unfiltered.get(key), new ValuePredicate(key)); + } + + @Override public Set<K> keySet() { + return asMap().keySet(); + } + + Collection<V> values; + + @Override public Collection<V> values() { + return (values == null) ? values = new Values() : values; + } + + class Values extends Multimaps.Values<K, V> { + @Override Multimap<K, V> multimap() { + return FilteredMultimap.this; + } + + @Override public boolean contains(@Nullable Object o) { + return Iterators.contains(iterator(), o); + } + + // Override remove methods since iterator doesn't support remove. + + @Override public boolean remove(Object o) { + Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator(); + while (iterator.hasNext()) { + Entry<K, V> entry = iterator.next(); + if (Objects.equal(o, entry.getValue()) && predicate.apply(entry)) { + iterator.remove(); + return true; + } + } + return false; + } + + @Override public boolean removeAll(Collection<?> c) { + boolean changed = false; + Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator(); + while (iterator.hasNext()) { + Entry<K, V> entry = iterator.next(); + if (c.contains(entry.getValue()) && predicate.apply(entry)) { + iterator.remove(); + changed = true; + } + } + return changed; + } + + @Override public boolean retainAll(Collection<?> c) { + boolean changed = false; + Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator(); + while (iterator.hasNext()) { + Entry<K, V> entry = iterator.next(); + if (!c.contains(entry.getValue()) && predicate.apply(entry)) { + iterator.remove(); + changed = true; + } + } + return changed; + } + } + + Collection<Entry<K, V>> entries; + + @Override public Collection<Entry<K, V>> entries() { + return (entries == null) + ? entries = Collections2.filter(unfiltered.entries(), predicate) + : entries; + } + + /** + * Remove all filtered asMap() entries that satisfy the predicate. + */ + boolean removeEntriesIf(Predicate<Map.Entry<K, Collection<V>>> removalPredicate) { + Iterator<Map.Entry<K, Collection<V>>> iterator = unfiltered.asMap().entrySet().iterator(); + boolean changed = false; + while (iterator.hasNext()) { + // Determine whether to remove the filtered values with this key. + Map.Entry<K, Collection<V>> entry = iterator.next(); + K key = entry.getKey(); + Collection<V> collection = entry.getValue(); + Predicate<V> valuePredicate = new ValuePredicate(key); + Collection<V> filteredCollection = filterCollection(collection, valuePredicate); + Map.Entry<K, Collection<V>> filteredEntry = Maps.immutableEntry(key, filteredCollection); + if (removalPredicate.apply(filteredEntry) && !filteredCollection.isEmpty()) { + changed = true; + if (Iterables.all(collection, valuePredicate)) { + iterator.remove(); // Remove all values for the key. + } else { + filteredCollection.clear(); // Remove the filtered values only. + } + } + } + return changed; + } + + Map<K, Collection<V>> asMap; + + @Override public Map<K, Collection<V>> asMap() { + return (asMap == null) ? asMap = createAsMap() : asMap; + } + + static final Predicate<Collection<?>> NOT_EMPTY = new Predicate<Collection<?>>() { + @Override public boolean apply(Collection<?> input) { + return !input.isEmpty(); + } + }; + + Map<K, Collection<V>> createAsMap() { + // Select the values that satisify the predicate. + EntryTransformer<K, Collection<V>, Collection<V>> transformer + = new EntryTransformer<K, Collection<V>, Collection<V>>() { + @Override public Collection<V> transformEntry(K key, Collection<V> collection) { + return filterCollection(collection, new ValuePredicate(key)); + } + }; + Map<K, Collection<V>> transformed + = Maps.transformEntries(unfiltered.asMap(), transformer); + + // Select the keys that have at least one value remaining. + Map<K, Collection<V>> filtered = Maps.filterValues(transformed, NOT_EMPTY); + + // Override the removal methods, since removing a map entry should not + // affect values that don't satisfy the filter. + return new AsMap(filtered); + } + + class AsMap extends ForwardingMap<K, Collection<V>> { + final Map<K, Collection<V>> delegate; + + AsMap(Map<K, Collection<V>> delegate) { + this.delegate = delegate; + } + + @Override protected Map<K, Collection<V>> delegate() { + return delegate; + } + + @Override public Collection<V> remove(Object o) { + Collection<V> output = FilteredMultimap.this.removeAll(o); + return output.isEmpty() ? null : output; + } + + @Override public void clear() { + FilteredMultimap.this.clear(); + } + + Set<K> keySet; + + @Override public Set<K> keySet() { + return (keySet == null) ? keySet = new KeySet() : keySet; + } + + class KeySet extends Maps.KeySet<K, Collection<V>> { + @Override Map<K, Collection<V>> map() { + return AsMap.this; + } + + @Override public boolean remove(Object o) { + Collection<V> collection = delegate.get(o); + if (collection == null) { + return false; + } + collection.clear(); + return true; + } + + @Override public boolean removeAll(Collection<?> c) { + return Sets.removeAllImpl(this, c); + } + + @Override public boolean retainAll(final Collection<?> c) { + Predicate<Map.Entry<K, Collection<V>>> removalPredicate + = new Predicate<Map.Entry<K, Collection<V>>>() { + @Override public boolean apply(Map.Entry<K, Collection<V>> entry) { + return !c.contains(entry.getKey()); + } + }; + return removeEntriesIf(removalPredicate); + } + } + + Values asMapValues; + + @Override public Collection<Collection<V>> values() { + return (asMapValues == null) ? asMapValues = new Values() : asMapValues; + } + + class Values extends Maps.Values<K, Collection<V>> { + @Override Map<K, Collection<V>> map() { + return AsMap.this; + } + + @Override public boolean remove(Object o) { + for (Collection<V> collection : this) { + if (collection.equals(o)) { + collection.clear(); + return true; + } + } + return false; + } + + @Override public boolean removeAll(final Collection<?> c) { + Predicate<Map.Entry<K, Collection<V>>> removalPredicate + = new Predicate<Map.Entry<K, Collection<V>>>() { + @Override public boolean apply(Map.Entry<K, Collection<V>> entry) { + return c.contains(entry.getValue()); + } + }; + return removeEntriesIf(removalPredicate); + } + + @Override public boolean retainAll(final Collection<?> c) { + Predicate<Map.Entry<K, Collection<V>>> removalPredicate + = new Predicate<Map.Entry<K, Collection<V>>>() { + @Override public boolean apply(Map.Entry<K, Collection<V>> entry) { + return !c.contains(entry.getValue()); + } + }; + return removeEntriesIf(removalPredicate); + } + } + + EntrySet entrySet; + + @Override public Set<Map.Entry<K, Collection<V>>> entrySet() { + return (entrySet == null) ? entrySet = new EntrySet(super.entrySet()) : entrySet; + } + + class EntrySet extends Maps.EntrySet<K, Collection<V>> { + Set<Map.Entry<K, Collection<V>>> delegateEntries; + + public EntrySet(Set<Map.Entry<K, Collection<V>>> delegateEntries) { + this.delegateEntries = delegateEntries; + } + + @Override Map<K, Collection<V>> map() { + return AsMap.this; + } + + @Override public Iterator<Map.Entry<K, Collection<V>>> iterator() { + return delegateEntries.iterator(); + } + + @Override public boolean remove(Object o) { + if (o instanceof Entry<?, ?>) { + Entry<?, ?> entry = (Entry<?, ?>) o; + Collection<V> collection = delegate.get(entry.getKey()); + if (collection != null && collection.equals(entry.getValue())) { + collection.clear(); + return true; + } + } + return false; + } + + @Override public boolean removeAll(Collection<?> c) { + return Sets.removeAllImpl(this, c); + } + + @Override public boolean retainAll(final Collection<?> c) { + Predicate<Map.Entry<K, Collection<V>>> removalPredicate + = new Predicate<Map.Entry<K, Collection<V>>>() { + @Override public boolean apply(Map.Entry<K, Collection<V>> entry) { + return !c.contains(entry); + } + }; + return removeEntriesIf(removalPredicate); + } + } + } + + AbstractMultiset<K> keys; + + @Override public Multiset<K> keys() { + return (keys == null) ? keys = new Keys() : keys; + } + + class Keys extends Multimaps.Keys<K, V> { + @Override Multimap<K, V> multimap() { + return FilteredMultimap.this; + } + + @Override public int remove(Object o, int occurrences) { + checkArgument(occurrences >= 0); + Collection<V> values = unfiltered.asMap().get(o); + if (values == null) { + return 0; + } + int priorCount = 0; + int removed = 0; + Iterator<V> iterator = values.iterator(); + while (iterator.hasNext()) { + if (satisfiesPredicate(o, iterator.next())) { + priorCount++; + if (removed < occurrences) { + iterator.remove(); + removed++; + } + } + } + return priorCount; + } + + @Override Set<Multiset.Entry<K>> createEntrySet() { + return new EntrySet(); + } + + class EntrySet extends Multimaps.Keys<K, V>.KeysEntrySet { + @Override public boolean removeAll(Collection<?> c) { + return Sets.removeAllImpl(this, c); + } + + @Override public boolean retainAll(final Collection<?> c) { + Predicate<Map.Entry<K, Collection<V>>> removalPredicate + = new Predicate<Map.Entry<K, Collection<V>>>() { + @Override public boolean apply(Map.Entry<K, Collection<V>> entry) { + Multiset.Entry<K> multisetEntry + = Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); + return !c.contains(multisetEntry); + } + }; + return removeEntriesIf(removalPredicate); + } + } + } } // TODO(jlevy): Create methods that filter a SetMultimap or SortedSetMultimap. diff --git a/guava/src/com/google/common/collect/Multiset.java b/guava/src/com/google/common/collect/Multiset.java index bb254c9..823750e 100644 --- a/guava/src/com/google/common/collect/Multiset.java +++ b/guava/src/com/google/common/collect/Multiset.java @@ -31,13 +31,13 @@ import javax.annotation.Nullable; * may have duplicate elements. A multiset is also sometimes called a * <i>bag</i>. * - * <p>Elements of a multiset that are equal to one another are referred to as - * <i>occurrences</i> of the same single element. The total number of - * occurrences of an element in a multiset is called the <i>count</i> of that - * element (the terms "frequency" and "multiplicity" are equivalent, but not - * used in this API). Since the count of an element is represented as an {@code - * int}, a multiset may never contain more than {@link Integer#MAX_VALUE} - * occurrences of any one element. + * <p>Elements of a multiset that are equal to one another (see "Note on + * element equivalence", below) are referred to as <i>occurrences</i> of the + * same single element. The total number of occurrences of an element in a + * multiset is called the <i>count</i> of that element (the terms "frequency" + * and "multiplicity" are equivalent, but not used in this API). Since the count + * of an element is represented as an {@code int}, a multiset may never contain + * more than {@link Integer#MAX_VALUE} occurrences of any one element. * * <p>{@code Multiset} refines the specifications of several methods from * {@code Collection}. It also defines an additional query operation, {@link @@ -77,10 +77,6 @@ import javax.annotation.Nullable; * may wish to use {@link com.google.common.util.concurrent.AtomicLongMap} * instead. Note, however, that unlike {@code Multiset}, {@code AtomicLongMap} * does not automatically remove zeros. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/Multisets.java b/guava/src/com/google/common/collect/Multisets.java index d0ab028..dbd54c3 100644 --- a/guava/src/com/google/common/collect/Multisets.java +++ b/guava/src/com/google/common/collect/Multisets.java @@ -18,33 +18,32 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Multiset.Entry; -import com.google.common.primitives.Ints; +import static com.google.common.base.Preconditions.checkState; import java.io.Serializable; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; +import java.util.SortedSet; import javax.annotation.Nullable; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.collect.Multiset.Entry; +import com.google.common.primitives.Ints; + /** * Provides static utility methods for creating and working with {@link * Multiset} instances. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multisets"> - * {@code Multisets}</a>. - * * @author Kevin Bourrillion * @author Mike Bostock * @author Louis Wasserman @@ -194,10 +193,92 @@ public final class Multisets { @Beta public static <E> SortedMultiset<E> unmodifiableSortedMultiset( SortedMultiset<E> sortedMultiset) { - // it's in its own file so it can be emulated for GWT return new UnmodifiableSortedMultiset<E>(checkNotNull(sortedMultiset)); } + private static final class UnmodifiableSortedMultiset<E> + extends UnmodifiableMultiset<E> implements SortedMultiset<E> { + private UnmodifiableSortedMultiset(SortedMultiset<E> delegate) { + super(delegate); + } + + @Override + protected SortedMultiset<E> delegate() { + return (SortedMultiset<E>) super.delegate(); + } + + @Override + public Comparator<? super E> comparator() { + return delegate().comparator(); + } + + @Override + SortedSet<E> createElementSet() { + return Collections.unmodifiableSortedSet(delegate().elementSet()); + } + + @Override + public SortedSet<E> elementSet() { + return (SortedSet<E>) super.elementSet(); + } + + private transient UnmodifiableSortedMultiset<E> descendingMultiset; + + @Override + public SortedMultiset<E> descendingMultiset() { + UnmodifiableSortedMultiset<E> result = descendingMultiset; + if (result == null) { + result = new UnmodifiableSortedMultiset<E>( + delegate().descendingMultiset()); + result.descendingMultiset = this; + return descendingMultiset = result; + } + return result; + } + + @Override + public Entry<E> firstEntry() { + return delegate().firstEntry(); + } + + @Override + public Entry<E> lastEntry() { + return delegate().lastEntry(); + } + + @Override + public Entry<E> pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public Entry<E> pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { + return unmodifiableSortedMultiset( + delegate().headMultiset(upperBound, boundType)); + } + + @Override + public SortedMultiset<E> subMultiset( + E lowerBound, BoundType lowerBoundType, + E upperBound, BoundType upperBoundType) { + return unmodifiableSortedMultiset(delegate().subMultiset( + lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + @Override + public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + return unmodifiableSortedMultiset( + delegate().tailMultiset(lowerBound, boundType)); + } + + private static final long serialVersionUID = 0; + } + /** * Returns an immutable multiset entry with the specified element and count. * The entry will be serializable if {@code e} is. @@ -235,125 +316,152 @@ public final class Multisets { } /** - * Returns a view of the elements of {@code unfiltered} that satisfy a predicate. The returned - * multiset is a live view of {@code unfiltered}; changes to one affect the other. - * - * <p>The resulting multiset's iterators, and those of its {@code entrySet()} and - * {@code elementSet()}, do not support {@code remove()}. However, all other multiset methods - * supported by {@code unfiltered} are supported by the returned multiset. When given an element - * that doesn't satisfy the predicate, the multiset's {@code add()} and {@code addAll()} methods - * throw an {@link IllegalArgumentException}. When methods such as {@code removeAll()} and - * {@code clear()} are called on the filtered multiset, only elements that satisfy the filter - * will be removed from the underlying multiset. + * Returns a multiset view of the specified set. The multiset is backed by the + * set, so changes to the set are reflected in the multiset, and vice versa. + * If the set is modified while an iteration over the multiset is in progress + * (except through the iterator's own {@code remove} operation) the results of + * the iteration are undefined. * - * <p>The returned multiset isn't threadsafe or serializable, even if {@code unfiltered} is. + * <p>The multiset supports element removal, which removes the corresponding + * element from the set. It does not support the {@code add} or {@code addAll} + * operations, nor does it support the use of {@code setCount} to add + * elements. * - * <p>Many of the filtered multiset's methods, such as {@code size()}, iterate across every - * element in the underlying multiset and determine which elements satisfy the filter. When a - * live view is <i>not</i> needed, it may be faster to copy the returned multiset and use the - * copy. + * <p>The returned multiset will be serializable if the specified set is + * serializable. The multiset is threadsafe if the set is threadsafe. * - * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as - * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See - * {@link Iterables#filter(Iterable, Class)} for related functionality.) - * - * @since 14.0 + * @param set the backing set for the returned multiset view */ - @Beta - public static <E> Multiset<E> filter(Multiset<E> unfiltered, Predicate<? super E> predicate) { - if (unfiltered instanceof FilteredMultiset) { - // Support clear(), removeAll(), and retainAll() when filtering a filtered - // collection. - FilteredMultiset<E> filtered = (FilteredMultiset<E>) unfiltered; - Predicate<E> combinedPredicate - = Predicates.<E>and(filtered.predicate, predicate); - return new FilteredMultiset<E>(filtered.unfiltered, combinedPredicate); - } - return new FilteredMultiset<E>(unfiltered, predicate); + static <E> Multiset<E> forSet(Set<E> set) { + return new SetMultiset<E>(set); } - private static final class FilteredMultiset<E> extends AbstractMultiset<E> { - final Multiset<E> unfiltered; - final Predicate<? super E> predicate; + /** @see Multisets#forSet */ + private static class SetMultiset<E> extends ForwardingCollection<E> + implements Multiset<E>, Serializable { + final Set<E> delegate; - FilteredMultiset(Multiset<E> unfiltered, Predicate<? super E> predicate) { - this.unfiltered = checkNotNull(unfiltered); - this.predicate = checkNotNull(predicate); + SetMultiset(Set<E> set) { + delegate = checkNotNull(set); } - @Override - Set<E> createElementSet() { - return Sets.filter(unfiltered.elementSet(), predicate); + @Override protected Set<E> delegate() { + return delegate; } @Override - Set<Entry<E>> createEntrySet() { - return Sets.filter(unfiltered.entrySet(), new Predicate<Entry<E>>() { - @Override - public boolean apply(Entry<E> entry) { - return predicate.apply(entry.getElement()); - } - }); + public int count(Object element) { + return delegate.contains(element) ? 1 : 0; } @Override - Iterator<Entry<E>> entryIterator() { - throw new AssertionError("should never be called"); + public int add(E element, int occurrences) { + throw new UnsupportedOperationException(); } @Override - int distinctElements() { - return elementSet().size(); + public int remove(Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0); + return delegate.remove(element) ? 1 : 0; } + transient Set<E> elementSet; + @Override - public boolean contains(@Nullable Object element) { - return count(element) > 0; + public Set<E> elementSet() { + Set<E> es = elementSet; + return (es == null) ? elementSet = new ElementSet() : es; } - @Override - public int count(@Nullable Object element) { - int count = unfiltered.count(element); - if (count > 0) { - @SuppressWarnings("unchecked") // element is equal to an E - E e = (E) element; - return predicate.apply(e) ? count : 0; + transient Set<Entry<E>> entrySet; + + @Override public Set<Entry<E>> entrySet() { + Set<Entry<E>> es = entrySet; + if (es == null) { + es = entrySet = new EntrySet<E>() { + @Override Multiset<E> multiset() { + return SetMultiset.this; + } + + @Override public Iterator<Entry<E>> iterator() { + return Iterators.transform(delegate.iterator(), + new Function<E, Entry<E>>() { + @Override public Entry<E> apply(E elem) { + return immutableEntry(elem, 1); + } + }); + } + + @Override public int size() { + return delegate.size(); + } + }; } - return 0; + return es; } - @Override - public int add(@Nullable E element, int occurrences) { - checkArgument(predicate.apply(element), - "Element %s does not match predicate %s", element, predicate); - return unfiltered.add(element, occurrences); + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); } @Override - public int remove(@Nullable Object element, int occurrences) { - Multisets.checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(element); + public int setCount(E element, int count) { + checkNonnegative(count, "count"); + + if (count == count(element)) { + return count; + } else if (count == 0) { + remove(element); + return 1; } else { - return contains(element) ? unfiltered.remove(element, occurrences) : 0; + throw new UnsupportedOperationException(); } } @Override - public boolean removeAll(Collection<?> c) { - return elementSet().removeAll(c); + public boolean setCount(E element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); } - @Override - public boolean retainAll(Collection<?> c) { - return elementSet().retainAll(c); + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multiset) { + Multiset<?> that = (Multiset<?>) object; + return this.size() == that.size() && delegate.equals(that.elementSet()); + } + return false; } - @Override - public void clear() { - elementSet().clear(); + @Override public int hashCode() { + int sum = 0; + for (E e : this) { + sum += ((e == null) ? 0 : e.hashCode()) ^ 1; + } + return sum; + } + + /** @see SetMultiset#elementSet */ + class ElementSet extends ForwardingSet<E> { + @Override protected Set<E> delegate() { + return delegate; + } + + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); + } } + + private static final long serialVersionUID = 0; } /** @@ -370,93 +478,16 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the union of two multisets. - * In the returned multiset, the count of each element is the <i>maximum</i> - * of its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that are - * not contained in {@code multiset1}, with repeated occurrences of the same + * Returns an unmodifiable <b>view</b> of the intersection of two multisets. + * An element's count in the multiset is the smaller of its counts in the two + * backing multisets. The iteration order of the returned multiset matches the + * element set of {@code multiset1}, with repeated occurrences of the same * element appearing consecutively. * * <p>Results are undefined if {@code multiset1} and {@code multiset2} are * based on different equivalence relations (as {@code HashMultiset} and * {@code TreeMultiset} are). * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> union( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int count(Object element) { - return Math.max(multiset1.count(element), multiset2.count(element)); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = Math.max(entry1.getCount(), multiset2.count(element)); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the intersection of two multisets. - * In the returned multiset, the count of each element is the <i>minimum</i> - * of its counts in the two backing multisets, with elements that would have - * a count of 0 not included. The iteration order of the returned multiset - * matches that of the element set of {@code multiset1}, with repeated - * occurrences of the same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * * @since 2.0 */ public static <E> Multiset<E> intersection( @@ -488,88 +519,7 @@ public final class Multisets { E element = entry1.getElement(); int count = Math.min(entry1.getCount(), multiset2.count(element)); if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the sum of two multisets. - * In the returned multiset, the count of each element is the <i>sum</i> of - * its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that that - * are not contained in {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> sum( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int size() { - return multiset1.size() + multiset2.size(); - } - - @Override - public int count(Object element) { - return multiset1.count(element) + multiset2.count(element); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() + multiset2.count(element); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); + return Multisets.immutableEntry(element, count); } } return endOfData(); @@ -585,66 +535,12 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the difference of two multisets. - * In the returned multiset, the count of each element is the result of the - * <i>zero-truncated subtraction</i> of its count in the second multiset from - * its count in the first multiset, with elements that would have a count of - * 0 not included. The iteration order of the returned multiset matches that - * of the element set of {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> difference( - final Multiset<E> multiset1, final Multiset<?> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public int count(@Nullable Object element) { - int count1 = multiset1.count(element); - return (count1 == 0) ? 0 : - Math.max(0, count1 - multiset2.count(element)); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<Entry<E>> iterator1 = multiset1.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - while (iterator1.hasNext()) { - Entry<E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() - multiset2.count(element); - if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return Iterators.size(entryIterator()); - } - }; - } - - /** * Returns {@code true} if {@code subMultiset.count(o) <= * superMultiset.count(o)} for all {@code o}. * * @since 10.0 */ + @Beta public static boolean containsOccurrences( Multiset<?> superMultiset, Multiset<?> subMultiset) { checkNotNull(superMultiset); @@ -677,7 +573,7 @@ public final class Multisets { * of this operation * @since 10.0 */ - public static boolean retainOccurrences(Multiset<?> multisetToModify, + @Beta public static boolean retainOccurrences(Multiset<?> multisetToModify, Multiset<?> multisetToRetain) { return retainOccurrencesImpl(multisetToModify, multisetToRetain); } @@ -729,7 +625,7 @@ public final class Multisets { * this operation * @since 10.0 */ - public static boolean removeOccurrences( + @Beta public static boolean removeOccurrences( Multiset<?> multisetToModify, Multiset<?> occurrencesToRemove) { return removeOccurrencesImpl(multisetToModify, occurrencesToRemove); } @@ -864,7 +760,6 @@ public final class Multisets { */ static boolean retainAllImpl( Multiset<?> self, Collection<?> elementsToRetain) { - checkNotNull(elementsToRetain); Collection<?> collection = (elementsToRetain instanceof Multiset) ? ((Multiset<?>) elementsToRetain).elementSet() : elementsToRetain; @@ -905,7 +800,7 @@ public final class Multisets { } } - abstract static class ElementSet<E> extends Sets.ImprovedAbstractSet<E> { + static abstract class ElementSet<E> extends AbstractSet<E> { abstract Multiset<E> multiset(); @Override public void clear() { @@ -925,12 +820,12 @@ public final class Multisets { } @Override public Iterator<E> iterator() { - return new TransformedIterator<Entry<E>, E>(multiset().entrySet().iterator()) { - @Override - E transform(Entry<E> entry) { - return entry.getElement(); - } - }; + return Iterators.transform(multiset().entrySet().iterator(), + new Function<Entry<E>, E>() { + @Override public E apply(Entry<E> entry) { + return entry.getElement(); + } + }); } @Override @@ -948,14 +843,11 @@ public final class Multisets { } } - abstract static class EntrySet<E> extends Sets.ImprovedAbstractSet<Entry<E>> { + static abstract class EntrySet<E> extends AbstractSet<Entry<E>>{ abstract Multiset<E> multiset(); @Override public boolean contains(@Nullable Object o) { if (o instanceof Entry) { - /* - * The GWT compiler wrongly issues a warning here. - */ @SuppressWarnings("cast") Entry<?> entry = (Entry<?>) o; if (entry.getCount() <= 0) { @@ -968,21 +860,10 @@ public final class Multisets { return false; } - // GWT compiler warning; see contains(). @SuppressWarnings("cast") - @Override public boolean remove(Object object) { - if (object instanceof Multiset.Entry) { - Entry<?> entry = (Entry<?>) object; - Object element = entry.getElement(); - int entryCount = entry.getCount(); - if (entryCount != 0) { - // Safe as long as we never add a new entry, which we won't. - @SuppressWarnings("unchecked") - Multiset<Object> multiset = (Multiset) multiset(); - return multiset.setCount(element, entryCount, 0); - } - } - return false; + @Override public boolean remove(Object o) { + return contains(o) + && multiset().elementSet().remove(((Entry<?>) o).getElement()); } @Override public void clear() { @@ -1035,7 +916,8 @@ public final class Multisets { @Override public void remove() { - Iterators.checkRemove(canRemove); + checkState( + canRemove, "no calls to next() since the last call to remove()"); if (totalCount == 1) { entryIterator.remove(); } else { diff --git a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java index c723cea..b40e801 100644 --- a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java @@ -25,10 +25,6 @@ import java.util.Map; /** * A mutable class-to-instance map backed by an arbitrary user-provided map. * See also {@link ImmutableClassToInstanceMap}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#ClassToInstanceMap"> - * {@code ClassToInstanceMap}</a>. * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/ObjectArrays.java b/guava/src/com/google/common/collect/ObjectArrays.java index dfd1fe9..954a30e 100644 --- a/guava/src/com/google/common/collect/ObjectArrays.java +++ b/guava/src/com/google/common/collect/ObjectArrays.java @@ -19,7 +19,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import java.lang.reflect.Array; import java.util.Collection; import javax.annotation.Nullable; @@ -32,8 +31,6 @@ import javax.annotation.Nullable; */ @GwtCompatible(emulated = true) public final class ObjectArrays { - static final Object[] EMPTY_ARRAY = new Object[0]; - private ObjectArrays() {} /** @@ -43,9 +40,8 @@ public final class ObjectArrays { * @param length the length of the new array */ @GwtIncompatible("Array.newInstance(Class, int)") - @SuppressWarnings("unchecked") public static <T> T[] newArray(Class<T> type, int length) { - return (T[]) Array.newInstance(type, length); + return Platform.newArray(type, length); } /** @@ -69,8 +65,8 @@ public final class ObjectArrays { @GwtIncompatible("Array.newInstance(Class, int)") public static <T> T[] concat(T[] first, T[] second, Class<T> type) { T[] result = newArray(type, first.length + second.length); - System.arraycopy(first, 0, result, 0, first.length); - System.arraycopy(second, 0, result, first.length, second.length); + Platform.unsafeArrayCopy(first, 0, result, 0, first.length); + Platform.unsafeArrayCopy(second, 0, result, first.length, second.length); return result; } @@ -86,7 +82,7 @@ public final class ObjectArrays { public static <T> T[] concat(@Nullable T element, T[] array) { T[] result = newArray(array, array.length + 1); result[0] = element; - System.arraycopy(array, 0, result, 1, array.length); + Platform.unsafeArrayCopy(array, 0, result, 1, array.length); return result; } @@ -108,7 +104,7 @@ public final class ObjectArrays { /** GWT safe version of Arrays.copyOf. */ static <T> T[] arraysCopyOf(T[] original, int newLength) { T[] copy = newArray(original, newLength); - System.arraycopy( + Platform.unsafeArrayCopy( original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } @@ -183,13 +179,4 @@ public final class ObjectArrays { array[i] = array[j]; array[j] = temp; } - - // We do this instead of Preconditions.checkNotNull to save boxing and array - // creation cost. - static Object checkElementNotNull(Object element, int index) { - if (element == null) { - throw new NullPointerException("at index " + index); - } - return element; - } } diff --git a/guava/src/com/google/common/collect/Ordering.java b/guava/src/com/google/common/collect/Ordering.java index 9ee9c48..1f0c6e3 100644 --- a/guava/src/com/google/common/collect/Ordering.java +++ b/guava/src/com/google/common/collect/Ordering.java @@ -19,13 +19,12 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -35,15 +34,13 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.SortedSet; -import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; /** - * A comparator, with additional methods to support common operations. This is - * an "enriched" version of {@code Comparator}, in the same sense that {@link - * FluentIterable} is an enriched {@link Iterable}). For example: <pre> {@code + * A comparator with added methods to support common functions. For example: + * <pre> {@code * * if (Ordering.from(comparator).reverse().isOrdered(list)) { ... }}</pre> * @@ -62,17 +59,13 @@ import javax.annotation.Nullable; * are. For example, if {@code ordering} and {@code function} can themselves be * serialized, then {@code ordering.onResultOf(function)} can as well. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/OrderingExplained"> - * {@code Ordering}</a>. - * * @author Jesse Wilson * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible public abstract class Ordering<T> implements Comparator<T> { - // Natural order + // Static factories /** * Returns a serializable ordering that uses the natural order of the values. @@ -89,17 +82,13 @@ public abstract class Ordering<T> implements Comparator<T> { return (Ordering<C>) NaturalOrdering.INSTANCE; } - // Static factories - /** - * Returns an ordering based on an <i>existing</i> comparator instance. Note - * that there's no need to create a <i>new</i> comparator just to pass it in - * here; simply subclass {@code Ordering} and implement its {@code compareTo} - * method directly instead. + * Returns an ordering for a pre-existing {@code comparator}. Note + * that if the comparator is not pre-existing, and you don't require + * serialization, you can subclass {@code Ordering} and implement its + * {@link #compare(Object, Object) compare} method instead. * * @param comparator the comparator that defines the order - * @return comparator itself if it is already an {@code Ordering}; otherwise - * an ordering that wraps that comparator */ @GwtCompatible(serializable = true) public static <T> Ordering<T> from(Comparator<T> comparator) { @@ -173,48 +162,23 @@ public abstract class Ordering<T> implements Comparator<T> { return explicit(Lists.asList(leastValue, remainingValuesInOrder)); } - // Ordering<Object> singletons - /** - * Returns an ordering which treats all values as equal, indicating "no - * ordering." Passing this ordering to any <i>stable</i> sort algorithm - * results in no change to the order of elements. Note especially that {@link - * #sortedCopy} and {@link #immutableSortedCopy} are stable, and in the - * returned instance these are implemented by simply copying the source list. - * - * <p>Example: <pre> {@code - * - * Ordering.allEqual().nullsLast().sortedCopy( - * asList(t, null, e, s, null, t, null))}</pre> - * - * Assuming {@code t}, {@code e} and {@code s} are non-null, this returns - * {@code [t, e, s, t, null, null, null]} regardlesss of the true comparison - * order of those three values (which might not even implement {@link - * Comparable} at all). - * - * <p><b>Warning:</b> by definition, this comparator is not <i>consistent with - * equals</i> (as defined {@linkplain Comparator here}). Avoid its use in - * APIs, such as {@link TreeSet#TreeSet(Comparator)}, where such consistency - * is expected. - * - * <p>The returned comparator is serializable. + * Exception thrown by a {@link Ordering#explicit(List)} or {@link + * Ordering#explicit(Object, Object[])} comparator when comparing a value + * outside the set of values it can compare. Extending {@link + * ClassCastException} may seem odd, but it is required. */ - @GwtCompatible(serializable = true) - @SuppressWarnings("unchecked") - public static Ordering<Object> allEqual() { - return AllEqualOrdering.INSTANCE; - } + // TODO(kevinb): make this public, document it right + @VisibleForTesting + static class IncomparableValueException extends ClassCastException { + final Object value; - /** - * Returns an ordering that compares objects by the natural ordering of their - * string representations as returned by {@code toString()}. It does not - * support null values. - * - * <p>The comparator is serializable. - */ - @GwtCompatible(serializable = true) - public static Ordering<Object> usingToString() { - return UsingToStringOrdering.INSTANCE; + IncomparableValueException(Object value) { + super("Cannot compare value: " + value); + this.value = value; + } + + private static final long serialVersionUID = 0; } /** @@ -256,10 +220,6 @@ public abstract class Ordering<T> implements Comparator<T> { @Override public int compare(Object left, Object right) { if (left == right) { return 0; - } else if (left == null) { - return -1; - } else if (right == null) { - return 1; } int leftCode = identityHashCode(left); int rightCode = identityHashCode(right); @@ -292,62 +252,46 @@ public abstract class Ordering<T> implements Comparator<T> { } } - // Constructor - /** - * Constructs a new instance of this class (only invokable by the subclass - * constructor, typically implicit). - */ - protected Ordering() {} - - // Instance-based factories (and any static equivalents) - - /** - * Returns the reverse of this ordering; the {@code Ordering} equivalent to - * {@link Collections#reverseOrder(Comparator)}. + * Returns an ordering that compares objects by the natural ordering of their + * string representations as returned by {@code toString()}. It does not + * support null values. + * + * <p>The comparator is serializable. */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().reverse(); @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> reverse() { - return new ReverseOrdering<S>(this); + public static Ordering<Object> usingToString() { + return UsingToStringOrdering.INSTANCE; } /** - * Returns an ordering that treats {@code null} as less than all other values - * and uses {@code this} to compare non-null values. + * Returns an ordering which tries each given comparator in order until a + * non-zero result is found, returning that result, and returning zero only if + * all comparators return zero. The returned ordering is based on the state of + * the {@code comparators} iterable at the time it was provided to this + * method. + * + * <p>The returned ordering is equivalent to that produced using {@code + * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. + * + * <p><b>Warning:</b> Supplying an argument with undefined iteration order, + * such as a {@link HashSet}, will produce non-deterministic results. + * + * @param comparators the comparators to try in order */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().nullsFirst(); @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> nullsFirst() { - return new NullsFirstOrdering<S>(this); + public static <T> Ordering<T> compound( + Iterable<? extends Comparator<? super T>> comparators) { + return new CompoundOrdering<T>(comparators); } /** - * Returns an ordering that treats {@code null} as greater than all other - * values and uses this ordering to compare non-null values. + * Constructs a new instance of this class (only invokable by the subclass + * constructor, typically implicit). */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().nullsLast(); - @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> nullsLast() { - return new NullsLastOrdering<S>(this); - } + protected Ordering() {} - /** - * Returns a new ordering on {@code F} which orders elements by first applying - * a function to them, then comparing those results using {@code this}. For - * example, to compare objects by their string forms, in a case-insensitive - * manner, use: <pre> {@code - * - * Ordering.from(String.CASE_INSENSITIVE_ORDER) - * .onResultOf(Functions.toStringFunction())}</pre> - */ - @GwtCompatible(serializable = true) - public <F> Ordering<F> onResultOf(Function<F, ? extends T> function) { - return new ByFunctionOrdering<F, T>(function, this); - } + // Non-static factories /** * Returns an ordering which first uses the ordering {@code this}, but which @@ -367,24 +311,28 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * Returns an ordering which tries each given comparator in order until a - * non-zero result is found, returning that result, and returning zero only if - * all comparators return zero. The returned ordering is based on the state of - * the {@code comparators} iterable at the time it was provided to this - * method. - * - * <p>The returned ordering is equivalent to that produced using {@code - * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. - * - * <p><b>Warning:</b> Supplying an argument with undefined iteration order, - * such as a {@link HashSet}, will produce non-deterministic results. + * Returns the reverse of this ordering; the {@code Ordering} equivalent to + * {@link Collections#reverseOrder(Comparator)}. + */ + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().reverse(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> reverse() { + return new ReverseOrdering<S>(this); + } + + /** + * Returns a new ordering on {@code F} which orders elements by first applying + * a function to them, then comparing those results using {@code this}. For + * example, to compare objects by their string forms, in a case-insensitive + * manner, use: <pre> {@code * - * @param comparators the comparators to try in order + * Ordering.from(String.CASE_INSENSITIVE_ORDER) + * .onResultOf(Functions.toStringFunction())}</pre> */ @GwtCompatible(serializable = true) - public static <T> Ordering<T> compound( - Iterable<? extends Comparator<? super T>> comparators) { - return new CompoundOrdering<T>(comparators); + public <F> Ordering<F> onResultOf(Function<F, ? extends T> function) { + return new ByFunctionOrdering<F, T>(function, this); } /** @@ -416,162 +364,32 @@ public abstract class Ordering<T> implements Comparator<T> { return new LexicographicalOrdering<S>(this); } - // Regular instance methods - - // Override to add @Nullable - @Override public abstract int compare(@Nullable T left, @Nullable T right); - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. The - * iterator will be left exhausted: its {@code hasNext()} method will return - * {@code false}. - * - * @param iterator the iterator whose minimum element is to be determined - * @throws NoSuchElementException if {@code iterator} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - * - * @since 11.0 - */ - public <E extends T> E min(Iterator<E> iterator) { - // let this throw NoSuchElementException as necessary - E minSoFar = iterator.next(); - - while (iterator.hasNext()) { - minSoFar = min(minSoFar, iterator.next()); - } - - return minSoFar; - } - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. - * - * @param iterable the iterable whose minimum element is to be determined - * @throws NoSuchElementException if {@code iterable} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min(Iterable<E> iterable) { - return min(iterable.iterator()); - } - - /** - * Returns the lesser of the two values according to this ordering. If the - * values compare as 0, the first is returned. - * - * <p><b>Implementation note:</b> this method is invoked by the default - * implementations of the other {@code min} overloads, so overriding it will - * affect their behavior. - * - * @param a value to compare, returned if less than or equal to b. - * @param b value to compare. - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min(@Nullable E a, @Nullable E b) { - return (compare(a, b) <= 0) ? a : b; - } - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. - * - * @param a value to compare, returned if less than or equal to the rest. - * @param b value to compare - * @param c value to compare - * @param rest values to compare - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min( - @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { - E minSoFar = min(min(a, b), c); - - for (E r : rest) { - minSoFar = min(minSoFar, r); - } - - return minSoFar; - } - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. The - * iterator will be left exhausted: its {@code hasNext()} method will return - * {@code false}. - * - * @param iterator the iterator whose maximum element is to be determined - * @throws NoSuchElementException if {@code iterator} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - * - * @since 11.0 - */ - public <E extends T> E max(Iterator<E> iterator) { - // let this throw NoSuchElementException as necessary - E maxSoFar = iterator.next(); - - while (iterator.hasNext()) { - maxSoFar = max(maxSoFar, iterator.next()); - } - - return maxSoFar; - } - - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. - * - * @param iterable the iterable whose maximum element is to be determined - * @throws NoSuchElementException if {@code iterable} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. + * Returns an ordering that treats {@code null} as less than all other values + * and uses {@code this} to compare non-null values. */ - public <E extends T> E max(Iterable<E> iterable) { - return max(iterable.iterator()); + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().nullsFirst(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> nullsFirst() { + return new NullsFirstOrdering<S>(this); } /** - * Returns the greater of the two values according to this ordering. If the - * values compare as 0, the first is returned. - * - * <p><b>Implementation note:</b> this method is invoked by the default - * implementations of the other {@code max} overloads, so overriding it will - * affect their behavior. - * - * @param a value to compare, returned if greater than or equal to b. - * @param b value to compare. - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. + * Returns an ordering that treats {@code null} as greater than all other + * values and uses this ordering to compare non-null values. */ - public <E extends T> E max(@Nullable E a, @Nullable E b) { - return (compare(a, b) >= 0) ? a : b; + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().nullsLast(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> nullsLast() { + return new NullsLastOrdering<S>(this); } - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. - * - * @param a value to compare, returned if greater than or equal to the rest. - * @param b value to compare - * @param c value to compare - * @param rest values to compare - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E max( - @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { - E maxSoFar = max(max(a, b), c); - - for (E r : rest) { - maxSoFar = max(maxSoFar, r); - } + // Regular instance methods - return maxSoFar; - } + // Override to add @Nullable + @Override public abstract int compare(@Nullable T left, @Nullable T right); /** * Returns the {@code k} least elements of the given iterable according to @@ -587,130 +405,64 @@ public abstract class Ordering<T> implements Comparator<T> { * @throws IllegalArgumentException if {@code k} is negative * @since 8.0 */ + @Beta public <E extends T> List<E> leastOf(Iterable<E> iterable, int k) { - if (iterable instanceof Collection) { - Collection<E> collection = (Collection<E>) iterable; - if (collection.size() <= 2L * k) { - // In this case, just dumping the collection to an array and sorting is - // faster than using the implementation for Iterator, which is - // specialized for k much smaller than n. - - @SuppressWarnings("unchecked") // c only contains E's and doesn't escape - E[] array = (E[]) collection.toArray(); - Arrays.sort(array, this); - if (array.length > k) { - array = ObjectArrays.arraysCopyOf(array, k); - } - return Collections.unmodifiableList(Arrays.asList(array)); - } + checkArgument(k >= 0, "%d is negative", k); + + // values is not an E[], but we use it as such for readability. Hack. + @SuppressWarnings("unchecked") + E[] values = (E[]) Iterables.toArray(iterable); + + // TODO(nshupe): also sort whole list if k is *near* values.length? + // TODO(kevinb): benchmark this impl against hand-coded heap + E[] resultArray; + if (values.length <= k) { + Arrays.sort(values, this); + resultArray = values; + } else { + quicksortLeastK(values, 0, values.length - 1, k); + + // this is not an E[], but we use it as such for readability. Hack. + @SuppressWarnings("unchecked") + E[] tmp = (E[]) new Object[k]; + resultArray = tmp; + System.arraycopy(values, 0, resultArray, 0, k); } - return leastOf(iterable.iterator(), k); + + return Collections.unmodifiableList(Arrays.asList(resultArray)); } /** - * Returns the {@code k} least elements from the given iterator according to - * this ordering, in order from least to greatest. If there are fewer than + * Returns the {@code k} greatest elements of the given iterable according to + * this ordering, in order from greatest to least. If there are fewer than * {@code k} elements present, all will be included. * * <p>The implementation does not necessarily use a <i>stable</i> sorting * algorithm; when multiple elements are equivalent, it is undefined which * will come first. * - * @return an immutable {@code RandomAccess} list of the {@code k} least - * elements in ascending order + * @return an immutable {@code RandomAccess} list of the {@code k} greatest + * elements in <i>descending order</i> * @throws IllegalArgumentException if {@code k} is negative - * @since 14.0 + * @since 8.0 */ - public <E extends T> List<E> leastOf(Iterator<E> elements, int k) { - checkNotNull(elements); - checkArgument(k >= 0, "k (%s) must be nonnegative", k); - - if (k == 0 || !elements.hasNext()) { - return ImmutableList.of(); - } else if (k >= Integer.MAX_VALUE / 2) { - // k is really large; just do a straightforward sorted-copy-and-sublist - ArrayList<E> list = Lists.newArrayList(elements); - Collections.sort(list, this); - if (list.size() > k) { - list.subList(k, list.size()).clear(); - } - list.trimToSize(); - return Collections.unmodifiableList(list); - } - - /* - * Our goal is an O(n) algorithm using only one pass and O(k) additional - * memory. - * - * We use the following algorithm: maintain a buffer of size 2*k. Every time - * the buffer gets full, find the median and partition around it, keeping - * only the lowest k elements. This requires n/k find-median-and-partition - * steps, each of which take O(k) time with a traditional quickselect. - * - * After sorting the output, the whole algorithm is O(n + k log k). It - * degrades gracefully for worst-case input (descending order), performs - * competitively or wins outright for randomly ordered input, and doesn't - * require the whole collection to fit into memory. - */ - int bufferCap = k * 2; - @SuppressWarnings("unchecked") // we'll only put E's in - E[] buffer = (E[]) new Object[bufferCap]; - E threshold = elements.next(); - buffer[0] = threshold; - int bufferSize = 1; - // threshold is the kth smallest element seen so far. Once bufferSize >= k, - // anything larger than threshold can be ignored immediately. - - while (bufferSize < k && elements.hasNext()) { - E e = elements.next(); - buffer[bufferSize++] = e; - threshold = max(threshold, e); - } - - while (elements.hasNext()) { - E e = elements.next(); - if (compare(e, threshold) >= 0) { - continue; - } - - buffer[bufferSize++] = e; - if (bufferSize == bufferCap) { - // We apply the quickselect algorithm to partition about the median, - // and then ignore the last k elements. - int left = 0; - int right = bufferCap - 1; - - int minThresholdPosition = 0; - // The leftmost position at which the greatest of the k lower elements - // -- the new value of threshold -- might be found. - - while (left < right) { - int pivotIndex = (left + right + 1) >>> 1; - int pivotNewIndex = partition(buffer, left, right, pivotIndex); - if (pivotNewIndex > k) { - right = pivotNewIndex - 1; - } else if (pivotNewIndex < k) { - left = Math.max(pivotNewIndex, left + 1); - minThresholdPosition = pivotNewIndex; - } else { - break; - } - } - bufferSize = k; + @Beta + public <E extends T> List<E> greatestOf(Iterable<E> iterable, int k) { + // TODO(kevinb): see if delegation is hurting performance noticeably + // TODO(kevinb): if we change this implementation, add full unit tests. + return reverse().leastOf(iterable, k); + } - threshold = buffer[minThresholdPosition]; - for (int i = minThresholdPosition + 1; i < bufferSize; i++) { - threshold = max(threshold, buffer[i]); - } + private <E extends T> void quicksortLeastK( + E[] values, int left, int right, int k) { + if (right > left) { + int pivotIndex = (left + right) >>> 1; // left + ((right - left) / 2) + int pivotNewIndex = partition(values, left, right, pivotIndex); + quicksortLeastK(values, left, pivotNewIndex - 1, k); + if (pivotNewIndex < k) { + quicksortLeastK(values, pivotNewIndex + 1, right, k); } } - - Arrays.sort(buffer, 0, bufferSize, this); - - bufferSize = Math.min(bufferSize, k); - return Collections.unmodifiableList( - Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize))); - // We can't use ImmutableList; we have to be null-friendly! } private <E extends T> int partition( @@ -732,41 +484,15 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * Returns the {@code k} greatest elements of the given iterable according to - * this ordering, in order from greatest to least. If there are fewer than - * {@code k} elements present, all will be included. - * - * <p>The implementation does not necessarily use a <i>stable</i> sorting - * algorithm; when multiple elements are equivalent, it is undefined which - * will come first. - * - * @return an immutable {@code RandomAccess} list of the {@code k} greatest - * elements in <i>descending order</i> - * @throws IllegalArgumentException if {@code k} is negative - * @since 8.0 - */ - public <E extends T> List<E> greatestOf(Iterable<E> iterable, int k) { - // TODO(kevinb): see if delegation is hurting performance noticeably - // TODO(kevinb): if we change this implementation, add full unit tests. - return reverse().leastOf(iterable, k); - } - - /** - * Returns the {@code k} greatest elements from the given iterator according to - * this ordering, in order from greatest to least. If there are fewer than - * {@code k} elements present, all will be included. - * - * <p>The implementation does not necessarily use a <i>stable</i> sorting - * algorithm; when multiple elements are equivalent, it is undefined which - * will come first. + * {@link Collections#binarySearch(List, Object, Comparator) Searches} + * {@code sortedList} for {@code key} using the binary search algorithm. The + * list must be sorted using this ordering. * - * @return an immutable {@code RandomAccess} list of the {@code k} greatest - * elements in <i>descending order</i> - * @throws IllegalArgumentException if {@code k} is negative - * @since 14.0 + * @param sortedList the list to be searched + * @param key the key to be searched for */ - public <E extends T> List<E> greatestOf(Iterator<E> iterator, int k) { - return reverse().leastOf(iterator, k); + public int binarySearch(List<? extends T> sortedList, @Nullable T key) { + return Collections.binarySearch(sortedList, key, this); } /** @@ -783,10 +509,9 @@ public abstract class Ordering<T> implements Comparator<T> { * @return a new list containing the given elements in sorted order */ public <E extends T> List<E> sortedCopy(Iterable<E> iterable) { - @SuppressWarnings("unchecked") // does not escape, and contains only E's - E[] array = (E[]) Iterables.toArray(iterable); - Arrays.sort(array, this); - return Lists.newArrayList(Arrays.asList(array)); + List<E> list = Lists.newArrayList(iterable); + Collections.sort(list, this); + return list; } /** @@ -806,13 +531,7 @@ public abstract class Ordering<T> implements Comparator<T> { */ public <E extends T> ImmutableList<E> immutableSortedCopy( Iterable<E> iterable) { - @SuppressWarnings("unchecked") // we'll only ever have E's in here - E[] elements = (E[]) Iterables.toArray(iterable); - for (E e : elements) { - checkNotNull(e); - } - Arrays.sort(elements, this); - return ImmutableList.asImmutableList(elements); + return ImmutableList.copyOf(sortedCopy(iterable)); } /** @@ -858,34 +577,157 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * {@link Collections#binarySearch(List, Object, Comparator) Searches} - * {@code sortedList} for {@code key} using the binary search algorithm. The - * list must be sorted using this ordering. + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. * - * @param sortedList the list to be searched - * @param key the key to be searched for + * @param iterator the iterator whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + * + * @since 11.0 */ - public int binarySearch(List<? extends T> sortedList, @Nullable T key) { - return Collections.binarySearch(sortedList, key, this); + @Beta + public <E extends T> E max(Iterator<E> iterator) { + // let this throw NoSuchElementException as necessary + E maxSoFar = iterator.next(); + + while (iterator.hasNext()) { + maxSoFar = max(maxSoFar, iterator.next()); + } + + return maxSoFar; } /** - * Exception thrown by a {@link Ordering#explicit(List)} or {@link - * Ordering#explicit(Object, Object[])} comparator when comparing a value - * outside the set of values it can compare. Extending {@link - * ClassCastException} may seem odd, but it is required. + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param iterable the iterable whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. */ - // TODO(kevinb): make this public, document it right - @VisibleForTesting - static class IncomparableValueException extends ClassCastException { - final Object value; + public <E extends T> E max(Iterable<E> iterable) { + return max(iterable.iterator()); + } - IncomparableValueException(Object value) { - super("Cannot compare value: " + value); - this.value = value; + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param a value to compare, returned if greater than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E max( + @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E maxSoFar = max(max(a, b), c); + + for (E r : rest) { + maxSoFar = max(maxSoFar, r); } - private static final long serialVersionUID = 0; + return maxSoFar; + } + + /** + * Returns the greater of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + * <p><b>Implementation note:</b> this method is invoked by the default + * implementations of the other {@code max} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if greater than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E max(@Nullable E a, @Nullable E b) { + return compare(a, b) >= 0 ? a : b; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + * + * @since 11.0 + */ + @Beta + public <E extends T> E min(Iterator<E> iterator) { + // let this throw NoSuchElementException as necessary + E minSoFar = iterator.next(); + + while (iterator.hasNext()) { + minSoFar = min(minSoFar, iterator.next()); + } + + return minSoFar; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param iterable the iterable whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min(Iterable<E> iterable) { + return min(iterable.iterator()); + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param a value to compare, returned if less than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min( + @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E minSoFar = min(min(a, b), c); + + for (E r : rest) { + minSoFar = min(minSoFar, r); + } + + return minSoFar; + } + + /** + * Returns the lesser of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + * <p><b>Implementation note:</b> this method is invoked by the default + * implementations of the other {@code min} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if less than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min(@Nullable E a, @Nullable E b) { + return compare(a, b) <= 0 ? a : b; } // Never make these public diff --git a/guava/src/com/google/common/collect/PeekingIterator.java b/guava/src/com/google/common/collect/PeekingIterator.java index 294b2e6..be8989d 100644 --- a/guava/src/com/google/common/collect/PeekingIterator.java +++ b/guava/src/com/google/common/collect/PeekingIterator.java @@ -23,10 +23,6 @@ import java.util.NoSuchElementException; /** * An iterator that supports a one-element lookahead while iterating. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionHelpersExplained#PeekingIterator"> - * {@code PeekingIterator}</a>. * * @author Mick Killianey * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/Platform.java b/guava/src/com/google/common/collect/Platform.java index cd321e8..408563b 100644 --- a/guava/src/com/google/common/collect/Platform.java +++ b/guava/src/com/google/common/collect/Platform.java @@ -17,16 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Maps.EntryTransformer; +import com.google.common.annotations.GwtIncompatible; import java.lang.reflect.Array; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.SortedMap; -import java.util.SortedSet; /** * Methods factored out so that they can be emulated differently in GWT. @@ -44,6 +37,35 @@ class Platform { } /** + * Wrapper around {@link System#arraycopy} so that it can be emulated + * correctly in GWT. + * + * <p>It is only intended for the case {@code src} and {@code dest} are + * different. It also doesn't validate the types and indices. + * + * <p>As of GWT 2.0, The built-in {@link System#arraycopy} doesn't work + * in general case. See + * http://code.google.com/p/google-web-toolkit/issues/detail?id=3621 + * for more details. + */ + static void unsafeArrayCopy( + Object[] src, int srcPos, Object[] dest, int destPos, int length) { + System.arraycopy(src, srcPos, dest, destPos, length); + } + + /** + * Returns a new array of the given length with the specified component type. + * + * @param type the component type + * @param length the length of the new array + */ + @GwtIncompatible("Array.newInstance(Class, int)") + @SuppressWarnings("unchecked") + static <T> T[] newArray(Class<T> type, int length) { + return (T[]) Array.newInstance(type, length); + } + + /** * Returns a new array of the given length with the same type as a reference * array. * @@ -70,34 +92,5 @@ class Platform { return mapMaker.weakKeys(); } - static <K, V1, V2> SortedMap<K, V2> mapsTransformEntriesSortedMap( - SortedMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - return (fromMap instanceof NavigableMap) - ? Maps.transformEntries((NavigableMap<K, V1>) fromMap, transformer) - : Maps.transformEntriesIgnoreNavigable(fromMap, transformer); - } - - static <K, V> SortedMap<K, V> mapsAsMapSortedSet(SortedSet<K> set, - Function<? super K, V> function) { - return (set instanceof NavigableSet) - ? Maps.asMap((NavigableSet<K>) set, function) - : Maps.asMapSortedIgnoreNavigable(set, function); - } - - static <E> SortedSet<E> setsFilterSortedSet(SortedSet<E> set, - Predicate<? super E> predicate) { - return (set instanceof NavigableSet) - ? Sets.filter((NavigableSet<E>) set, predicate) - : Sets.filterSortedIgnoreNavigable(set, predicate); - } - - static <K, V> SortedMap<K, V> mapsFilterSortedMap(SortedMap<K, V> map, - Predicate<? super Map.Entry<K, V>> predicate) { - return (map instanceof NavigableMap) - ? Maps.filterEntries((NavigableMap<K, V>) map, predicate) - : Maps.filterSortedIgnoreNavigable(map, predicate); - } - private Platform() {} } diff --git a/guava/src/com/google/common/collect/Queues.java b/guava/src/com/google/common/collect/Queues.java index d2bf4ad..d68146a 100644 --- a/guava/src/com/google/common/collect/Queues.java +++ b/guava/src/com/google/common/collect/Queues.java @@ -19,7 +19,6 @@ import com.google.common.base.Preconditions; import java.util.ArrayDeque; import java.util.Collection; -import java.util.Deque; import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; @@ -32,12 +31,14 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /** - * Static utility methods pertaining to {@link Queue} and {@link Deque} instances. - * Also see this class's counterparts {@link Lists}, {@link Sets}, and {@link Maps}. + * Static utility methods pertaining to {@link Queue} + * instances. Also see this class's counterparts + * {@link Lists}, {@link Sets}, and {@link Maps}. * * @author Kurt Alfred Kluever * @since 11.0 */ +@Beta public final class Queues { private Queues() {} @@ -54,32 +55,6 @@ public final class Queues { // ArrayDeque - /** - * Creates an empty {@code ArrayDeque} instance. - * - * @return a new, empty {@code ArrayDeque} - * @since 12.0 - */ - public static <E> ArrayDeque<E> newArrayDeque() { - return new ArrayDeque<E>(); - } - - /** - * Creates an {@code ArrayDeque} instance containing the given elements. - * - * @param elements the elements that the queue should contain, in order - * @return a new {@code ArrayDeque} containing those elements - * @since 12.0 - */ - public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) { - if (elements instanceof Collection) { - return new ArrayDeque<E>(Collections2.cast(elements)); - } - ArrayDeque<E> deque = new ArrayDeque<E>(); - Iterables.addAll(deque, elements); - return deque; - } - // ConcurrentLinkedQueue /** @@ -109,44 +84,6 @@ public final class Queues { // LinkedBlockingDeque - /** - * Creates an empty {@code LinkedBlockingDeque} instance. - * - * @return a new, empty {@code LinkedBlockingDeque} - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque() { - return new LinkedBlockingDeque<E>(); - } - - /** - * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. - * - * @param capacity the capacity of this deque - * @return a new, empty {@code LinkedBlockingDeque} - * @throws IllegalArgumentException if {@code capacity} is less than 1 - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(int capacity) { - return new LinkedBlockingDeque<E>(capacity); - } - - /** - * Creates an {@code LinkedBlockingDeque} instance containing the given elements. - * - * @param elements the elements that the queue should contain, in order - * @return a new {@code LinkedBlockingDeque} containing those elements - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) { - if (elements instanceof Collection) { - return new LinkedBlockingDeque<E>(Collections2.cast(elements)); - } - LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>(); - Iterables.addAll(deque, elements); - return deque; - } - // LinkedBlockingQueue /** @@ -249,12 +186,12 @@ public final class Queues { public static <E> SynchronousQueue<E> newSynchronousQueue() { return new SynchronousQueue<E>(); } - + /** - * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested + * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested * {@code numElements} elements are not available, it will wait for them up to the specified * timeout. - * + * * @param q the blocking queue to be drained * @param buffer where to add the transferred elements * @param numElements the number of elements to be waited for @@ -263,7 +200,6 @@ public final class Queues { * @return the number of elements transferred * @throws InterruptedException if interrupted while waiting */ - @Beta public static <E> int drain(BlockingQueue<E> q, Collection<? super E> buffer, int numElements, long timeout, TimeUnit unit) throws InterruptedException { Preconditions.checkNotNull(buffer); @@ -289,13 +225,13 @@ public final class Queues { } return added; } - + /** - * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, - * but with a different behavior in case it is interrupted while waiting. In that case, the - * operation will continue as usual, and in the end the thread's interruption status will be set - * (no {@code InterruptedException} is thrown). - * + * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, + * but with a different behavior in case it is interrupted while waiting. In that case, the + * operation will continue as usual, and in the end the thread's interruption status will be set + * (no {@code InterruptedException} is thrown). + * * @param q the blocking queue to be drained * @param buffer where to add the transferred elements * @param numElements the number of elements to be waited for @@ -303,8 +239,7 @@ public final class Queues { * @param unit a {@code TimeUnit} determining how to interpret the timeout parameter * @return the number of elements transferred */ - @Beta - public static <E> int drainUninterruptibly(BlockingQueue<E> q, Collection<? super E> buffer, + public static <E> int drainUninterruptibly(BlockingQueue<E> q, Collection<? super E> buffer, int numElements, long timeout, TimeUnit unit) { Preconditions.checkNotNull(buffer); long deadline = System.nanoTime() + unit.toNanos(timeout); @@ -312,7 +247,7 @@ public final class Queues { boolean interrupted = false; try { while (added < numElements) { - // we could rely solely on #poll, but #drainTo might be more efficient when there are + // we could rely solely on #poll, but #drainTo might be more efficient when there are // multiple elements already available (e.g. LinkedBlockingQueue#drainTo locks only once) added += q.drainTo(buffer, numElements - added); if (added < numElements) { // not enough elements immediately available; will have to poll @@ -339,36 +274,4 @@ public final class Queues { } return added; } - - /** - * Returns a synchronized (thread-safe) queue backed by the specified queue. In order to - * guarantee serial access, it is critical that <b>all</b> access to the backing queue is - * accomplished through the returned queue. - * - * <p>It is imperative that the user manually synchronize on the returned queue when accessing - * the queue's iterator: <pre> {@code - * - * Queue<E> queue = Queues.synchronizedQueue(MinMaxPriorityQueue<E>.create()); - * ... - * queue.add(element); // Needn't be in synchronized block - * ... - * synchronized (queue) { // Must synchronize on queue! - * Iterator<E> i = queue.iterator(); // Must be in synchronized block - * while (i.hasNext()) { - * foo(i.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned queue will be serializable if the specified queue is serializable. - * - * @param queue the queue to be wrapped in a synchronized view - * @return a synchronized view of the specified queue - * @since 14.0 - */ - @Beta - public static <E> Queue<E> synchronizedQueue(Queue<E> queue) { - return Synchronized.queue(queue, null); - } } diff --git a/guava/src/com/google/common/collect/Range.java b/guava/src/com/google/common/collect/Range.java index e70c34f..c6a9189 100644 --- a/guava/src/com/google/common/collect/Range.java +++ b/guava/src/com/google/common/collect/Range.java @@ -17,17 +17,15 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Ranges.create; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Equivalence; -import com.google.common.base.Function; import com.google.common.base.Predicate; import java.io.Serializable; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; @@ -35,338 +33,94 @@ import java.util.SortedSet; import javax.annotation.Nullable; /** - * A range (or "interval") defines the <i>boundaries</i> around a contiguous span of values of some - * {@code Comparable} type; for example, "integers from 1 to 100 inclusive." Note that it is not - * possible to <i>iterate</i> over these contained values. To do so, pass this range instance and - * an appropriate {@link DiscreteDomain} to {@link ContiguousSet#create}. + * A range, sometimes known as an <i>interval</i>, is a <i>convex</i> + * (informally, "contiguous" or "unbroken") portion of a particular domain. + * Formally, convexity means that for any {@code a <= b <= c}, + * {@code range.contains(a) && range.contains(c)} implies that {@code + * range.contains(b)}. * - * <h3>Types of ranges</h3> + * <p>A range is characterized by its lower and upper <i>bounds</i> (extremes), + * each of which can <i>open</i> (exclusive of its endpoint), <i>closed</i> + * (inclusive of its endpoint), or <i>unbounded</i>. This yields nine basic + * types of ranges: * - * <p>Each end of the range may be bounded or unbounded. If bounded, there is an associated - * <i>endpoint</i> value, and the range is considered to be either <i>open</i> (does not include the - * endpoint) or <i>closed</i> (includes the endpoint) on that side. With three possibilities on each - * side, this yields nine basic types of ranges, enumerated below. (Notation: a square bracket - * ({@code [ ]}) indicates that the range is closed on that side; a parenthesis ({@code ( )}) means - * it is either open or unbounded. The construct {@code {x | statement}} is read "the set of all - * <i>x</i> such that <i>statement</i>.") + * <ul> + * <li>{@code (a..b) = {x | a < x < b}} + * <li>{@code [a..b] = {x | a <= x <= b}} + * <li>{@code [a..b) = {x | a <= x < b}} + * <li>{@code (a..b] = {x | a < x <= b}} + * <li>{@code (a..+∞) = {x | x > a}} + * <li>{@code [a..+∞) = {x | x >= a}} + * <li>{@code (-∞..b) = {x | x < b}} + * <li>{@code (-∞..b] = {x | x <= b}} + * <li>{@code (-∞..+∞) = all values} + * </ul> + * + * (The notation {@code {x | statement}} is read "the set of all <i>x</i> such + * that <i>statement</i>.") * - * <blockquote><table> - * <tr><td><b>Notation</b> <td><b>Definition</b> <td><b>Factory method</b> - * <tr><td>{@code (a..b)} <td>{@code {x | a < x < b}} <td>{@link Range#open open} - * <tr><td>{@code [a..b]} <td>{@code {x | a <= x <= b}}<td>{@link Range#closed closed} - * <tr><td>{@code (a..b]} <td>{@code {x | a < x <= b}} <td>{@link Range#openClosed openClosed} - * <tr><td>{@code [a..b)} <td>{@code {x | a <= x < b}} <td>{@link Range#closedOpen closedOpen} - * <tr><td>{@code (a..+∞)} <td>{@code {x | x > a}} <td>{@link Range#greaterThan greaterThan} - * <tr><td>{@code [a..+∞)} <td>{@code {x | x >= a}} <td>{@link Range#atLeast atLeast} - * <tr><td>{@code (-∞..b)} <td>{@code {x | x < b}} <td>{@link Range#lessThan lessThan} - * <tr><td>{@code (-∞..b]} <td>{@code {x | x <= b}} <td>{@link Range#atMost atMost} - * <tr><td>{@code (-∞..+∞)}<td>{@code {x}} <td>{@link Range#all all} - * </table></blockquote> + * <p>Notice that we use a square bracket ({@code [ ]}) to denote that an range + * is closed on that end, and a parenthesis ({@code ( )}) when it is open or + * unbounded. * - * <p>When both endpoints exist, the upper endpoint may not be less than the lower. The endpoints - * may be equal only if at least one of the bounds is closed: + * <p>The values {@code a} and {@code b} used above are called <i>endpoints</i>. + * The upper endpoint may not be less than the lower endpoint. The endpoints may + * be equal only if at least one of the bounds is closed: * * <ul> - * <li>{@code [a..a]} : a singleton range - * <li>{@code [a..a); (a..a]} : {@linkplain #isEmpty empty} ranges; also valid - * <li>{@code (a..a)} : <b>invalid</b>; an exception will be thrown + * <li>{@code [a..a]} : singleton range + * <li>{@code [a..a); (a..a]} : {@linkplain #isEmpty empty}, but valid + * <li>{@code (a..a)} : <b>invalid</b> * </ul> * - * <h3>Warnings</h3> + * <p>Instances of this type can be obtained using the static factory methods in + * the {@link Ranges} class. * - * <ul> - * <li>Use immutable value types only, if at all possible. If you must use a mutable type, <b>do - * not</b> allow the endpoint instances to mutate after the range is created! - * <li>Your value type's comparison method should be {@linkplain Comparable consistent with equals} - * if at all possible. Otherwise, be aware that concepts used throughout this documentation such - * as "equal", "same", "unique" and so on actually refer to whether {@link Comparable#compareTo - * compareTo} returns zero, not whether {@link Object#equals equals} returns {@code true}. - * <li>A class which implements {@code Comparable<UnrelatedType>} is very broken, and will cause - * undefined horrible things to happen in {@code Range}. For now, the Range API does not prevent - * its use, because this would also rule out all ungenerified (pre-JDK1.5) data types. <b>This - * may change in the future.</b> - * </ul> + * <p>Instances of {@code Range} are immutable. It is strongly encouraged to + * use this class only with immutable data types. When creating a range over a + * mutable type, take great care not to allow the value objects to mutate after + * the range is created. * - * <h3>Other notes</h3> + * <p>In this and other range-related specifications, concepts like "equal", + * "same", "unique" and so on are based on {@link Comparable#compareTo} + * returning zero, not on {@link Object#equals} returning {@code true}. Of + * course, when these methods are kept <i>consistent</i> (as defined in {@link + * Comparable}), this is not an issue. * - * <ul> - * <li>Instances of this type are obtained using the static factory methods in this class. - * <li>Ranges are <i>convex</i>: whenever two values are contained, all values in between them must - * also be contained. More formally, for any {@code c1 <= c2 <= c3} of type {@code C}, {@code - * r.contains(c1) && r.contains(c3)} implies {@code r.contains(c2)}). This means that a {@code - * Range<Integer>} can never be used to represent, say, "all <i>prime</i> numbers from 1 to - * 100." - * <li>When evaluated as a {@link Predicate}, a range yields the same result as invoking {@link - * #contains}. - * <li>Terminology note: a range {@code a} is said to be the <i>maximal</i> range having property - * <i>P</i> if, for all ranges {@code b} also having property <i>P</i>, {@code a.encloses(b)}. - * Likewise, {@code a} is <i>minimal</i> when {@code b.encloses(a)} for all {@code b} having - * property <i>P</i>. See, for example, the definition of {@link #intersection intersection}. - * </ul> + * <p>A range {@code a} is said to be the <i>maximal</i> range having property + * <i>P</i> if, for all ranges {@code b} also having property <i>P</i>, {@code + * a.encloses(b)}. Likewise, {@code a} is <i>minimal</i> when {@code + * b.encloses(a)} for all {@code b} having property <i>P</i>. See, for example, + * the definition of {@link #intersection}. * - * <h3>Further reading</h3> + * <p>This class can be used with any type which implements {@code Comparable}; + * it does not require {@code Comparable<? super C>} because this would be + * incompatible with pre-Java 5 types. If this class is used with a perverse + * {@code Comparable} type ({@code Foo implements Comparable<Bar>} where {@code + * Bar} is not a supertype of {@code Foo}), any of its methods may throw {@link + * ClassCastException}. (There is no good reason for such a type to exist.) * - * <p>See the Guava User Guide article on - * <a href="http://code.google.com/p/guava-libraries/wiki/RangesExplained">{@code Range}</a>. + * <p>When evaluated as a {@link Predicate}, a range yields the same result as + * invoking {@link #contains}. * * @author Kevin Bourrillion * @author Gregory Kick * @since 10.0 */ @GwtCompatible -@SuppressWarnings("rawtypes") -public final class Range<C extends Comparable> implements Predicate<C>, Serializable { - - private static final Function<Range, Cut> LOWER_BOUND_FN = new Function<Range, Cut>() { - @Override - public Cut apply(Range range) { - return range.lowerBound; - } - }; - - @SuppressWarnings("unchecked") - static <C extends Comparable<?>> Function<Range<C>, Cut<C>> lowerBoundFn() { - return (Function) LOWER_BOUND_FN; - } - - private static final Function<Range, Cut> UPPER_BOUND_FN = new Function<Range, Cut>() { - @Override - public Cut apply(Range range) { - return range.upperBound; - } - }; - - @SuppressWarnings("unchecked") - static <C extends Comparable<?>> Function<Range<C>, Cut<C>> upperBoundFn() { - return (Function) UPPER_BOUND_FN; - } - - static final Ordering<Range<?>> RANGE_LEX_ORDERING = new Ordering<Range<?>>() { - @Override - public int compare(Range<?> left, Range<?> right) { - return ComparisonChain.start() - .compare(left.lowerBound, right.lowerBound) - .compare(left.upperBound, right.upperBound) - .result(); - } - }; - - static <C extends Comparable<?>> Range<C> create( - Cut<C> lowerBound, Cut<C> upperBound) { - return new Range<C>(lowerBound, upperBound); - } - - /** - * Returns a range that contains all values strictly greater than {@code - * lower} and strictly less than {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than <i>or - * equal to</i> {@code upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> open(C lower, C upper) { - return create(Cut.aboveValue(lower), Cut.belowValue(upper)); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code lower} and less than or equal to {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> closed(C lower, C upper) { - return create(Cut.belowValue(lower), Cut.aboveValue(upper)); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code lower} and strictly less than {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> closedOpen( - C lower, C upper) { - return create(Cut.belowValue(lower), Cut.belowValue(upper)); - } - - /** - * Returns a range that contains all values strictly greater than {@code - * lower} and less than or equal to {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> openClosed( - C lower, C upper) { - return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); - } - - /** - * Returns a range that contains any value from {@code lower} to {@code - * upper}, where each endpoint may be either inclusive (closed) or exclusive - * (open). - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> range( - C lower, BoundType lowerType, C upper, BoundType upperType) { - checkNotNull(lowerType); - checkNotNull(upperType); - - Cut<C> lowerBound = (lowerType == BoundType.OPEN) - ? Cut.aboveValue(lower) - : Cut.belowValue(lower); - Cut<C> upperBound = (upperType == BoundType.OPEN) - ? Cut.belowValue(upper) - : Cut.aboveValue(upper); - return create(lowerBound, upperBound); - } - - /** - * Returns a range that contains all values strictly less than {@code - * endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> lessThan(C endpoint) { - return create(Cut.<C>belowAll(), Cut.belowValue(endpoint)); - } - - /** - * Returns a range that contains all values less than or equal to - * {@code endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> atMost(C endpoint) { - return create(Cut.<C>belowAll(), Cut.aboveValue(endpoint)); - } - - /** - * Returns a range with no lower bound up to the given endpoint, which may be - * either inclusive (closed) or exclusive (open). - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> upTo( - C endpoint, BoundType boundType) { - switch (boundType) { - case OPEN: - return lessThan(endpoint); - case CLOSED: - return atMost(endpoint); - default: - throw new AssertionError(); - } - } - - /** - * Returns a range that contains all values strictly greater than {@code - * endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> greaterThan(C endpoint) { - return create(Cut.aboveValue(endpoint), Cut.<C>aboveAll()); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> atLeast(C endpoint) { - return create(Cut.belowValue(endpoint), Cut.<C>aboveAll()); - } - - /** - * Returns a range from the given endpoint, which may be either inclusive - * (closed) or exclusive (open), with no upper bound. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> downTo( - C endpoint, BoundType boundType) { - switch (boundType) { - case OPEN: - return greaterThan(endpoint); - case CLOSED: - return atLeast(endpoint); - default: - throw new AssertionError(); - } - } - - private static final Range<Comparable> ALL = - new Range<Comparable>(Cut.belowAll(), Cut.aboveAll()); - - /** - * Returns a range that contains every value of type {@code C}. - * - * @since 14.0 - */ - @SuppressWarnings("unchecked") - public static <C extends Comparable<?>> Range<C> all() { - return (Range) ALL; - } - - /** - * Returns a range that {@linkplain Range#contains(Comparable) contains} only - * the given value. The returned range is {@linkplain BoundType#CLOSED closed} - * on both ends. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> singleton(C value) { - return closed(value, value); - } - - /** - * Returns the minimal range that - * {@linkplain Range#contains(Comparable) contains} all of the given values. - * The returned range is {@linkplain BoundType#CLOSED closed} on both ends. - * - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> - * @throws NoSuchElementException if {@code values} is empty - * @throws NullPointerException if any of {@code values} is null - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> encloseAll( - Iterable<C> values) { - checkNotNull(values); - if (values instanceof ContiguousSet) { - return ((ContiguousSet<C>) values).range(); - } - Iterator<C> valueIterator = values.iterator(); - C min = checkNotNull(valueIterator.next()); - C max = min; - while (valueIterator.hasNext()) { - C value = checkNotNull(valueIterator.next()); - min = Ordering.natural().min(min, value); - max = Ordering.natural().max(max, value); - } - return closed(min, max); - } - +@Beta +public final class Range<C extends Comparable> + implements Predicate<C>, Serializable { final Cut<C> lowerBound; final Cut<C> upperBound; - private Range(Cut<C> lowerBound, Cut<C> upperBound) { - if (lowerBound.compareTo(upperBound) > 0 || lowerBound == Cut.<C>aboveAll() - || upperBound == Cut.<C>belowAll()) { - throw new IllegalArgumentException("Invalid range: " + toString(lowerBound, upperBound)); + Range(Cut<C> lowerBound, Cut<C> upperBound) { + if (lowerBound.compareTo(upperBound) > 0) { + throw new IllegalArgumentException( + "Invalid range: " + toString(lowerBound, upperBound)); } - this.lowerBound = checkNotNull(lowerBound); - this.upperBound = checkNotNull(upperBound); + this.lowerBound = lowerBound; + this.upperBound = upperBound; } /** @@ -379,19 +133,20 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ /** * Returns the lower endpoint of this range. * - * @throws IllegalStateException if this range is unbounded below (that is, {@link - * #hasLowerBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) */ public C lowerEndpoint() { return lowerBound.endpoint(); } /** - * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if the range includes - * its lower endpoint, {@link BoundType#OPEN} if it does not. + * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if + * the range includes its lower endpoint, {@link BoundType#OPEN} if it does + * not. * - * @throws IllegalStateException if this range is unbounded below (that is, {@link - * #hasLowerBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) */ public BoundType lowerBoundType() { return lowerBound.typeAsLowerBound(); @@ -407,41 +162,42 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ /** * Returns the upper endpoint of this range. * - * @throws IllegalStateException if this range is unbounded above (that is, {@link - * #hasUpperBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) */ public C upperEndpoint() { return upperBound.endpoint(); } /** - * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if the range includes - * its upper endpoint, {@link BoundType#OPEN} if it does not. + * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if + * the range includes its upper endpoint, {@link BoundType#OPEN} if it does + * not. * - * @throws IllegalStateException if this range is unbounded above (that is, {@link - * #hasUpperBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) */ public BoundType upperBoundType() { return upperBound.typeAsUpperBound(); } /** - * Returns {@code true} if this range is of the form {@code [v..v)} or {@code (v..v]}. (This does - * not encompass ranges of the form {@code (v..v)}, because such ranges are <i>invalid</i> and - * can't be constructed at all.) + * Returns {@code true} if this range is of the form {@code [v..v)} or {@code + * (v..v]}. (This does not encompass ranges of the form {@code (v..v)}, + * because such ranges are <i>invalid</i> and can't be constructed at all.) * - * <p>Note that certain discrete ranges such as the integer range {@code (3..4)} are <b>not</b> - * considered empty, even though they contain no actual values. In these cases, it may be - * helpful to preprocess ranges with {@link #canonical(DiscreteDomain)}. + * <p>Note that certain discrete ranges such as the integer range {@code + * (3..4)} are <b>not</b> considered empty, even though they contain no actual + * values. */ public boolean isEmpty() { return lowerBound.equals(upperBound); } /** - * Returns {@code true} if {@code value} is within the bounds of this range. For example, on the - * range {@code [0..2)}, {@code contains(1)} returns {@code true}, while {@code contains(2)} - * returns {@code false}. + * Returns {@code true} if {@code value} is within the bounds of this + * range. For example, on the range {@code [0..2)}, {@code contains(1)} + * returns {@code true}, while {@code contains(2)} returns {@code false}. */ public boolean contains(C value) { checkNotNull(value); @@ -450,16 +206,17 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Equivalent to {@link #contains}; provided only to satisfy the {@link Predicate} interface. When - * using a reference of type {@code Range}, always invoke {@link #contains} directly instead. + * Equivalent to {@link #contains}; provided only to satisfy the {@link + * Predicate} interface. When using a reference of type {@code Range}, always + * invoke {@link #contains} directly instead. */ @Override public boolean apply(C input) { return contains(input); } /** - * Returns {@code true} if every element in {@code values} is {@linkplain #contains contained} in - * this range. + * Returns {@code true} if every element in {@code values} is {@linkplain + * #contains contained} in this range. */ public boolean containsAll(Iterable<? extends C> values) { if (Iterables.isEmpty(values)) { @@ -484,27 +241,42 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns {@code true} if the bounds of {@code other} do not extend outside the bounds of this - * range. Examples: + * Returns {@code true} if the bounds of {@code other} do not extend outside + * the bounds of this range. Examples: * * <ul> * <li>{@code [3..6]} encloses {@code [4..5]} * <li>{@code (3..6)} encloses {@code (3..6)} - * <li>{@code [3..6]} encloses {@code [4..4)} (even though the latter is empty) + * <li>{@code [3..6]} encloses {@code [4..4)} (even though the latter is + * empty) * <li>{@code (3..6]} does not enclose {@code [3..6]} - * <li>{@code [4..5]} does not enclose {@code (3..6)} (even though it contains every value - * contained by the latter range) - * <li>{@code [3..6]} does not enclose {@code (1..1]} (even though it contains every value - * contained by the latter range) + * <li>{@code [4..5]} does not enclose {@code (3..6)} (even though it contains + * every value contained by the latter range) + * <li>{@code [3..6]} does not enclose {@code (1..1]} (even though it contains + * every value contained by the latter range) * </ul> * - * Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies {@code a.contains(v)}, - * but as the last two examples illustrate, the converse is not always true. + * Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies + * {@code a.contains(v)}, but as the last two examples illustrate, the + * converse is not always true. * - * <p>Being reflexive, antisymmetric and transitive, the {@code encloses} relation defines a - * <i>partial order</i> over ranges. There exists a unique {@linkplain Range#all maximal} range - * according to this relation, and also numerous {@linkplain #isEmpty minimal} ranges. Enclosure - * also implies {@linkplain #isConnected connectedness}. + * <p>The encloses relation has the following properties: + * + * <ul> + * <li>reflexive: {@code a.encloses(a)} is always true + * <li>antisymmetric: {@code a.encloses(b) && b.encloses(a)} implies {@code + * a.equals(b)} + * <li>transitive: {@code a.encloses(b) && b.encloses(c)} implies {@code + * a.encloses(c)} + * <li>not a total ordering: {@code !a.encloses(b)} does not imply {@code + * b.encloses(a)} + * <li>there exists a {@linkplain Ranges#all maximal} range, for which + * {@code encloses} is always true + * <li>there also exist {@linkplain #isEmpty minimal} ranges, for + * which {@code encloses(b)} is always false when {@code !equals(b)} + * <li>if {@code a.encloses(b)}, then {@link #isConnected a.isConnected(b)} + * is {@code true}. + * </ul> */ public boolean encloses(Range<C> other) { return lowerBound.compareTo(other.lowerBound) <= 0 @@ -512,133 +284,160 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns {@code true} if there exists a (possibly empty) range which is {@linkplain #encloses - * enclosed} by both this range and {@code other}. + * Returns the maximal range {@linkplain #encloses enclosed} by both this + * range and {@code other}, if such a range exists. + * + * <p>For example, the intersection of {@code [1..5]} and {@code (3..7)} is + * {@code (3..5]}. The resulting range may be empty; for example, + * {@code [1..5)} intersected with {@code [5..7)} yields the empty range + * {@code [5..5)}. + * + * <p>Generally, the intersection exists if and only if this range and + * {@code other} are {@linkplain #isConnected connected}. + * + * <p>The intersection operation has the following properties: * - * <p>For example, * <ul> - * <li>{@code [2, 4)} and {@code [5, 7)} are not connected - * <li>{@code [2, 4)} and {@code [3, 5)} are connected, because both enclose {@code [3, 4)} - * <li>{@code [2, 4)} and {@code [4, 6)} are connected, because both enclose the empty range - * {@code [4, 4)} + * <li>commutative: {@code a.intersection(b)} produces the same result as + * {@code b.intersection(a)} + * <li>associative: {@code a.intersection(b).intersection(c)} produces the + * same result as {@code a.intersection(b.intersection(c))} + * <li>idempotent: {@code a.intersection(a)} equals {@code a} + * <li>identity ({@link Ranges#all}): {@code a.intersection(Ranges.all())} + * equals {@code a} * </ul> * - * <p>Note that this range and {@code other} have a well-defined {@linkplain #span union} and - * {@linkplain #intersection intersection} (as a single, possibly-empty range) if and only if this - * method returns {@code true}. - * - * <p>The connectedness relation is both reflexive and symmetric, but does not form an {@linkplain - * Equivalence equivalence relation} as it is not transitive. - * - * <p>Note that certain discrete ranges are not considered connected, even though there are no - * elements "between them." For example, {@code [3, 5]} is not considered connected to {@code - * [6, 10]}. In these cases, it may be desirable for both input ranges to be preprocessed with - * {@link #canonical(DiscreteDomain)} before testing for connectedness. + * @throws IllegalArgumentException if no range exists that is enclosed by + * both these ranges */ - public boolean isConnected(Range<C> other) { - return lowerBound.compareTo(other.upperBound) <= 0 - && other.lowerBound.compareTo(upperBound) <= 0; + public Range<C> intersection(Range<C> other) { + Cut<C> newLower = Ordering.natural().max(lowerBound, other.lowerBound); + Cut<C> newUpper = Ordering.natural().min(upperBound, other.upperBound); + return create(newLower, newUpper); } /** - * Returns the maximal range {@linkplain #encloses enclosed} by both this range and {@code - * connectedRange}, if such a range exists. - * - * <p>For example, the intersection of {@code [1..5]} and {@code (3..7)} is {@code (3..5]}. The - * resulting range may be empty; for example, {@code [1..5)} intersected with {@code [5..7)} - * yields the empty range {@code [5..5)}. - * - * <p>The intersection exists if and only if the two ranges are {@linkplain #isConnected - * connected}. - * - * <p>The intersection operation is commutative, associative and idempotent, and its identity - * element is {@link Range#all}). + * Returns {@code true} if there exists a (possibly empty) range which is + * {@linkplain #encloses enclosed} by both this range and {@code other}. + * + * <p>For example, + * <ul> + * <li>{@code [2, 4)} and {@code [5, 7)} are not connected + * <li>{@code [2, 4)} and {@code [3, 5)} are connected, because both enclose + * {@code [3, 4)} + * <li>{@code [2, 4)} and {@code [4, 6)} are connected, because both enclose + * the empty range {@code [4, 4)} + * </ul> + * + * <p>Note that this range and {@code other} have a well-defined {@linkplain + * #span union} and {@linkplain #intersection intersection} (as a single, + * possibly-empty range) if and only if this method returns {@code true}. + * + * <p>The connectedness relation has the following properties: * - * @throws IllegalArgumentException if {@code isConnected(connectedRange)} is {@code false} + * <ul> + * <li>symmetric: {@code a.isConnected(b)} produces the same result as + * {@code b.isConnected(a)} + * <li>reflexive: {@code a.isConnected(a)} returns {@code true} + * </ul> */ - public Range<C> intersection(Range<C> connectedRange) { - int lowerCmp = lowerBound.compareTo(connectedRange.lowerBound); - int upperCmp = upperBound.compareTo(connectedRange.upperBound); - if (lowerCmp >= 0 && upperCmp <= 0) { - return this; - } else if (lowerCmp <= 0 && upperCmp >= 0) { - return connectedRange; - } else { - Cut<C> newLower = (lowerCmp >= 0) ? lowerBound : connectedRange.lowerBound; - Cut<C> newUpper = (upperCmp <= 0) ? upperBound : connectedRange.upperBound; - return create(newLower, newUpper); - } + public boolean isConnected(Range<C> other) { + return lowerBound.compareTo(other.upperBound) <= 0 + && other.lowerBound.compareTo(upperBound) <= 0; } /** - * Returns the minimal range that {@linkplain #encloses encloses} both this range and {@code - * other}. For example, the span of {@code [1..3]} and {@code (5..7)} is {@code [1..7)}. + * Returns the minimal range that {@linkplain #encloses encloses} both this + * range and {@code other}. For example, the span of {@code [1..3]} and + * {@code (5..7)} is {@code [1..7)}. Note that the span may contain values + * that are not contained by either original range. * - * <p><i>If</i> the input ranges are {@linkplain #isConnected connected}, the returned range can - * also be called their <i>union</i>. If they are not, note that the span might contain values - * that are not contained in either input range. + * <p>The span operation has the following properties: * - * <p>Like {@link #intersection(Range) intersection}, this operation is commutative, associative - * and idempotent. Unlike it, it is always well-defined for any two input ranges. + * <ul> + * <li>closed: the range {@code a.span(b)} exists for all ranges {@code a} and + * {@code b} + * <li>commutative: {@code a.span(b)} equals {@code b.span(a)} + * <li>associative: {@code a.span(b).span(c)} equals {@code a.span(b.span(c))} + * <li>idempotent: {@code a.span(a)} equals {@code a} + * </ul> + * + * <p>Note that the returned range is also called the <i>union</i> of this + * range and {@code other} if and only if the ranges are + * {@linkplain #isConnected connected}. */ public Range<C> span(Range<C> other) { - int lowerCmp = lowerBound.compareTo(other.lowerBound); - int upperCmp = upperBound.compareTo(other.upperBound); - if (lowerCmp <= 0 && upperCmp >= 0) { - return this; - } else if (lowerCmp >= 0 && upperCmp <= 0) { - return other; - } else { - Cut<C> newLower = (lowerCmp <= 0) ? lowerBound : other.lowerBound; - Cut<C> newUpper = (upperCmp >= 0) ? upperBound : other.upperBound; - return create(newLower, newUpper); - } + Cut<C> newLower = Ordering.natural().min(lowerBound, other.lowerBound); + Cut<C> newUpper = Ordering.natural().max(upperBound, other.upperBound); + return create(newLower, newUpper); } /** - * Returns an {@link ContiguousSet} containing the same values in the given domain - * {@linkplain Range#contains contained} by this range. + * Returns an {@link ImmutableSortedSet} containing the same values in the + * given domain {@linkplain Range#contains contained} by this range. * - * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For - * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty - * ranges {@code [3..3)} and {@code [4..4)}. + * <p><b>Note:</b> {@code a.asSet().equals(b.asSet())} does not imply {@code + * a.equals(b)}! For example, {@code a} and {@code b} could be {@code [2..4]} + * and {@code (1..5)}, or the empty ranges {@code [3..3)} and {@code [4..4)}. * - * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large - * range (such as {@code Range.greaterThan(0)}). Certain operations on such a set can be - * performed efficiently, but others (such as {@link Set#hashCode} or {@link - * Collections#frequency}) can cause major performance problems. + * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} + * view of a large range (such as {@code Ranges.greaterThan(0)}). Certain + * operations on such a set can be performed efficiently, but others (such as + * {@link Set#hashCode} or {@link Collections#frequency}) can cause major + * performance problems. * - * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's - * contents, such as {@code "[1..100]}"}. + * <p>The returned set's {@link Object#toString} method returns a short-hand + * form of set's contents such as {@code "[1..100]}"}. * - * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if - * neither has an upper bound - * @deprecated Use {@code ContiguousSet.create(range, domain)} instead. + * @throws IllegalArgumentException if neither this range nor the domain has a + * lower bound, or if neither has an upper bound */ // TODO(kevinb): commit in spec to which methods are efficient? - @Beta @GwtCompatible(serializable = false) - @Deprecated public ContiguousSet<C> asSet(DiscreteDomain<C> domain) { - return ContiguousSet.create(this, domain); + checkNotNull(domain); + Range<C> effectiveRange = this; + try { + if (!hasLowerBound()) { + effectiveRange = effectiveRange.intersection( + Ranges.atLeast(domain.minValue())); + } + if (!hasUpperBound()) { + effectiveRange = effectiveRange.intersection( + Ranges.atMost(domain.maxValue())); + } + } catch (NoSuchElementException e) { + throw new IllegalArgumentException(e); + } + + // Per class spec, we are allowed to throw CCE if necessary + boolean empty = effectiveRange.isEmpty() + || compareOrThrow( + lowerBound.leastValueAbove(domain), + upperBound.greatestValueBelow(domain)) > 0; + + return empty + ? new EmptyContiguousSet<C>(domain) + : new RegularContiguousSet<C>(effectiveRange, domain); } /** - * Returns the canonical form of this range in the given domain. The canonical form has the - * following properties: + * Returns the canonical form of this range in the given domain. The canonical + * form has the following properties: * * <ul> - * <li>equivalence: {@code a.canonical().contains(v) == a.contains(v)} for all {@code v} (in other - * words, {@code ContiguousSet.create(a.canonical(domain), domain).equals( - * ContiguousSet.create(a, domain))} + * <li>equivalence: {@code a.canonical().contains(v) == a.contains(v)} for + * all {@code v} (in other words, {@code + * a.canonical(domain).asSet(domain).equals(a.asSet(domain))} * <li>uniqueness: unless {@code a.isEmpty()}, - * {@code ContiguousSet.create(a, domain).equals(ContiguousSet.create(b, domain))} implies + * {@code a.asSet(domain).equals(b.asSet(domain))} implies * {@code a.canonical(domain).equals(b.canonical(domain))} - * <li>idempotence: {@code a.canonical(domain).canonical(domain).equals(a.canonical(domain))} + * <li>idempotence: {@code + * a.canonical(domain).canonical(domain).equals(a.canonical(domain))} * </ul> * - * Furthermore, this method guarantees that the range returned will be one of the following - * canonical forms: + * Furthermore, this method guarantees that the range returned will be one + * of the following canonical forms: * * <ul> * <li>[start..end) @@ -651,15 +450,18 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ checkNotNull(domain); Cut<C> lower = lowerBound.canonical(domain); Cut<C> upper = upperBound.canonical(domain); - return (lower == lowerBound && upper == upperBound) ? this : create(lower, upper); + return (lower == lowerBound && upper == upperBound) + ? this : create(lower, upper); } /** - * Returns {@code true} if {@code object} is a range having the same endpoints and bound types as - * this range. Note that discrete ranges such as {@code (1..4)} and {@code [2..3]} are <b>not</b> - * equal to one another, despite the fact that they each contain precisely the same set of values. - * Similarly, empty ranges are not equal unless they have exactly the same representation, so - * {@code [3..3)}, {@code (3..3]}, {@code (4..4]} are all unequal. + * Returns {@code true} if {@code object} is a range having the same + * endpoints and bound types as this range. Note that discrete ranges + * such as {@code (1..4)} and {@code [2..3]} are <b>not</b> equal to one + * another, despite the fact that they each contain precisely the same set of + * values. Similarly, empty ranges are not equal unless they have exactly + * the same representation, so {@code [3..3)}, {@code (3..3]}, {@code (4..4]} + * are all unequal. */ @Override public boolean equals(@Nullable Object object) { if (object instanceof Range) { @@ -676,8 +478,8 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns a string representation of this range, such as {@code "[3..5)"} (other examples are - * listed in the class documentation). + * Returns a string representation of this range, such as {@code "[3..5)"} + * (other examples are listed in the class documentation). */ @Override public String toString() { return toString(lowerBound, upperBound); @@ -698,14 +500,6 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ return (SortedSet<T>) iterable; } - Object readResolve() { - if (this.equals(ALL)) { - return all(); - } else { - return this; - } - } - @SuppressWarnings("unchecked") // this method may throw CCE static int compareOrThrow(Comparable left, Comparable right) { return left.compareTo(right); diff --git a/guava/src/com/google/common/collect/RangeMap.java b/guava/src/com/google/common/collect/RangeMap.java deleted file mode 100644 index ba42985..0000000 --- a/guava/src/com/google/common/collect/RangeMap.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.Beta; - -import java.util.Map; - -import javax.annotation.Nullable; - -/** - * A mapping from disjoint nonempty ranges to non-null values. Queries look up the value - * associated with the range (if any) that contains a specified key. - * - * <p>In contrast to {@link RangeSet}, no "coalescing" is done of {@linkplain - * Range#isConnected(Range) connected} ranges, even if they are mapped to the same value. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public interface RangeMap<K extends Comparable, V> { - /** - * Returns the value associated with the specified key, or {@code null} if there is no - * such value. - * - * <p>Specifically, if any range in this range map contains the specified key, the value - * associated with that range is returned. - */ - @Nullable - V get(K key); - - /** - * Returns the range containing this key and its associated value, if such a range is present - * in the range map, or {@code null} otherwise. - */ - @Nullable - Map.Entry<Range<K>, V> getEntry(K key); - - /** - * Returns the minimal range {@linkplain Range#encloses(Range) enclosing} the ranges - * in this {@code RangeMap}. - * - * @throws NoSuchElementException if this range map is empty - */ - Range<K> span(); - - /** - * Maps a range to a specified value (optional operation). - * - * <p>Specifically, after a call to {@code put(range, value)}, if - * {@link Range#contains(Comparable) range.contains(k)}, then {@link #get(Comparable) get(k)} - * will return {@code value}. - * - * <p>If {@code range} {@linkplain Range#isEmpty() is empty}, then this is a no-op. - */ - void put(Range<K> range, V value); - - /** - * Puts all the associations from {@code rangeMap} into this range map (optional operation). - */ - void putAll(RangeMap<K, V> rangeMap); - - /** - * Removes all associations from this range map (optional operation). - */ - void clear(); - - /** - * Removes all associations from this range map in the specified range (optional operation). - * - * <p>If {@code !range.contains(k)}, {@link #get(Comparable) get(k)} will return the same result - * before and after a call to {@code remove(range)}. If {@code range.contains(k)}, then - * after a call to {@code remove(range)}, {@code get(k)} will return {@code null}. - */ - void remove(Range<K> range); - - /** - * Returns a view of this range map as an unmodifiable {@code Map<Range<K>, V>}. - * Modifications to this range map are guaranteed to read through to the returned {@code Map}. - * - * <p>It is guaranteed that no empty ranges will be in the returned {@code Map}. - */ - Map<Range<K>, V> asMapOfRanges(); - - /** - * Returns a view of the part of this range map that intersects with {@code range}. - * - * <p>For example, if {@code rangeMap} had the entries - * {@code [1, 5] => "foo", (6, 8) => "bar", (10, \u2025) => "baz"} - * then {@code rangeMap.subRangeMap(Range.open(3, 12))} would return a range map - * with the entries {@code (3, 5) => "foo", (6, 8) => "bar", (10, 12) => "baz"}. - * - * <p>The returned range map supports all optional operations that this range map supports, - * except for {@code asMapOfRanges().iterator().remove()}. - * - * <p>The returned range map will throw an {@link IllegalArgumentException} on an attempt to - * insert a range not {@linkplain Range#encloses(Range) enclosed} by {@code range}. - */ - RangeMap<K, V> subRangeMap(Range<K> range); - - /** - * Returns {@code true} if {@code obj} is another {@code RangeMap} that has an equivalent - * {@link #asMapOfRanges()}. - */ - @Override - boolean equals(@Nullable Object o); - - /** - * Returns {@code asMapOfRanges().hashCode()}. - */ - @Override - int hashCode(); - - /** - * Returns a readable string representation of this range map. - */ - @Override - String toString(); -} diff --git a/guava/src/com/google/common/collect/RangeSet.java b/guava/src/com/google/common/collect/RangeSet.java deleted file mode 100644 index f07c1fc..0000000 --- a/guava/src/com/google/common/collect/RangeSet.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 com.google.common.annotations.Beta; - -import java.util.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * A set comprising zero or more {@linkplain Range#isEmpty nonempty}, - * {@linkplain Range#isConnected(Range) disconnected} ranges of type {@code C}. - * - * <p>Implementations that choose to support the {@link #add(Range)} operation are required to - * ignore empty ranges and coalesce connected ranges. For example: <pre> {@code - * - * RangeSet<Integer> rangeSet = TreeRangeSet.create(); - * rangeSet.add(Range.closed(1, 10)); // {[1, 10]} - * rangeSet.add(Range.closedOpen(11, 15)); // {[1, 10], [11, 15)} - * rangeSet.add(Range.open(15, 20)); // disconnected range; {[1, 10], [11, 20)} - * rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)} - * rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}}</pre> - * - * <p>Note that the behavior of {@link Range#isEmpty()} and {@link Range#isConnected(Range)} may - * not be as expected on discrete ranges. See the Javadoc of those methods for details. - * - * <p>For a {@link Set} whose contents are specified by a {@link Range}, see {@link ContiguousSet}. - * - * @author Kevin Bourrillion - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public interface RangeSet<C extends Comparable> { - - // Query methods - - /** - * Determines whether any of this range set's member ranges contains {@code value}. - */ - boolean contains(C value); - - /** - * Returns the unique range from this range set that {@linkplain Range#contains contains} - * {@code value}, or {@code null} if this range set does not contain {@code value}. - */ - Range<C> rangeContaining(C value); - - /** - * Returns {@code true} if there exists a member range in this range set which - * {@linkplain Range#encloses encloses} the specified range. - */ - boolean encloses(Range<C> otherRange); - - /** - * Returns {@code true} if for each member range in {@code other} there exists a member range in - * this range set which {@linkplain Range#encloses encloses} it. It follows that - * {@code this.contains(value)} whenever {@code other.contains(value)}. Returns {@code true} if - * {@code other} is empty. - * - * <p>This is equivalent to checking if this range set {@link #encloses} each of the ranges in - * {@code other}. - */ - boolean enclosesAll(RangeSet<C> other); - - /** - * Returns {@code true} if this range set contains no ranges. - */ - boolean isEmpty(); - - /** - * Returns the minimal range which {@linkplain Range#encloses(Range) encloses} all ranges - * in this range set. - * - * @throws NoSuchElementException if this range set is {@linkplain #isEmpty() empty} - */ - Range<C> span(); - - // Views - - /** - * Returns a view of the {@linkplain Range#isConnected disconnected} ranges that make up this - * range set. The returned set may be empty. The iterators returned by its - * {@link Iterable#iterator} method return the ranges in increasing order of lower bound - * (equivalently, of upper bound). - */ - Set<Range<C>> asRanges(); - - /** - * Returns a view of the complement of this {@code RangeSet}. - * - * <p>The returned view supports the {@link #add} operation if this {@code RangeSet} supports - * {@link #remove}, and vice versa. - */ - RangeSet<C> complement(); - - /** - * Returns a view of the intersection of this {@code RangeSet} with the specified range. - * - * <p>The returned view supports all optional operations supported by this {@code RangeSet}, with - * the caveat that an {@link IllegalArgumentException} is thrown on an attempt to - * {@linkplain #add(Range) add} any range not {@linkplain Range#encloses(Range) enclosed} by - * {@code view}. - */ - RangeSet<C> subRangeSet(Range<C> view); - - // Modification - - /** - * Adds the specified range to this {@code RangeSet} (optional operation). That is, for equal - * range sets a and b, the result of {@code a.add(range)} is that {@code a} will be the minimal - * range set for which both {@code a.enclosesAll(b)} and {@code a.encloses(range)}. - * - * <p>Note that {@code range} will be {@linkplain Range#span(Range) coalesced} with any ranges in - * the range set that are {@linkplain Range#isConnected(Range) connected} with it. Moreover, - * if {@code range} is empty, this is a no-op. - * - * @throws UnsupportedOperationException if this range set does not support the {@code add} - * operation - */ - void add(Range<C> range); - - /** - * Removes the specified range from this {@code RangeSet} (optional operation). After this - * operation, if {@code range.contains(c)}, {@code this.contains(c)} will return {@code false}. - * - * <p>If {@code range} is empty, this is a no-op. - * - * @throws UnsupportedOperationException if this range set does not support the {@code remove} - * operation - */ - void remove(Range<C> range); - - /** - * Removes all ranges from this {@code RangeSet} (optional operation). After this operation, - * {@code this.contains(c)} will return false for all {@code c}. - * - * <p>This is equivalent to {@code remove(Range.all())}. - * - * @throws UnsupportedOperationException if this range set does not support the {@code clear} - * operation - */ - void clear(); - - /** - * Adds all of the ranges from the specified range set to this range set (optional operation). - * After this operation, this range set is the minimal range set that - * {@linkplain #enclosesAll(RangeSet) encloses} both the original range set and {@code other}. - * - * <p>This is equivalent to calling {@link #add} on each of the ranges in {@code other} in turn. - * - * @throws UnsupportedOperationException if this range set does not support the {@code addAll} - * operation - */ - void addAll(RangeSet<C> other); - - /** - * Removes all of the ranges from the specified range set from this range set (optional - * operation). After this operation, if {@code other.contains(c)}, {@code this.contains(c)} will - * return {@code false}. - * - * <p>This is equivalent to calling {@link #remove} on each of the ranges in {@code other} in - * turn. - * - * @throws UnsupportedOperationException if this range set does not support the {@code removeAll} - * operation - */ - void removeAll(RangeSet<C> other); - - // Object methods - - /** - * Returns {@code true} if {@code obj} is another {@code RangeSet} that contains the same ranges - * according to {@link Range#equals(Object)}. - */ - @Override - boolean equals(@Nullable Object obj); - - /** - * Returns {@code asRanges().hashCode()}. - */ - @Override - int hashCode(); - - /** - * Returns a readable string representation of this range set. For example, if this - * {@code RangeSet} consisted of {@code Ranges.closed(1, 3)} and {@code Ranges.greaterThan(4)}, - * this might return {@code " [1‥3](4‥+∞)}"}. - */ - @Override - String toString(); -} diff --git a/guava/src/com/google/common/collect/Ranges.java b/guava/src/com/google/common/collect/Ranges.java index 75cbc1e..131bf3b 100644 --- a/guava/src/com/google/common/collect/Ranges.java +++ b/guava/src/com/google/common/collect/Ranges.java @@ -16,9 +16,12 @@ package com.google.common.collect; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; import java.util.NoSuchElementException; /** @@ -59,21 +62,20 @@ import java.util.NoSuchElementException; * <dd>{@link #upTo} * </dl> * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained"> - * {@code Range}</a>. - * * @author Kevin Bourrillion * @author Gregory Kick * @since 10.0 - * @deprecated Use the corresponding method in {@link Range}. */ -@Deprecated @GwtCompatible @Beta public final class Ranges { private Ranges() {} + static <C extends Comparable<?>> Range<C> create( + Cut<C> lowerBound, Cut<C> upperBound) { + return new Range<C>(lowerBound, upperBound); + } + /** * Returns a range that contains all values strictly greater than {@code * lower} and strictly less than {@code upper}. @@ -82,7 +84,7 @@ public final class Ranges { * equal to</i> {@code upper} */ public static <C extends Comparable<?>> Range<C> open(C lower, C upper) { - return Range.open(lower, upper); + return create(Cut.aboveValue(lower), Cut.belowValue(upper)); } /** @@ -93,7 +95,7 @@ public final class Ranges { * upper} */ public static <C extends Comparable<?>> Range<C> closed(C lower, C upper) { - return Range.closed(lower, upper); + return create(Cut.belowValue(lower), Cut.aboveValue(upper)); } /** @@ -105,7 +107,7 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> closedOpen( C lower, C upper) { - return Range.closedOpen(lower, upper); + return create(Cut.belowValue(lower), Cut.belowValue(upper)); } /** @@ -117,7 +119,7 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> openClosed( C lower, C upper) { - return Range.openClosed(lower, upper); + return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); } /** @@ -130,7 +132,16 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> range( C lower, BoundType lowerType, C upper, BoundType upperType) { - return Range.range(lower, lowerType, upper, upperType); + checkNotNull(lowerType); + checkNotNull(upperType); + + Cut<C> lowerBound = (lowerType == BoundType.OPEN) + ? Cut.aboveValue(lower) + : Cut.belowValue(lower); + Cut<C> upperBound = (upperType == BoundType.OPEN) + ? Cut.belowValue(upper) + : Cut.aboveValue(upper); + return create(lowerBound, upperBound); } /** @@ -138,7 +149,7 @@ public final class Ranges { * endpoint}. */ public static <C extends Comparable<?>> Range<C> lessThan(C endpoint) { - return Range.lessThan(endpoint); + return create(Cut.<C>belowAll(), Cut.belowValue(endpoint)); } /** @@ -146,7 +157,7 @@ public final class Ranges { * {@code endpoint}. */ public static <C extends Comparable<?>> Range<C> atMost(C endpoint) { - return Range.atMost(endpoint); + return create(Cut.<C>belowAll(), Cut.aboveValue(endpoint)); } /** @@ -155,7 +166,14 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> upTo( C endpoint, BoundType boundType) { - return Range.upTo(endpoint, boundType); + switch (boundType) { + case OPEN: + return lessThan(endpoint); + case CLOSED: + return atMost(endpoint); + default: + throw new AssertionError(); + } } /** @@ -163,7 +181,7 @@ public final class Ranges { * endpoint}. */ public static <C extends Comparable<?>> Range<C> greaterThan(C endpoint) { - return Range.greaterThan(endpoint); + return create(Cut.aboveValue(endpoint), Cut.<C>aboveAll()); } /** @@ -171,7 +189,7 @@ public final class Ranges { * {@code endpoint}. */ public static <C extends Comparable<?>> Range<C> atLeast(C endpoint) { - return Range.atLeast(endpoint); + return create(Cut.belowValue(endpoint), Cut.<C>aboveAll()); } /** @@ -180,12 +198,19 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> downTo( C endpoint, BoundType boundType) { - return Range.downTo(endpoint, boundType); + switch (boundType) { + case OPEN: + return greaterThan(endpoint); + case CLOSED: + return atLeast(endpoint); + default: + throw new AssertionError(); + } } /** Returns a range that contains every value of type {@code C}. */ public static <C extends Comparable<?>> Range<C> all() { - return Range.all(); + return create(Cut.<C>belowAll(), Cut.<C>aboveAll()); } /** @@ -194,7 +219,7 @@ public final class Ranges { * on both ends. */ public static <C extends Comparable<?>> Range<C> singleton(C value) { - return Range.singleton(value); + return closed(value, value); } /** @@ -209,6 +234,18 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> encloseAll( Iterable<C> values) { - return Range.encloseAll(values); + checkNotNull(values); + if (values instanceof ContiguousSet) { + return ((ContiguousSet<C>) values).range(); + } + Iterator<C> valueIterator = values.iterator(); + C min = checkNotNull(valueIterator.next()); + C max = min; + while (valueIterator.hasNext()) { + C value = checkNotNull(valueIterator.next()); + min = Ordering.natural().min(min, value); + max = Ordering.natural().max(max, value); + } + return closed(min, max); } } diff --git a/guava/src/com/google/common/collect/RegularContiguousSet.java b/guava/src/com/google/common/collect/RegularContiguousSet.java index 7948fe8..10b26f9 100644 --- a/guava/src/com/google/common/collect/RegularContiguousSet.java +++ b/guava/src/com/google/common/collect/RegularContiguousSet.java @@ -41,38 +41,32 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> this.range = range; } - private ContiguousSet<C> intersectionInCurrentDomain(Range<C> other) { - return (range.isConnected(other)) - ? ContiguousSet.create(range.intersection(other), domain) - : new EmptyContiguousSet<C>(domain); + // Abstract method doesn't exist in GWT emulation + /* @Override */ ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) { + return range.intersection(Ranges.upTo(toElement, BoundType.forBoolean(inclusive))) + .asSet(domain); } - @Override ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) { - return intersectionInCurrentDomain(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + // Abstract method doesn't exist in GWT emulation + /* @Override */ int indexOf(Object target) { + return contains(target) ? (int) domain.distance(first(), (C) target) : -1; } - @Override ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement, + // Abstract method doesn't exist in GWT emulation + /* @Override */ ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { - if (fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive) { - // Range would reject our attempt to create (x, x). - return new EmptyContiguousSet<C>(domain); - } - return intersectionInCurrentDomain(Range.range( - fromElement, BoundType.forBoolean(fromInclusive), - toElement, BoundType.forBoolean(toInclusive))); + return range.intersection(Ranges.range(fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))).asSet(domain); } - @Override ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) { - return intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); - } - - @GwtIncompatible("not used by GWT emulation") - @Override int indexOf(Object target) { - return contains(target) ? (int) domain.distance(first(), (C) target) : -1; + // Abstract method doesn't exist in GWT emulation + /* @Override */ ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) { + return range.intersection(Ranges.downTo(fromElement, BoundType.forBoolean(inclusive))) + .asSet(domain); } @Override public UnmodifiableIterator<C> iterator() { - return new AbstractSequentialIterator<C>(first()) { + return new AbstractLinkedIterator<C>(first()) { final C last = last(); @Override @@ -82,18 +76,6 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> }; } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<C> descendingIterator() { - return new AbstractSequentialIterator<C>(last()) { - final C first = first(); - - @Override - protected C computeNext(C previous) { - return equalsOrThrow(previous, first) ? null : domain.previous(previous); - } - }; - } - private static boolean equalsOrThrow(Comparable<?> left, @Nullable Comparable<?> right) { return right != null && Range.compareOrThrow(left, right) == 0; } @@ -115,10 +97,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1; } - @Override public boolean contains(@Nullable Object object) { - if (object == null) { - return false; - } + @Override public boolean contains(Object object) { try { return range.contains((C) object); } catch (ClassCastException e) { @@ -127,7 +106,11 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> } @Override public boolean containsAll(Collection<?> targets) { - return Collections2.containsAllImpl(this, targets); + try { + return range.containsAll((Iterable<? extends C>) targets); + } catch (ClassCastException e) { + return false; + } } @Override public boolean isEmpty() { @@ -153,7 +136,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> C lowerEndpoint = Ordering.natural().max(this.first(), other.first()); C upperEndpoint = Ordering.natural().min(this.last(), other.last()); return (lowerEndpoint.compareTo(upperEndpoint) < 0) - ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain) + ? Ranges.closed(lowerEndpoint, upperEndpoint).asSet(domain) : new EmptyContiguousSet<C>(domain); } } @@ -163,14 +146,14 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> } @Override public Range<C> range(BoundType lowerBoundType, BoundType upperBoundType) { - return Range.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain), + return Ranges.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain), range.upperBound.withUpperBoundType(upperBoundType, domain)); } - @Override public boolean equals(@Nullable Object object) { + @Override public boolean equals(Object object) { if (object == this) { return true; - } else if (object instanceof RegularContiguousSet) { + } else if (object instanceof RegularContiguousSet<?>) { RegularContiguousSet<?> that = (RegularContiguousSet<?>) object; if (this.domain.equals(that.domain)) { return this.first().equals(that.first()) diff --git a/guava/src/com/google/common/collect/RegularImmutableAsList.java b/guava/src/com/google/common/collect/RegularImmutableAsList.java deleted file mode 100644 index c137d6b..0000000 --- a/guava/src/com/google/common/collect/RegularImmutableAsList.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -/** - * An {@link ImmutableAsList} implementation specialized for when the delegate collection is - * already backed by an {@code ImmutableList} or array. - * - * @author Louis Wasserman - */ -@GwtCompatible -@SuppressWarnings("serial") // uses writeReplace, not default serialization -class RegularImmutableAsList<E> extends ImmutableAsList<E> { - private final ImmutableCollection<E> delegate; - private final ImmutableList<? extends E> delegateList; - - RegularImmutableAsList(ImmutableCollection<E> delegate, ImmutableList<? extends E> delegateList) { - this.delegate = delegate; - this.delegateList = delegateList; - } - - RegularImmutableAsList(ImmutableCollection<E> delegate, Object[] array) { - this(delegate, ImmutableList.<E>asImmutableList(array)); - } - - @Override - ImmutableCollection<E> delegateCollection() { - return delegate; - } - - ImmutableList<? extends E> delegateList() { - return delegateList; - } - - @SuppressWarnings("unchecked") // safe covariant cast! - @Override - public UnmodifiableListIterator<E> listIterator(int index) { - return (UnmodifiableListIterator<E>) delegateList.listIterator(index); - } - - @Override - public Object[] toArray() { - return delegateList.toArray(); - } - - @Override - public <T> T[] toArray(T[] other) { - return delegateList.toArray(other); - } - - @Override - public int indexOf(Object object) { - return delegateList.indexOf(object); - } - - @Override - public int lastIndexOf(Object object) { - return delegateList.lastIndexOf(object); - } - - @Override - public boolean equals(Object obj) { - return delegateList.equals(obj); - } - - @Override - public int hashCode() { - return delegateList.hashCode(); - } - - @Override - public E get(int index) { - return delegateList.get(index); - } -} diff --git a/guava/src/com/google/common/collect/RegularImmutableBiMap.java b/guava/src/com/google/common/collect/RegularImmutableBiMap.java index b67aa76..dca1f05 100644 --- a/guava/src/com/google/common/collect/RegularImmutableBiMap.java +++ b/guava/src/com/google/common/collect/RegularImmutableBiMap.java @@ -16,283 +16,45 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; -import java.io.Serializable; -import java.util.Collection; - -import javax.annotation.Nullable; - /** - * Bimap with two or more mappings. - * - * @author Louis Wasserman + * Bimap with one or more mappings. + * + * @author Jared Levy */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization class RegularImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { - private static class BiMapEntry<K, V> extends ImmutableEntry<K, V> { - BiMapEntry(K key, V value) { - super(key, value); - } - - @Nullable - BiMapEntry<K, V> getNextInKToVBucket() { - return null; - } - - @Nullable - BiMapEntry<K, V> getNextInVToKBucket() { - return null; - } - } - - private static class NonTerminalBiMapEntry<K, V> extends BiMapEntry<K, V> { - @Nullable private final BiMapEntry<K, V> nextInKToVBucket; - @Nullable private final BiMapEntry<K, V> nextInVToKBucket; - - NonTerminalBiMapEntry(K key, V value, @Nullable BiMapEntry<K, V> nextInKToVBucket, - @Nullable BiMapEntry<K, V> nextInVToKBucket) { - super(key, value); - this.nextInKToVBucket = nextInKToVBucket; - this.nextInVToKBucket = nextInVToKBucket; - } + final transient ImmutableMap<K, V> delegate; + final transient ImmutableBiMap<V, K> inverse; - @Override - @Nullable - BiMapEntry<K, V> getNextInKToVBucket() { - return nextInKToVBucket; - } - - @Override - @Nullable - BiMapEntry<K, V> getNextInVToKBucket() { - return nextInVToKBucket; - } - } - - static final double MAX_LOAD_FACTOR = 1.2; - - private transient final BiMapEntry<K, V>[] kToVTable; - private transient final BiMapEntry<K, V>[] vToKTable; - private transient final BiMapEntry<K, V>[] entries; - private transient final int mask; - private transient final int hashCode; - - RegularImmutableBiMap(Collection<? extends Entry<? extends K, ? extends V>> entriesToAdd) { - int n = entriesToAdd.size(); - int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); - this.mask = tableSize - 1; - BiMapEntry<K, V>[] kToVTable = createEntryArray(tableSize); - BiMapEntry<K, V>[] vToKTable = createEntryArray(tableSize); - BiMapEntry<K, V>[] entries = createEntryArray(n); - int i = 0; - int hashCode = 0; - - for (Entry<? extends K, ? extends V> entry : entriesToAdd) { - K key = checkNotNull(entry.getKey()); - V value = checkNotNull(entry.getValue()); - - int keyHash = key.hashCode(); - int valueHash = value.hashCode(); - int keyBucket = Hashing.smear(keyHash) & mask; - int valueBucket = Hashing.smear(valueHash) & mask; - - BiMapEntry<K, V> nextInKToVBucket = kToVTable[keyBucket]; - for (BiMapEntry<K, V> kToVEntry = nextInKToVBucket; kToVEntry != null; - kToVEntry = kToVEntry.getNextInKToVBucket()) { - if (key.equals(kToVEntry.getKey())) { - throw new IllegalArgumentException("Multiple entries with same key: " + - entry + " and " + kToVEntry); - } - } - BiMapEntry<K, V> nextInVToKBucket = vToKTable[valueBucket]; - for (BiMapEntry<K, V> vToKEntry = nextInVToKBucket; vToKEntry != null; - vToKEntry = vToKEntry.getNextInVToKBucket()) { - if (value.equals(vToKEntry.getValue())) { - throw new IllegalArgumentException("Multiple entries with same value: " - + entry + " and " + vToKEntry); - } - } - BiMapEntry<K, V> newEntry = - (nextInKToVBucket == null && nextInVToKBucket == null) - ? new BiMapEntry<K, V>(key, value) - : new NonTerminalBiMapEntry<K, V>(key, value, nextInKToVBucket, nextInVToKBucket); - kToVTable[keyBucket] = newEntry; - vToKTable[valueBucket] = newEntry; - entries[i++] = newEntry; - hashCode += keyHash ^ valueHash; - } - - this.kToVTable = kToVTable; - this.vToKTable = vToKTable; - this.entries = entries; - this.hashCode = hashCode; - } - - @SuppressWarnings("unchecked") - private static <K, V> BiMapEntry<K, V>[] createEntryArray(int length) { - return new BiMapEntry[length]; - } + RegularImmutableBiMap(ImmutableMap<K, V> delegate) { + this.delegate = delegate; - @Override - @Nullable - public V get(@Nullable Object key) { - if (key == null) { - return null; - } - int bucket = Hashing.smear(key.hashCode()) & mask; - for (BiMapEntry<K, V> entry = kToVTable[bucket]; entry != null; - entry = entry.getNextInKToVBucket()) { - if (key.equals(entry.getKey())) { - return entry.getValue(); - } + ImmutableMap.Builder<V, K> builder = ImmutableMap.builder(); + for (Entry<K, V> entry : delegate.entrySet()) { + builder.put(entry.getValue(), entry.getKey()); } - return null; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new ImmutableMapEntrySet<K, V>() { - @Override - ImmutableMap<K, V> map() { - return RegularImmutableBiMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<K, V>> createAsList() { - return new RegularImmutableAsList<Entry<K, V>>(this, entries); - } - - @Override - boolean isHashCodeFast() { - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - }; + ImmutableMap<V, K> backwardMap = builder.build(); + this.inverse = new RegularImmutableBiMap<V, K>(backwardMap, this); } - @Override - boolean isPartialView() { - return false; + RegularImmutableBiMap(ImmutableMap<K, V> delegate, + ImmutableBiMap<V, K> inverse) { + this.delegate = delegate; + this.inverse = inverse; } - @Override - public int size() { - return entries.length; + @Override ImmutableMap<K, V> delegate() { + return delegate; } - - private transient ImmutableBiMap<V, K> inverse; - @Override - public ImmutableBiMap<V, K> inverse() { - ImmutableBiMap<V, K> result = inverse; - return (result == null) ? inverse = new Inverse() : result; + @Override public ImmutableBiMap<V, K> inverse() { + return inverse; } - - private final class Inverse extends ImmutableBiMap<V, K> { - - @Override - public int size() { - return inverse().size(); - } - - @Override - public ImmutableBiMap<K, V> inverse() { - return RegularImmutableBiMap.this; - } - - @Override - public K get(@Nullable Object value) { - if (value == null) { - return null; - } - int bucket = Hashing.smear(value.hashCode()) & mask; - for (BiMapEntry<K, V> entry = vToKTable[bucket]; entry != null; - entry = entry.getNextInVToKBucket()) { - if (value.equals(entry.getValue())) { - return entry.getKey(); - } - } - return null; - } - - @Override - ImmutableSet<Entry<V, K>> createEntrySet() { - return new InverseEntrySet(); - } - - final class InverseEntrySet extends ImmutableMapEntrySet<V, K> { - @Override - ImmutableMap<V, K> map() { - return Inverse.this; - } - @Override - boolean isHashCodeFast() { - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public UnmodifiableIterator<Entry<V, K>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<V, K>> createAsList() { - return new ImmutableAsList<Entry<V, K>>() { - @Override - public Entry<V, K> get(int index) { - Entry<K, V> entry = entries[index]; - return Maps.immutableEntry(entry.getValue(), entry.getKey()); - } - - @Override - ImmutableCollection<Entry<V, K>> delegateCollection() { - return InverseEntrySet.this; - } - }; - } - } - - @Override - boolean isPartialView() { - return false; - } - - @Override - Object writeReplace() { - return new InverseSerializedForm<K, V>(RegularImmutableBiMap.this); - } - } - - private static class InverseSerializedForm<K, V> implements Serializable { - private final ImmutableBiMap<K, V> forward; - - InverseSerializedForm(ImmutableBiMap<K, V> forward) { - this.forward = forward; - } - - Object readResolve() { - return forward.inverse(); - } - - private static final long serialVersionUID = 1; + @Override boolean isPartialView() { + return delegate.isPartialView() || inverse.delegate().isPartialView(); } } diff --git a/guava/src/com/google/common/collect/RegularImmutableList.java b/guava/src/com/google/common/collect/RegularImmutableList.java index 4314b6e..27f47f5 100644 --- a/guava/src/com/google/common/collect/RegularImmutableList.java +++ b/guava/src/com/google/common/collect/RegularImmutableList.java @@ -58,6 +58,16 @@ class RegularImmutableList<E> extends ImmutableList<E> { return offset != 0 || size != array.length; } + @Override public boolean contains(@Nullable Object target) { + return indexOf(target) != -1; + } + + // The fake cast to E is safe because the creation methods only allow E's + @SuppressWarnings("unchecked") + @Override public UnmodifiableIterator<E> iterator() { + return (UnmodifiableIterator<E>) Iterators.forArray(array, offset, size); + } + @Override public Object[] toArray() { Object[] newArray = new Object[size()]; System.arraycopy(array, offset, newArray, 0, size); @@ -82,19 +92,45 @@ class RegularImmutableList<E> extends ImmutableList<E> { return (E) array[index + offset]; } - @Override - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new RegularImmutableList<E>( - array, offset + fromIndex, toIndex - fromIndex); + @Override public int indexOf(@Nullable Object target) { + if (target != null) { + for (int i = offset; i < offset + size; i++) { + if (array[i].equals(target)) { + return i - offset; + } + } + } + return -1; } - @SuppressWarnings("unchecked") - @Override - public UnmodifiableListIterator<E> listIterator(int index) { - // for performance - // The fake cast to E is safe because the creation methods only allow E's - return (UnmodifiableListIterator<E>) - Iterators.forArray(array, offset, size, index); + @Override public int lastIndexOf(@Nullable Object target) { + if (target != null) { + for (int i = offset + size - 1; i >= offset; i--) { + if (array[i].equals(target)) { + return i - offset; + } + } + } + return -1; + } + + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (fromIndex == toIndex) + ? ImmutableList.<E>of() + : new RegularImmutableList<E>( + array, offset + fromIndex, toIndex - fromIndex); + } + + @Override public UnmodifiableListIterator<E> listIterator(final int start) { + return new AbstractIndexedListIterator<E>(size, start) { + // The fake cast to E is safe because the creation methods only allow E's + @SuppressWarnings("unchecked") + @Override protected E get(int index) { + return (E) array[index + offset]; + } + + }; } @Override public boolean equals(@Nullable Object object) { @@ -128,6 +164,16 @@ class RegularImmutableList<E> extends ImmutableList<E> { return true; } + @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 hashCode = 1; + for (int i = offset; i < offset + size; i++) { + hashCode = 31 * hashCode + array[i].hashCode(); + } + return hashCode; + } + @Override public String toString() { StringBuilder sb = Collections2.newStringBuilderForCollection(size()) .append('[').append(array[offset]); diff --git a/guava/src/com/google/common/collect/RegularImmutableMap.java b/guava/src/com/google/common/collect/RegularImmutableMap.java index 6d9be99..e2b7150 100644 --- a/guava/src/com/google/common/collect/RegularImmutableMap.java +++ b/guava/src/com/google/common/collect/RegularImmutableMap.java @@ -19,6 +19,8 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.ImmutableSet.ArrayImmutableSet; +import com.google.common.collect.ImmutableSet.TransformedImmutableSet; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -39,6 +41,7 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { private final transient LinkedEntry<K, V>[] table; // 'and' with an int to get a table index private final transient int mask; + private final transient int keySetHashCode; // TODO(gak): investigate avoiding the creation of ImmutableEntries since we // re-copy them anyway. @@ -46,16 +49,18 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { int size = immutableEntries.length; entries = createEntryArray(size); - int tableSize = Hashing.closedTableSize(size, MAX_LOAD_FACTOR); + int tableSize = chooseTableSize(size); table = createEntryArray(tableSize); mask = tableSize - 1; + int keySetHashCodeMutable = 0; for (int entryIndex = 0; entryIndex < size; entryIndex++) { // each of our 6 callers carefully put only Entry<K, V>s into the array! @SuppressWarnings("unchecked") Entry<K, V> entry = (Entry<K, V>) immutableEntries[entryIndex]; K key = entry.getKey(); int keyHashCode = key.hashCode(); + keySetHashCodeMutable += keyHashCode; int tableIndex = Hashing.smear(keyHashCode) & mask; @Nullable LinkedEntry<K, V> existing = table[tableIndex]; // prepend, not append, so the entries can be immutable @@ -68,14 +73,15 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { existing = existing.next(); } } + keySetHashCode = keySetHashCodeMutable; } - /** - * Closed addressing tends to perform well even with high load factors. - * Being conservative here ensures that the table is still likely to be - * relatively sparse (hence it misses fast) while saving space. - */ - private static final double MAX_LOAD_FACTOR = 1.2; + private static int chooseTableSize(int size) { + // least power of 2 greater than size + int tableSize = Integer.highestOneBit(size) << 1; + checkArgument(tableSize > 0, "table too large: %s", size); + return tableSize; + } /** * Creates a {@link LinkedEntry} array to hold parameterized entries. The @@ -160,31 +166,125 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { public int size() { return entries.length; } - + + @Override public boolean isEmpty() { + return false; + } + + @Override public boolean containsValue(@Nullable Object value) { + if (value == null) { + return false; + } + for (Entry<K, V> entry : entries) { + if (entry.getValue().equals(value)) { + return true; + } + } + return false; + } + @Override boolean isPartialView() { return false; } - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new EntrySet(); + private transient ImmutableSet<Entry<K, V>> entrySet; + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = new EntrySet<K, V>(this)) : es; } @SuppressWarnings("serial") // uses writeReplace(), not default serialization - private class EntrySet extends ImmutableMapEntrySet<K, V> { - @Override ImmutableMap<K, V> map() { - return RegularImmutableMap.this; + private static class EntrySet<K, V> extends ArrayImmutableSet<Entry<K, V>> { + final transient RegularImmutableMap<K, V> map; + + EntrySet(RegularImmutableMap<K, V> map) { + super(map.entries); + this.map = map; } - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); + @Override public boolean contains(Object target) { + if (target instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) target; + V mappedValue = map.get(entry.getKey()); + return mappedValue != null && mappedValue.equals(entry.getValue()); + } + return false; + } + } + + private transient ImmutableSet<K> keySet; + + @Override public ImmutableSet<K> keySet() { + ImmutableSet<K> ks = keySet; + return (ks == null) ? (keySet = new KeySet<K, V>(this)) : ks; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class KeySet<K, V> + extends TransformedImmutableSet<Entry<K, V>, K> { + final RegularImmutableMap<K, V> map; + + KeySet(RegularImmutableMap<K, V> map) { + super(map.entries, map.keySetHashCode); + this.map = map; + } + + @Override K transform(Entry<K, V> element) { + return element.getKey(); + } + + @Override public boolean contains(Object target) { + return map.containsKey(target); + } + + @Override boolean isPartialView() { + return true; + } + } + + private transient ImmutableCollection<V> values; + + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(this)) : v; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + final RegularImmutableMap<?, V> map; + + Values(RegularImmutableMap<?, V> map) { + this.map = map; } @Override - ImmutableList<Entry<K, V>> createAsList() { - return new RegularImmutableAsList<Entry<K, V>>(this, entries); + public int size() { + return map.entries.length; + } + + @Override public UnmodifiableIterator<V> iterator() { + return new AbstractIndexedListIterator<V>(map.entries.length) { + @Override protected V get(int index) { + return map.entries[index].getValue(); + } + }; } + + @Override public boolean contains(Object target) { + return map.containsValue(target); + } + + @Override boolean isPartialView() { + return true; + } + } + + @Override public String toString() { + StringBuilder result + = Collections2.newStringBuilderForCollection(size()).append('{'); + Collections2.STANDARD_JOINER.appendTo(result, entries); + return result.append('}').toString(); } // This class is never actually serialized directly, but we have to make the diff --git a/guava/src/com/google/common/collect/RegularImmutableMultiset.java b/guava/src/com/google/common/collect/RegularImmutableMultiset.java index e46f424..090c091 100644 --- a/guava/src/com/google/common/collect/RegularImmutableMultiset.java +++ b/guava/src/com/google/common/collect/RegularImmutableMultiset.java @@ -18,13 +18,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; import java.util.Map; import javax.annotation.Nullable; /** * Implementation of {@link ImmutableMultiset} with one or more elements. - * + * * @author Jared Levy * @author Louis Wasserman */ @@ -66,45 +67,31 @@ class RegularImmutableMultiset<E> extends ImmutableMultiset<E> { return map.keySet(); } - private static <E> Entry<E> entryFromMapEntry(Map.Entry<E, Integer> entry) { - return Multisets.immutableEntry(entry.getKey(), entry.getValue()); - } - @Override - ImmutableSet<Entry<E>> createEntrySet() { - return new EntrySet(); - } - - private class EntrySet extends ImmutableMultiset<E>.EntrySet { - @Override - public int size() { - return map.size(); - } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<E>> createAsList() { - final ImmutableList<Map.Entry<E, Integer>> entryList = map.entrySet().asList(); - return new ImmutableAsList<Entry<E>>() { - @Override - public Entry<E> get(int index) { - return entryFromMapEntry(entryList.get(index)); - } - - @Override - ImmutableCollection<Entry<E>> delegateCollection() { - return EntrySet.this; - } - }; - } + UnmodifiableIterator<Entry<E>> entryIterator() { + final Iterator<Map.Entry<E, Integer>> mapIterator = + map.entrySet().iterator(); + return new UnmodifiableIterator<Entry<E>>() { + @Override + public boolean hasNext() { + return mapIterator.hasNext(); + } + + @Override + public Entry<E> next() { + Map.Entry<E, Integer> mapEntry = mapIterator.next(); + return Multisets.immutableEntry(mapEntry.getKey(), mapEntry.getValue()); + } + }; } @Override public int hashCode() { return map.hashCode(); } + + @Override + int distinctElements() { + return map.size(); + } } diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedMap.java b/guava/src/com/google/common/collect/RegularImmutableSortedMap.java deleted file mode 100644 index dd93b7f..0000000 --- a/guava/src/com/google/common/collect/RegularImmutableSortedMap.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import javax.annotation.Nullable; - -/** - * An implementation of an immutable sorted map with one or more entries. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace, not default serialization -final class RegularImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> { - private final transient RegularImmutableSortedSet<K> keySet; - private final transient ImmutableList<V> valueList; - - RegularImmutableSortedMap(RegularImmutableSortedSet<K> keySet, ImmutableList<V> valueList) { - this.keySet = keySet; - this.valueList = valueList; - } - - RegularImmutableSortedMap( - RegularImmutableSortedSet<K> keySet, - ImmutableList<V> valueList, - ImmutableSortedMap<K, V> descendingMap) { - super(descendingMap); - this.keySet = keySet; - this.valueList = valueList; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new EntrySet(); - } - - private class EntrySet extends ImmutableMapEntrySet<K, V> { - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<K, V>> createAsList() { - return new ImmutableAsList<Entry<K, V>>() { - // avoid additional indirection - private final ImmutableList<K> keyList = keySet().asList(); - - @Override - public Entry<K, V> get(int index) { - return Maps.immutableEntry(keyList.get(index), valueList.get(index)); - } - - @Override - ImmutableCollection<Entry<K, V>> delegateCollection() { - return EntrySet.this; - } - }; - } - - @Override - ImmutableMap<K, V> map() { - return RegularImmutableSortedMap.this; - } - } - - @Override - public ImmutableSortedSet<K> keySet() { - return keySet; - } - - @Override - public ImmutableCollection<V> values() { - return valueList; - } - - @Override - public V get(@Nullable Object key) { - int index = keySet.indexOf(key); - return (index == -1) ? null : valueList.get(index); - } - - private ImmutableSortedMap<K, V> getSubMap(int fromIndex, int toIndex) { - if (fromIndex == 0 && toIndex == size()) { - return this; - } else if (fromIndex == toIndex) { - return emptyMap(comparator()); - } else { - return from( - keySet.getSubSet(fromIndex, toIndex), - valueList.subList(fromIndex, toIndex)); - } - } - - @Override - public ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) { - return getSubMap(0, keySet.headIndex(checkNotNull(toKey), inclusive)); - } - - @Override - public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { - return getSubMap(keySet.tailIndex(checkNotNull(fromKey), inclusive), size()); - } - - @Override - ImmutableSortedMap<K, V> createDescendingMap() { - return new RegularImmutableSortedMap<K, V>( - (RegularImmutableSortedSet<K>) keySet.descendingSet(), - valueList.reverse(), - this); - } - -} diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java b/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java index 3db3405..7318d9b 100644 --- a/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java @@ -15,11 +15,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkPositionIndexes; -import static com.google.common.collect.BoundType.CLOSED; +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; /** @@ -27,119 +32,167 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@SuppressWarnings("serial") // uses writeReplace, not default serialization final class RegularImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { - private final transient RegularImmutableSortedSet<E> elementSet; - private final transient int[] counts; - private final transient long[] cumulativeCounts; - private final transient int offset; - private final transient int length; + private static final class CumulativeCountEntry<E> extends Multisets.AbstractEntry<E> { + final E element; + final int count; + final long cumulativeCount; + + CumulativeCountEntry(E element, int count, @Nullable CumulativeCountEntry<E> 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 <E> RegularImmutableSortedMultiset<E> createFromSorted(Comparator<? super E> comparator, + List<? extends Multiset.Entry<E>> entries) { + List<CumulativeCountEntry<E>> newEntries = Lists.newArrayListWithCapacity(entries.size()); + CumulativeCountEntry<E> previous = null; + for (Multiset.Entry<E> entry : entries) { + newEntries.add( + previous = new CumulativeCountEntry<E>(entry.getElement(), entry.getCount(), previous)); + } + return new RegularImmutableSortedMultiset<E>(comparator, ImmutableList.copyOf(newEntries)); + } + + final transient ImmutableList<CumulativeCountEntry<E>> entries; RegularImmutableSortedMultiset( - RegularImmutableSortedSet<E> elementSet, - int[] counts, - long[] cumulativeCounts, - int offset, - int length) { - this.elementSet = elementSet; - this.counts = counts; - this.cumulativeCounts = cumulativeCounts; - this.offset = offset; - this.length = length; + Comparator<? super E> comparator, ImmutableList<CumulativeCountEntry<E>> entries) { + super(comparator); + this.entries = entries; + assert !entries.isEmpty(); } - private Entry<E> getEntry(int index) { - return Multisets.immutableEntry( - elementSet.asList().get(index), - counts[offset + index]); + ImmutableList<E> elementList() { + return new TransformedImmutableList<CumulativeCountEntry<E>, E>(entries) { + @Override + E transform(CumulativeCountEntry<E> entry) { + return entry.getElement(); + } + }; } @Override - public Entry<E> firstEntry() { - return getEntry(0); + ImmutableSortedSet<E> createElementSet() { + return new RegularImmutableSortedSet<E>(elementList(), comparator()); } @Override - public Entry<E> lastEntry() { - return getEntry(length - 1); + ImmutableSortedSet<E> createDescendingElementSet() { + return new RegularImmutableSortedSet<E>(elementList().reverse(), reverseComparator()); } + @SuppressWarnings("unchecked") @Override - public int count(@Nullable Object element) { - int index = elementSet.indexOf(element); - return (index == -1) ? 0 : counts[index + offset]; + UnmodifiableIterator<Multiset.Entry<E>> entryIterator() { + return (UnmodifiableIterator) entries.iterator(); } + @SuppressWarnings("unchecked") @Override - public int size() { - long size = cumulativeCounts[offset + length] - cumulativeCounts[offset]; - return Ints.saturatedCast(size); + UnmodifiableIterator<Multiset.Entry<E>> descendingEntryIterator() { + return (UnmodifiableIterator) entries.reverse().iterator(); } @Override - public ImmutableSortedSet<E> elementSet() { - return elementSet; + public CumulativeCountEntry<E> firstEntry() { + return entries.get(0); } @Override - public ImmutableSortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { - return getSubMultiset(0, elementSet.headIndex(upperBound, checkNotNull(boundType) == CLOSED)); + public CumulativeCountEntry<E> lastEntry() { + return entries.get(entries.size() - 1); } @Override - public ImmutableSortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { - return getSubMultiset(elementSet.tailIndex(lowerBound, checkNotNull(boundType) == CLOSED), - length); + public int size() { + CumulativeCountEntry<E> firstEntry = firstEntry(); + CumulativeCountEntry<E> lastEntry = lastEntry(); + return Ints.saturatedCast( + lastEntry.cumulativeCount - firstEntry.cumulativeCount + firstEntry.count); } - ImmutableSortedMultiset<E> getSubMultiset(int from, int to) { - checkPositionIndexes(from, to, length); - if (from == to) { - return emptyMultiset(comparator()); - } else if (from == 0 && to == length) { - return this; - } else { - RegularImmutableSortedSet<E> subElementSet = - (RegularImmutableSortedSet<E>) elementSet.getSubSet(from, to); - return new RegularImmutableSortedMultiset<E>( - subElementSet, counts, cumulativeCounts, offset + from, to - from); - } + @Override + int distinctElements() { + return entries.size(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - return new EntrySet(); + boolean isPartialView() { + return entries.isPartialView(); } - private final class EntrySet extends ImmutableMultiset<E>.EntrySet { - @Override - public int size() { - return length; + @SuppressWarnings("unchecked") + @Override + public int count(@Nullable Object element) { + if (element == null) { + return 0; } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); + 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 - ImmutableList<Entry<E>> createAsList() { - return new ImmutableAsList<Entry<E>>() { - @Override - public Entry<E> get(int index) { - return getEntry(index); - } - - @Override - ImmutableCollection<Entry<E>> delegateCollection() { - return EntrySet.this; - } - }; + @Override + public ImmutableSortedMultiset<E> 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 - boolean isPartialView() { - return offset > 0 || length < counts.length; + public ImmutableSortedMultiset<E> 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<E> createSubMultiset(int newFromIndex, int newToIndex) { + if (newFromIndex == 0 && newToIndex == entries.size()) { + return this; + } else if (newFromIndex >= newToIndex) { + return emptyMultiset(comparator()); + } else { + return new RegularImmutableSortedMultiset<E>( + comparator(), entries.subList(newFromIndex, newToIndex)); + } } } diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedSet.java b/guava/src/com/google/common/collect/RegularImmutableSortedSet.java index 9dc2e5c..3d2b7e0 100644 --- a/guava/src/com/google/common/collect/RegularImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/RegularImmutableSortedSet.java @@ -25,7 +25,6 @@ import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_AFT import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_PRESENT; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.util.Collection; import java.util.Collections; @@ -60,11 +59,6 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return elements.iterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<E> descendingIterator() { - return elements.reverse().iterator(); - } - @Override public boolean isEmpty() { return false; } @@ -75,8 +69,11 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } @Override public boolean contains(Object o) { + if (o == null) { + return false; + } try { - return o != null && unsafeBinarySearch(o) >= 0; + return binarySearch(o) >= 0; } catch (ClassCastException e) { return false; } @@ -87,7 +84,7 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { // targets.size() < size() / log(size()) // TODO(kevinb): see if we can share code with OrderedIterator after it // graduates from labs. - if (!SortedIterables.hasSameComparator(comparator(), targets) + if (!SortedIterables.hasSameComparator(comparator(), targets) || (targets.size() <= 1)) { return super.containsAll(targets); } @@ -128,8 +125,18 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return false; } - private int unsafeBinarySearch(Object key) throws ClassCastException { - return Collections.binarySearch(elements, key, unsafeComparator()); + private int binarySearch(Object key) { + // TODO(kevinb): split this into binarySearch(E) and + // unsafeBinarySearch(Object), use each appropriately. name all methods that + // might throw CCE "unsafe*". + + // Pretend the comparator can compare anything. If it turns out it can't + // compare a and b, we should get a CCE on the subsequent line. Only methods + // that are spec'd to throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator = (Comparator<Object>) comparator; + + return Collections.binarySearch(elements, key, unsafeComparator); } @Override boolean isPartialView() { @@ -190,38 +197,16 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } @Override - public E lower(E element) { - int index = headIndex(element, false) - 1; - return (index == -1) ? null : elements.get(index); - } - - @Override - public E floor(E element) { - int index = headIndex(element, true) - 1; - return (index == -1) ? null : elements.get(index); - } - - @Override - public E ceiling(E element) { - int index = tailIndex(element, true); - return (index == size()) ? null : elements.get(index); - } - - @Override - public E higher(E element) { - int index = tailIndex(element, false); - return (index == size()) ? null : elements.get(index); - } - - @Override ImmutableSortedSet<E> headSetImpl(E toElement, boolean inclusive) { - return getSubSet(0, headIndex(toElement, inclusive)); - } - - int headIndex(E toElement, boolean inclusive) { - return SortedLists.binarySearch( - elements, checkNotNull(toElement), comparator(), - inclusive ? FIRST_AFTER : FIRST_PRESENT, NEXT_HIGHER); + int index; + if (inclusive) { + index = SortedLists.binarySearch( + elements, checkNotNull(toElement), comparator(), FIRST_AFTER, NEXT_HIGHER); + } else { + index = SortedLists.binarySearch( + elements, checkNotNull(toElement), comparator(), FIRST_PRESENT, NEXT_HIGHER); + } + return createSubset(0, index); } @Override @@ -233,15 +218,15 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { @Override ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive) { - return getSubSet(tailIndex(fromElement, inclusive), size()); - } - - int tailIndex(E fromElement, boolean inclusive) { - return SortedLists.binarySearch( - elements, - checkNotNull(fromElement), - comparator(), - inclusive ? FIRST_PRESENT : FIRST_AFTER, NEXT_HIGHER); + int index; + if (inclusive) { + index = SortedLists.binarySearch( + elements, checkNotNull(fromElement), comparator(), FIRST_PRESENT, NEXT_HIGHER); + } else { + index = SortedLists.binarySearch( + elements, checkNotNull(fromElement), comparator(), FIRST_AFTER, NEXT_HIGHER); + } + return createSubset(index, size()); } // Pretend the comparator can compare anything. If it turns out it can't @@ -252,7 +237,7 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return (Comparator<Object>) comparator; } - ImmutableSortedSet<E> getSubSet(int newFromIndex, int newToIndex) { + private ImmutableSortedSet<E> createSubset(int newFromIndex, int newToIndex) { if (newFromIndex == 0 && newToIndex == size()) { return this; } else if (newFromIndex < newToIndex) { @@ -263,27 +248,28 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } } + @SuppressWarnings("unchecked") @Override int indexOf(@Nullable Object target) { if (target == null) { return -1; } int position; try { - position = SortedLists.binarySearch(elements, target, unsafeComparator(), + position = SortedLists.binarySearch(elements, (E) target, comparator(), ANY_PRESENT, INVERTED_INSERTION_INDEX); } catch (ClassCastException e) { return -1; } - return (position >= 0) ? position : -1; + // TODO(kevinb): reconsider if it's really worth making feeble attempts at + // sanity for inconsistent comparators. + + // The equals() check is needed when the comparator isn't compatible with + // equals(). + return (position >= 0 && elements.get(position).equals(target)) + ? position : -1; } @Override ImmutableList<E> createAsList() { return new ImmutableSortedAsList<E>(this, elements); } - - @Override - ImmutableSortedSet<E> createDescendingSet() { - return new RegularImmutableSortedSet<E>(elements.reverse(), - Ordering.from(comparator).reverse()); - } } diff --git a/guava/src/com/google/common/collect/RegularImmutableTable.java b/guava/src/com/google/common/collect/RegularImmutableTable.java index e4bfb70..c6ae3eb 100644 --- a/guava/src/com/google/common/collect/RegularImmutableTable.java +++ b/guava/src/com/google/common/collect/RegularImmutableTable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Guava Authors + * 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. @@ -21,11 +21,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; import com.google.common.base.Objects; import java.util.Collections; import java.util.Comparator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -36,63 +36,56 @@ import javax.annotation.concurrent.Immutable; * An implementation of {@link ImmutableTable} holding an arbitrary number of * cells. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { - private RegularImmutableTable() {} - - private transient ImmutableCollection<V> values; + private final ImmutableSet<Cell<R, C, V>> cellSet; - @Override public final ImmutableCollection<V> values() { - ImmutableCollection<V> result = values; - return (result == null) ? values = createValues() : result; + private RegularImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet) { + this.cellSet = cellSet; } - - abstract ImmutableCollection<V> createValues(); - @Override public abstract int size(); + private static final Function<Cell<Object, Object, Object>, Object> + GET_VALUE_FUNCTION = + new Function<Cell<Object, Object, Object>, Object>() { + @Override public Object apply(Cell<Object, Object, Object> from) { + return from.getValue(); + } + }; + + @SuppressWarnings("unchecked") + private Function<Cell<R, C, V>, V> getValueFunction() { + return (Function) GET_VALUE_FUNCTION; + } - @Override public final boolean containsValue(@Nullable Object value) { - return values().contains(value); + @Nullable private transient volatile ImmutableList<V> valueList; + + @Override public final ImmutableCollection<V> values() { + ImmutableList<V> result = valueList; + if (result == null) { + valueList = result = ImmutableList.copyOf( + Iterables.transform(cellSet(), getValueFunction())); + } + return result; } - - private transient ImmutableSet<Cell<R, C, V>> cellSet; - @Override - public final ImmutableSet<Cell<R, C, V>> cellSet() { - ImmutableSet<Cell<R, C, V>> result = cellSet; - return (result == null) ? cellSet = createCellSet() : result; + @Override public final int size() { + return cellSet().size(); } - - abstract ImmutableSet<Cell<R, C, V>> createCellSet(); - - abstract class CellSet extends ImmutableSet<Cell<R, C, V>> { - @Override - public int size() { - return RegularImmutableTable.this.size(); - } - - @Override - public boolean contains(@Nullable Object object) { - if (object instanceof Cell) { - Cell<?, ?, ?> cell = (Cell<?, ?, ?>) object; - Object value = get(cell.getRowKey(), cell.getColumnKey()); - return value != null && value.equals(cell.getValue()); - } - return false; - } - @Override - boolean isPartialView() { - return false; - } + @Override public final boolean containsValue(@Nullable Object value) { + return values().contains(value); } @Override public final boolean isEmpty() { return false; } + @Override public final ImmutableSet<Cell<R, C, V>> cellSet() { + return cellSet; + } + static final <R, C, V> RegularImmutableTable<R, C, V> forCells( List<Cell<R, C, V>> cells, @Nullable final Comparator<? super R> rowComparator, @@ -137,13 +130,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { forCellsInternal(Iterable<Cell<R, C, V>> cells, @Nullable Comparator<? super R> rowComparator, @Nullable Comparator<? super C> columnComparator) { + ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder = ImmutableSet.builder(); ImmutableSet.Builder<R> rowSpaceBuilder = ImmutableSet.builder(); ImmutableSet.Builder<C> columnSpaceBuilder = ImmutableSet.builder(); - ImmutableList<Cell<R, C, V>> cellList = ImmutableList.copyOf(cells); - for (Cell<R, C, V> cell : cellList) { + for (Cell<R, C, V> cell : cells) { + cellSetBuilder.add(cell); rowSpaceBuilder.add(cell.getRowKey()); columnSpaceBuilder.add(cell.getColumnKey()); } + ImmutableSet<Cell<R, C, V>> cellSet = cellSetBuilder.build(); ImmutableSet<R> rowSpace = rowSpaceBuilder.build(); if (rowComparator != null) { @@ -160,9 +155,9 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { // use a dense table if more than half of the cells have values // TODO(gak): tune this condition based on empirical evidence - return (cellList.size() > ((rowSpace.size() * columnSpace.size()) / 2)) ? - new DenseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace) : - new SparseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace); + return (cellSet.size() > ((rowSpace.size() * columnSpace.size()) / 2 )) ? + new DenseImmutableTable<R, C, V>(cellSet, rowSpace, columnSpace) : + new SparseImmutableTable<R, C, V>(cellSet, rowSpace, columnSpace); } /** @@ -175,52 +170,50 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { private final ImmutableMap<R, Map<C, V>> rowMap; private final ImmutableMap<C, Map<R, V>> columnMap; - private final int[] iterationOrderRow; - private final int[] iterationOrderColumn; - SparseImmutableTable(ImmutableList<Cell<R, C, V>> cellList, - ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { - Map<R, Integer> rowIndex = Maps.newHashMap(); - Map<R, Map<C, V>> rows = Maps.newLinkedHashMap(); - for (R row : rowSpace) { - rowIndex.put(row, rows.size()); - rows.put(row, new LinkedHashMap<C, V>()); - } - Map<C, Map<R, V>> columns = Maps.newLinkedHashMap(); - for (C col : columnSpace) { - columns.put(col, new LinkedHashMap<R, V>()); + /** + * Creates a {@link Map} over the key space with + * {@link ImmutableMap.Builder}s ready for values. + */ + private static final <A, B, V> Map<A, ImmutableMap.Builder<B, V>> + makeIndexBuilder(ImmutableSet<A> keySpace) { + Map<A, ImmutableMap.Builder<B, V>> indexBuilder = Maps.newLinkedHashMap(); + for (A key : keySpace) { + indexBuilder.put(key, ImmutableMap.<B, V>builder()); } - int[] iterationOrderRow = new int[cellList.size()]; - int[] iterationOrderColumn = new int[cellList.size()]; - for (int i = 0; i < cellList.size(); i++) { - Cell<R, C, V> cell = cellList.get(i); + return indexBuilder; + } + + /** + * Builds the value maps of the index and creates an immutable copy of the + * map. + */ + private static final <A, B, V> ImmutableMap<A, Map<B, V>> buildIndex( + Map<A, ImmutableMap.Builder<B, V>> indexBuilder) { + return ImmutableMap.copyOf(Maps.transformValues(indexBuilder, + new Function<ImmutableMap.Builder<B, V>, Map<B, V>>() { + @Override public Map<B, V> apply(ImmutableMap.Builder<B, V> from) { + return from.build(); + } + })); + } + + SparseImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet, + ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { + super(cellSet); + Map<R, ImmutableMap.Builder<C, V>> rowIndexBuilder + = makeIndexBuilder(rowSpace); + Map<C, ImmutableMap.Builder<R, V>> columnIndexBuilder + = makeIndexBuilder(columnSpace); + for (Cell<R, C, V> cell : cellSet) { R rowKey = cell.getRowKey(); C columnKey = cell.getColumnKey(); V value = cell.getValue(); - - iterationOrderRow[i] = rowIndex.get(rowKey); - Map<C, V> thisRow = rows.get(rowKey); - iterationOrderColumn[i] = thisRow.size(); - V oldValue = thisRow.put(columnKey, value); - if (oldValue != null) { - throw new IllegalArgumentException("Duplicate value for row=" + rowKey + ", column=" - + columnKey + ": " + value + ", " + oldValue); - } - columns.get(columnKey).put(rowKey, value); - } - this.iterationOrderRow = iterationOrderRow; - this.iterationOrderColumn = iterationOrderColumn; - ImmutableMap.Builder<R, Map<C, V>> rowBuilder = ImmutableMap.builder(); - for (Map.Entry<R, Map<C, V>> row : rows.entrySet()) { - rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue())); + rowIndexBuilder.get(rowKey).put(columnKey, value); + columnIndexBuilder.get(columnKey).put(rowKey, value); } - this.rowMap = rowBuilder.build(); - - ImmutableMap.Builder<C, Map<R, V>> columnBuilder = ImmutableMap.builder(); - for (Map.Entry<C, Map<R, V>> col : columns.entrySet()) { - columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue())); - } - this.columnMap = columnBuilder.build(); + this.rowMap = buildIndex(rowIndexBuilder); + this.columnMap = buildIndex(columnIndexBuilder); } @Override public ImmutableMap<R, V> column(C columnKey) { @@ -272,153 +265,6 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { Map<C, V> row = rowMap.get(rowKey); return (row == null) ? null : row.get(columnKey); } - - @Override - ImmutableCollection<V> createValues() { - return new ImmutableList<V>() { - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - public V get(int index) { - int rowIndex = iterationOrderRow[index]; - ImmutableMap<C, V> row = (ImmutableMap<C, V>) rowMap.values().asList().get(rowIndex); - int columnIndex = iterationOrderColumn[index]; - return row.values().asList().get(columnIndex); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - ImmutableSet<Cell<R, C, V>> createCellSet() { - return new SparseCellSet(); - } - - class SparseCellSet extends CellSet { - @Override - public UnmodifiableIterator<Cell<R, C, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Cell<R, C, V>> createAsList() { - return new ImmutableAsList<Cell<R, C, V>>() { - @Override - public Cell<R, C, V> get(int index) { - int rowIndex = iterationOrderRow[index]; - Map.Entry<R, Map<C, V>> rowEntry = rowMap.entrySet().asList().get(rowIndex); - ImmutableMap<C, V> row = (ImmutableMap<C, V>) rowEntry.getValue(); - int columnIndex = iterationOrderColumn[index]; - Map.Entry<C, V> colEntry = row.entrySet().asList().get(columnIndex); - return Tables.immutableCell(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue()); - } - - @Override - ImmutableCollection<Cell<R, C, V>> delegateCollection() { - return SparseCellSet.this; - } - }; - } - } - } - - /** - * An immutable map implementation backed by an indexed nullable array, used in - * DenseImmutableTable. - */ - private abstract static class ImmutableArrayMap<K, V> extends ImmutableMap<K, V> { - private final int size; - - ImmutableArrayMap(int size) { - this.size = size; - } - - abstract ImmutableMap<K, Integer> keyToIndex(); - - // True if getValue never returns null. - private boolean isFull() { - return size == keyToIndex().size(); - } - - K getKey(int index) { - return keyToIndex().keySet().asList().get(index); - } - - @Nullable abstract V getValue(int keyIndex); - - @Override - ImmutableSet<K> createKeySet() { - return isFull() ? keyToIndex().keySet() : super.createKeySet(); - } - - @Override - public int size() { - return size; - } - - @Override - public V get(@Nullable Object key) { - Integer keyIndex = keyToIndex().get(key); - return (keyIndex == null) ? null : getValue(keyIndex); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - if (isFull()) { - return new ImmutableMapEntrySet<K, V>() { - @Override ImmutableMap<K, V> map() { - return ImmutableArrayMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new AbstractIndexedListIterator<Entry<K, V>>(size()) { - @Override - protected Entry<K, V> get(int index) { - return Maps.immutableEntry(getKey(index), getValue(index)); - } - }; - } - }; - } else { - return new ImmutableMapEntrySet<K, V>() { - @Override ImmutableMap<K, V> map() { - return ImmutableArrayMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new AbstractIterator<Entry<K, V>>() { - private int index = -1; - private final int maxIndex = keyToIndex().size(); - - @Override - protected Entry<K, V> computeNext() { - for (index++; index < maxIndex; index++) { - V value = getValue(index); - if (value != null) { - return Maps.immutableEntry(getKey(index), value); - } - } - return endOfData(); - } - }; - } - }; - } - } } /** @@ -428,20 +274,14 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { static final class DenseImmutableTable<R, C, V> extends RegularImmutableTable<R, C, V> { - private final ImmutableMap<R, Integer> rowKeyToIndex; - private final ImmutableMap<C, Integer> columnKeyToIndex; - private final ImmutableMap<R, Map<C, V>> rowMap; - private final ImmutableMap<C, Map<R, V>> columnMap; - private final int[] rowCounts; - private final int[] columnCounts; + private final ImmutableBiMap<R, Integer> rowKeyToIndex; + private final ImmutableBiMap<C, Integer> columnKeyToIndex; private final V[][] values; - private final int[] iterationOrderRow; - private final int[] iterationOrderColumn; - private static <E> ImmutableMap<E, Integer> makeIndex( + private static <E> ImmutableBiMap<E, Integer> makeIndex( ImmutableSet<E> set) { - ImmutableMap.Builder<E, Integer> indexBuilder = - ImmutableMap.builder(); + ImmutableBiMap.Builder<E, Integer> indexBuilder = + ImmutableBiMap.builder(); int i = 0; for (E key : set) { indexBuilder.put(key, i); @@ -450,19 +290,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return indexBuilder.build(); } - DenseImmutableTable(ImmutableList<Cell<R, C, V>> cellList, + DenseImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet, ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { + super(cellSet); @SuppressWarnings("unchecked") V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()]; this.values = array; this.rowKeyToIndex = makeIndex(rowSpace); this.columnKeyToIndex = makeIndex(columnSpace); - rowCounts = new int[rowKeyToIndex.size()]; - columnCounts = new int[columnKeyToIndex.size()]; - int[] iterationOrderRow = new int[cellList.size()]; - int[] iterationOrderColumn = new int[cellList.size()]; - for (int i = 0; i < cellList.size(); i++) { - Cell<R, C, V> cell = cellList.get(i); + for (Cell<R, C, V> cell : cellSet) { R rowKey = cell.getRowKey(); C columnKey = cell.getColumnKey(); int rowIndex = rowKeyToIndex.get(rowKey); @@ -471,113 +307,25 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey, columnKey); values[rowIndex][columnIndex] = cell.getValue(); - rowCounts[rowIndex]++; - columnCounts[columnIndex]++; - iterationOrderRow[i] = rowIndex; - iterationOrderColumn[i] = columnIndex; - } - this.iterationOrderRow = iterationOrderRow; - this.iterationOrderColumn = iterationOrderColumn; - this.rowMap = new RowMap(); - this.columnMap = new ColumnMap(); - } - - private final class Row extends ImmutableArrayMap<C, V> { - private final int rowIndex; - - Row(int rowIndex) { - super(rowCounts[rowIndex]); - this.rowIndex = rowIndex; - } - - @Override - ImmutableMap<C, Integer> keyToIndex() { - return columnKeyToIndex; - } - - @Override - V getValue(int keyIndex) { - return values[rowIndex][keyIndex]; - } - - @Override - boolean isPartialView() { - return true; - } - } - - private final class Column extends ImmutableArrayMap<R, V> { - private final int columnIndex; - - Column(int columnIndex) { - super(columnCounts[columnIndex]); - this.columnIndex = columnIndex; - } - - @Override - ImmutableMap<R, Integer> keyToIndex() { - return rowKeyToIndex; - } - - @Override - V getValue(int keyIndex) { - return values[keyIndex][columnIndex]; - } - - @Override - boolean isPartialView() { - return true; - } - } - - private final class RowMap extends ImmutableArrayMap<R, Map<C, V>> { - private RowMap() { - super(rowCounts.length); - } - - @Override - ImmutableMap<R, Integer> keyToIndex() { - return rowKeyToIndex; - } - - @Override - Map<C, V> getValue(int keyIndex) { - return new Row(keyIndex); - } - - @Override - boolean isPartialView() { - return false; - } - } - - private final class ColumnMap extends ImmutableArrayMap<C, Map<R, V>> { - private ColumnMap() { - super(columnCounts.length); - } - - @Override - ImmutableMap<C, Integer> keyToIndex() { - return columnKeyToIndex; - } - - @Override - Map<R, V> getValue(int keyIndex) { - return new Column(keyIndex); - } - - @Override - boolean isPartialView() { - return false; } } @Override public ImmutableMap<R, V> column(C columnKey) { - Integer columnIndex = columnKeyToIndex.get(checkNotNull(columnKey)); - if (columnIndex == null) { + checkNotNull(columnKey); + Integer columnIndexInteger = columnKeyToIndex.get(columnKey); + if (columnIndexInteger == null) { return ImmutableMap.of(); } else { - return new Column(columnIndex); + // unbox only once + int columnIndex = columnIndexInteger; + ImmutableMap.Builder<R, V> columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < values.length; i++) { + V value = values[i][columnIndex]; + if (value != null) { + columnBuilder.put(rowKeyToIndex.inverse().get(i), value); + } + } + return columnBuilder.build(); } } @@ -585,8 +333,31 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return columnKeyToIndex.keySet(); } + private transient volatile ImmutableMap<C, Map<R, V>> columnMap; + + private ImmutableMap<C, Map<R, V>> makeColumnMap() { + ImmutableMap.Builder<C, Map<R, V>> columnMapBuilder = + ImmutableMap.builder(); + for (int c = 0; c < columnKeyToIndex.size(); c++) { + ImmutableMap.Builder<R, V> rowMapBuilder = ImmutableMap.builder(); + for (int r = 0; r < rowKeyToIndex.size(); r++) { + V value = values[r][c]; + if (value != null) { + rowMapBuilder.put(rowKeyToIndex.inverse().get(r), value); + } + } + columnMapBuilder.put(columnKeyToIndex.inverse().get(c), + rowMapBuilder.build()); + } + return columnMapBuilder.build(); + } + @Override public ImmutableMap<C, Map<R, V>> columnMap() { - return columnMap; + ImmutableMap<C, Map<R, V>> result = columnMap; + if (result == null) { + columnMap = result = makeColumnMap(); + } + return result; } @Override public boolean contains(@Nullable Object rowKey, @@ -616,7 +387,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { if (rowIndex == null) { return ImmutableMap.of(); } else { - return new Row(rowIndex); + ImmutableMap.Builder<C, V> rowBuilder = ImmutableMap.builder(); + V[] row = values[rowIndex]; + for (int r = 0; r < row.length; r++) { + V value = row[r]; + if (value != null) { + rowBuilder.put(columnKeyToIndex.inverse().get(r), value); + } + } + return rowBuilder.build(); } } @@ -624,66 +403,31 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return rowKeyToIndex.keySet(); } - @Override - public ImmutableMap<R, Map<C, V>> rowMap() { - return rowMap; - } + private transient volatile ImmutableMap<R, Map<C, V>> rowMap; - @Override - ImmutableCollection<V> createValues() { - return new ImmutableList<V>() { - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - public V get(int index) { - return values[iterationOrderRow[index]][iterationOrderColumn[index]]; - } - - @Override - boolean isPartialView() { - return true; + private ImmutableMap<R, Map<C, V>> makeRowMap() { + ImmutableMap.Builder<R, Map<C, V>> rowMapBuilder = ImmutableMap.builder(); + for (int r = 0; r < values.length; r++) { + V[] row = values[r]; + ImmutableMap.Builder<C, V> columnMapBuilder = ImmutableMap.builder(); + for (int c = 0; c < row.length; c++) { + V value = row[c]; + if (value != null) { + columnMapBuilder.put(columnKeyToIndex.inverse().get(c), value); + } } - }; - } - - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - ImmutableSet<Cell<R, C, V>> createCellSet() { - return new DenseCellSet(); - } - - class DenseCellSet extends CellSet { - @Override - public UnmodifiableIterator<Cell<R, C, V>> iterator() { - return asList().iterator(); + rowMapBuilder.put(rowKeyToIndex.inverse().get(r), + columnMapBuilder.build()); } + return rowMapBuilder.build(); + } - @Override - ImmutableList<Cell<R, C, V>> createAsList() { - return new ImmutableAsList<Cell<R, C, V>>() { - @Override - public Cell<R, C, V> get(int index) { - int rowIndex = iterationOrderRow[index]; - int columnIndex = iterationOrderColumn[index]; - R rowKey = rowKeySet().asList().get(rowIndex); - C columnKey = columnKeySet().asList().get(columnIndex); - V value = values[rowIndex][columnIndex]; - return Tables.immutableCell(rowKey, columnKey, value); - } - - @Override - ImmutableCollection<Cell<R, C, V>> delegateCollection() { - return DenseCellSet.this; - } - }; + @Override public ImmutableMap<R, Map<C, V>> rowMap() { + ImmutableMap<R, Map<C, V>> result = rowMap; + if (result == null) { + rowMap = result = makeRowMap(); } + return result; } } } diff --git a/guava/src/com/google/common/collect/SetMultimap.java b/guava/src/com/google/common/collect/SetMultimap.java index 18f4a18..1409d8d 100644 --- a/guava/src/com/google/common/collect/SetMultimap.java +++ b/guava/src/com/google/common/collect/SetMultimap.java @@ -26,22 +26,13 @@ import javax.annotation.Nullable; /** * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a - * key-value pair that's already in the multimap has no effect. See the {@link - * Multimap} documentation for information common to all multimaps. + * key-value pair that's already in the multimap has no effect. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link Set} of values, while {@link #entries} returns a {@code * Set} of map entries. Though the method signature doesn't say so explicitly, * the map returned by {@link #asMap} has {@code Set} values. * - * <p>If the values corresponding to a single key should be ordered according to - * a {@link java.util.Comparator} (or the natural order), see the - * {@link SortedSetMultimap} subinterface. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. - * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) */ diff --git a/guava/src/com/google/common/collect/Sets.java b/guava/src/com/google/common/collect/Sets.java index 95298e7..14ad867 100644 --- a/guava/src/com/google/common/collect/Sets.java +++ b/guava/src/com/google/common/collect/Sets.java @@ -19,11 +19,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Collections2.FilteredCollection; +import com.google.common.math.IntMath; import java.io.IOException; import java.io.ObjectInputStream; @@ -39,22 +44,16 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.concurrent.CopyOnWriteArraySet; import javax.annotation.Nullable; /** * Static utility methods pertaining to {@link Set} instances. Also see this - * class's counterparts {@link Lists}, {@link Maps} and {@link Queues}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Sets"> - * {@code Sets}</a>. + * class's counterparts {@link Lists} and {@link Maps}. * * @author Kevin Bourrillion * @author Jared Levy @@ -66,22 +65,6 @@ public final class Sets { private Sets() {} /** - * {@link AbstractSet} substitute without the potentially-quadratic - * {@code removeAll} implementation. - */ - abstract static class ImprovedAbstractSet<E> extends AbstractSet<E> { - @Override - public boolean removeAll(Collection<?> c) { - return removeAllImpl(this, c); - } - - @Override - public boolean retainAll(Collection<?> c) { - return super.retainAll(checkNotNull(c)); // GWT compatibility - } - } - - /** * Returns an immutable set instance containing the given enum elements. * Internally, the returned set will be backed by an {@link EnumSet}. * @@ -96,7 +79,7 @@ public final class Sets { @GwtCompatible(serializable = true) public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet( E anElement, E... otherElements) { - return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements)); + return new ImmutableEnumSet<E>(EnumSet.of(anElement, otherElements)); } /** @@ -114,25 +97,20 @@ public final class Sets { @GwtCompatible(serializable = true) public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet( Iterable<E> elements) { - if (elements instanceof ImmutableEnumSet) { - return (ImmutableEnumSet<E>) elements; - } else if (elements instanceof Collection) { - Collection<E> collection = (Collection<E>) elements; - if (collection.isEmpty()) { - return ImmutableSet.of(); - } else { - return ImmutableEnumSet.asImmutable(EnumSet.copyOf(collection)); - } - } else { - Iterator<E> itr = elements.iterator(); - if (itr.hasNext()) { - EnumSet<E> enumSet = EnumSet.of(itr.next()); - Iterators.addAll(enumSet, itr); - return ImmutableEnumSet.asImmutable(enumSet); - } else { - return ImmutableSet.of(); - } + Iterator<E> iterator = elements.iterator(); + if (!iterator.hasNext()) { + return ImmutableSet.of(); + } + if (elements instanceof EnumSet) { + EnumSet<E> enumSetClone = EnumSet.copyOf((EnumSet<E>) elements); + return new ImmutableEnumSet<E>(enumSetClone); } + E first = iterator.next(); + EnumSet<E> set = EnumSet.of(first); + while (iterator.hasNext()) { + set.add(iterator.next()); + } + return new ImmutableEnumSet<E>(set); } /** @@ -143,6 +121,20 @@ public final class Sets { */ public static <E extends Enum<E>> EnumSet<E> newEnumSet(Iterable<E> iterable, Class<E> elementType) { + /* + * TODO(cpovirk): noneOf() and addAll() will both throw + * NullPointerExceptions when appropriate. However, NullPointerTester will + * fail on this method because it passes in Class.class instead of an enum + * type. This means that, when iterable is null but elementType is not, + * noneOf() will throw a ClassCastException before addAll() has a chance to + * throw a NullPointerException. NullPointerTester considers this a failure. + * Ideally the test would be fixed, but it would require a special case for + * Class<E> where E extends Enum. Until that happens (if ever), leave + * checkNotNull() here. For now, contemplate the irony that checking + * elementType, the problem argument, is harmful, while checking iterable, + * the innocent bystander, is effective. + */ + checkNotNull(iterable); EnumSet<E> set = EnumSet.noneOf(elementType); Iterables.addAll(set, iterable); return set; @@ -367,38 +359,6 @@ public final class Sets { } /** - * Creates an empty {@code CopyOnWriteArraySet} instance. - * - * <p><b>Note:</b> if you need an immutable empty {@link Set}, use - * {@link Collections#emptySet} instead. - * - * @return a new, empty {@code CopyOnWriteArraySet} - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArraySet") - public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet() { - return new CopyOnWriteArraySet<E>(); - } - - /** - * Creates a {@code CopyOnWriteArraySet} instance containing the given elements. - * - * @param elements the elements that the set should contain, in order - * @return a new {@code CopyOnWriteArraySet} containing those elements - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArraySet") - public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet( - Iterable<? extends E> elements) { - // We copy elements to an ArrayList first, rather than incurring the - // quadratic cost of adding them to the COWAS directly. - Collection<? extends E> elementsCollection = (elements instanceof Collection) - ? Collections2.cast(elements) - : Lists.newArrayList(elements); - return new CopyOnWriteArraySet<E>(elementsCollection); - } - - /** * Creates an {@code EnumSet} consisting of all enum values that are not in * the specified collection. If the collection is an {@link EnumSet}, this * method has the same behavior as {@link EnumSet#complementOf}. Otherwise, @@ -618,11 +578,6 @@ public final class Sets { * <p><b>Note:</b> The returned view performs better when {@code set1} is the * smaller of the two sets. If you have reason to believe one of your sets * will generally be smaller than the other, pass it first. - * - * <p>Further, note that the current implementation is not suitable for nested - * {@code union} views, i.e. the following should be avoided when in a loop: - * {@code union = Sets.union(union, anotherSet);}, since iterating over the resulting - * set has a cubic complexity to the depth of the nesting. */ public static <E> SetView<E> union( final Set<? extends E> set1, final Set<? extends E> set2) { @@ -853,13 +808,10 @@ public final class Sets { * * @since 11.0 */ + @Beta + @SuppressWarnings("unchecked") public static <E> SortedSet<E> filter( SortedSet<E> unfiltered, Predicate<? super E> predicate) { - return Platform.setsFilterSortedSet(unfiltered, predicate); - } - - static <E> SortedSet<E> filterSortedIgnoreNavigable( - SortedSet<E> unfiltered, Predicate<? super E> predicate) { if (unfiltered instanceof FilteredSet) { // Support clear(), removeAll(), and retainAll() when filtering a filtered // collection. @@ -874,13 +826,21 @@ public final class Sets { checkNotNull(unfiltered), checkNotNull(predicate)); } - private static class FilteredSortedSet<E> extends FilteredSet<E> + private static class FilteredSortedSet<E> extends FilteredCollection<E> implements SortedSet<E> { FilteredSortedSet(SortedSet<E> unfiltered, Predicate<? super E> predicate) { super(unfiltered, predicate); } + @Override public boolean equals(@Nullable Object object) { + return equalsImpl(this, object); + } + + @Override public int hashCode() { + return hashCodeImpl(this); + } + @Override public Comparator<? super E> comparator() { return ((SortedSet<E>) unfiltered).comparator(); @@ -921,145 +881,6 @@ public final class Sets { } /** - * Returns the elements of a {@code NavigableSet}, {@code unfiltered}, that - * satisfy a predicate. The returned set is a live view of {@code unfiltered}; - * changes to one affect the other. - * - * <p>The resulting set's iterator does not support {@code remove()}, but all - * other set methods are supported. When given an element that doesn't satisfy - * the predicate, the set's {@code add()} and {@code addAll()} methods throw - * an {@link IllegalArgumentException}. When methods such as - * {@code removeAll()} and {@code clear()} are called on the filtered set, - * only elements that satisfy the filter will be removed from the underlying - * set. - * - * <p>The returned set isn't threadsafe or serializable, even if - * {@code unfiltered} is. - * - * <p>Many of the filtered set's methods, such as {@code size()}, iterate across - * every element in the underlying set and determine which elements satisfy - * the filter. When a live view is <i>not</i> needed, it may be faster to copy - * {@code Iterables.filter(unfiltered, predicate)} and use the copy. - * - * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, - * as documented at {@link Predicate#apply}. Do not provide a predicate such as - * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with - * equals. (See {@link Iterables#filter(Iterable, Class)} for related - * functionality.) - * - * @since 14.0 - */ - @GwtIncompatible("NavigableSet") - @SuppressWarnings("unchecked") - public static <E> NavigableSet<E> filter( - NavigableSet<E> unfiltered, Predicate<? super E> predicate) { - if (unfiltered instanceof FilteredSet) { - // Support clear(), removeAll(), and retainAll() when filtering a filtered - // collection. - FilteredSet<E> filtered = (FilteredSet<E>) unfiltered; - Predicate<E> combinedPredicate - = Predicates.<E>and(filtered.predicate, predicate); - return new FilteredNavigableSet<E>( - (NavigableSet<E>) filtered.unfiltered, combinedPredicate); - } - - return new FilteredNavigableSet<E>( - checkNotNull(unfiltered), checkNotNull(predicate)); - } - - @GwtIncompatible("NavigableSet") - private static class FilteredNavigableSet<E> extends FilteredSortedSet<E> - implements NavigableSet<E> { - FilteredNavigableSet(NavigableSet<E> unfiltered, Predicate<? super E> predicate) { - super(unfiltered, predicate); - } - - NavigableSet<E> unfiltered() { - return (NavigableSet<E>) unfiltered; - } - - @Override - @Nullable - public E lower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - @Override - @Nullable - public E floor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - @Override - public E ceiling(E e) { - return Iterables.getFirst(tailSet(e, true), null); - } - - @Override - public E higher(E e) { - return Iterables.getFirst(tailSet(e, false), null); - } - - @Override - public E pollFirst() { - Iterator<E> unfilteredIterator = unfiltered().iterator(); - while (unfilteredIterator.hasNext()) { - E e = unfilteredIterator.next(); - if (predicate.apply(e)) { - unfilteredIterator.remove(); - return e; - } - } - return null; - } - - @Override - public E pollLast() { - Iterator<E> unfilteredIterator = unfiltered().descendingIterator(); - while (unfilteredIterator.hasNext()) { - E e = unfilteredIterator.next(); - if (predicate.apply(e)) { - unfilteredIterator.remove(); - return e; - } - } - return null; - } - - @Override - public NavigableSet<E> descendingSet() { - return Sets.filter(unfiltered().descendingSet(), predicate); - } - - @Override - public Iterator<E> descendingIterator() { - return Iterators.filter(unfiltered().descendingIterator(), predicate); - } - - @Override - public E last() { - return descendingIterator().next(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return filter( - unfiltered().subSet(fromElement, fromInclusive, toElement, toInclusive), predicate); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return filter(unfiltered().headSet(toElement, inclusive), predicate); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return filter(unfiltered().tailSet(fromElement, inclusive), predicate); - } - } - - /** * Returns every possible list that can be formed by choosing one element * from each of the given sets in order; the "n-ary * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian @@ -1080,22 +901,12 @@ public final class Sets { * <li>{@code ImmutableList.of(2, "C")} * </ul> * - * The result is guaranteed to be in the "traditional", lexicographical - * order for Cartesian products that you would get from nesting for loops: - * <pre> {@code - * - * for (B b0 : sets.get(0)) { - * for (B b1 : sets.get(1)) { - * ... - * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...); - * // operate on tuple - * } - * }}</pre> - * - * Note that if any input set is empty, the Cartesian product will also be - * empty. If no sets at all are provided (an empty list), the resulting - * Cartesian product has one element, an empty list (counter-intuitive, but - * mathematically consistent). + * The order in which these lists are returned is not guaranteed, however the + * position of an element inside a tuple always corresponds to the position of + * the set from which it came in the input list. Note that if any input set is + * empty, the Cartesian product will also be empty. If no sets at all are + * provided (an empty list), the resulting Cartesian product has one element, + * an empty list (counter-intuitive, but mathematically consistent). * * <p><i>Performance notes:</i> while the cartesian product of sets of size * {@code m, n, p} is a set of size {@code m x n x p}, its actual memory @@ -1121,7 +932,8 @@ public final class Sets { return ImmutableSet.of(); } } - return CartesianSet.create(sets); + CartesianSet<B> cartesianSet = new CartesianSet<B>(sets); + return cartesianSet; } /** @@ -1145,22 +957,12 @@ public final class Sets { * <li>{@code ImmutableList.of(2, "C")} * </ul> * - * The result is guaranteed to be in the "traditional", lexicographical - * order for Cartesian products that you would get from nesting for loops: - * <pre> {@code - * - * for (B b0 : sets.get(0)) { - * for (B b1 : sets.get(1)) { - * ... - * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...); - * // operate on tuple - * } - * }}</pre> - * - * Note that if any input set is empty, the Cartesian product will also be - * empty. If no sets at all are provided (an empty list), the resulting - * Cartesian product has one element, an empty list (counter-intuitive, but - * mathematically consistent). + * The order in which these lists are returned is not guaranteed, however the + * position of an element inside a tuple always corresponds to the position of + * the set from which it came in the input list. Note that if any input set is + * empty, the Cartesian product will also be empty. If no sets at all are + * provided, the resulting Cartesian product has one element, an empty list + * (counter-intuitive, but mathematically consistent). * * <p><i>Performance notes:</i> while the cartesian product of sets of size * {@code m, n, p} is a set of size {@code m x n x p}, its actual memory @@ -1184,51 +986,73 @@ public final class Sets { return cartesianProduct(Arrays.asList(sets)); } - private static final class CartesianSet<E> - extends ForwardingCollection<List<E>> implements Set<List<E>> { - private transient final ImmutableList<ImmutableSet<E>> axes; - private transient final CartesianList<E> delegate; - - static <E> Set<List<E>> create(List<? extends Set<? extends E>> sets) { - ImmutableList.Builder<ImmutableSet<E>> axesBuilder = - new ImmutableList.Builder<ImmutableSet<E>>(sets.size()); - for (Set<? extends E> set : sets) { - ImmutableSet<E> copy = ImmutableSet.copyOf(set); - if (copy.isEmpty()) { - return ImmutableSet.of(); + private static class CartesianSet<B> extends AbstractSet<List<B>> { + final ImmutableList<Axis> axes; + final int size; + + CartesianSet(List<? extends Set<? extends B>> sets) { + int dividend = 1; + ImmutableList.Builder<Axis> builder = ImmutableList.builder(); + try { + for (Set<? extends B> set : sets) { + Axis axis = new Axis(set, dividend); + builder.add(axis); + dividend = IntMath.checkedMultiply(dividend, axis.size()); } - axesBuilder.add(copy); + } catch (ArithmeticException overflow) { + throw new IllegalArgumentException("cartesian product too big"); } - final ImmutableList<ImmutableSet<E>> axes = axesBuilder.build(); - ImmutableList<List<E>> listAxes = new ImmutableList<List<E>>() { + this.axes = builder.build(); + size = dividend; + } - @Override - public int size() { - return axes.size(); - } + @Override public int size() { + return size; + } + + @Override public UnmodifiableIterator<List<B>> iterator() { + return new UnmodifiableIterator<List<B>>() { + int index; @Override - public List<E> get(int index) { - return axes.get(index).asList(); + public boolean hasNext() { + return index < size; } @Override - boolean isPartialView() { - return true; + public List<B> next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + Object[] tuple = new Object[axes.size()]; + for (int i = 0 ; i < tuple.length; i++) { + tuple[i] = axes.get(i).getForIndex(index); + } + index++; + + @SuppressWarnings("unchecked") // only B's are put in here + List<B> result = (ImmutableList<B>) ImmutableList.copyOf(tuple); + return result; } }; - return new CartesianSet<E>(axes, new CartesianList<E>(listAxes)); } - private CartesianSet( - ImmutableList<ImmutableSet<E>> axes, CartesianList<E> delegate) { - this.axes = axes; - this.delegate = delegate; - } - - @Override - protected Collection<List<E>> delegate() { - return delegate; + @Override public boolean contains(Object element) { + if (!(element instanceof List<?>)) { + return false; + } + List<?> tuple = (List<?>) element; + int dimensions = axes.size(); + if (tuple.size() != dimensions) { + return false; + } + for (int i = 0; i < dimensions; i++) { + if (!axes.get(i).contains(tuple.get(i))) { + return false; + } + } + return true; } @Override public boolean equals(@Nullable Object object) { @@ -1241,26 +1065,56 @@ public final class Sets { return super.equals(object); } - @Override - public int hashCode() { + @Override public int hashCode() { // Warning: this is broken if size() == 0, so it is critical that we // substitute an empty ImmutableSet to the user in place of this // It's a weird formula, but tests prove it works. - int adjust = size() - 1; + int adjust = size - 1; for (int i = 0; i < axes.size(); i++) { adjust *= 31; - adjust = ~~adjust; - // in GWT, we have to deal with integer overflow carefully } - int hash = 1; - for (Set<E> axis : axes) { - hash = 31 * hash + (size() / axis.size() * axis.hashCode()); + return axes.hashCode() + adjust; + } + + private class Axis { + final ImmutableSet<? extends B> choices; + final ImmutableList<? extends B> choicesList; + final int dividend; + + Axis(Set<? extends B> set, int dividend) { + choices = ImmutableSet.copyOf(set); + choicesList = choices.asList(); + this.dividend = dividend; + } + + int size() { + return choices.size(); + } + + B getForIndex(int index) { + return choicesList.get(index / dividend % size()); + } + + boolean contains(Object target) { + return choices.contains(target); + } + + @Override public boolean equals(Object obj) { + if (obj instanceof CartesianSet.Axis) { + CartesianSet.Axis that = (CartesianSet.Axis) obj; + return this.choices.equals(that.choices); + // dividends must be equal or we wouldn't have gotten this far + } + return false; + } - hash = ~~hash; + @Override public int hashCode() { + // Because Axis instances are not exposed, we can + // opportunistically choose whatever bizarre formula happens + // to make CartesianSet.hashCode() as simple as possible. + return size / choices.size() * choices.hashCode(); } - hash += adjust; - return ~~hash; } } @@ -1398,9 +1252,6 @@ public final class Sets { int hashCode = 0; for (Object o : s) { hashCode += o != null ? o.hashCode() : 0; - - hashCode = ~~hashCode; - // Needed to deal with unusual integer overflow in GWT. } return hashCode; } @@ -1427,345 +1278,117 @@ public final class Sets { } /** - * Returns an unmodifiable view of the specified navigable set. This method - * allows modules to provide users with "read-only" access to internal - * navigable sets. Query operations on the returned set "read through" to the - * specified set, and attempts to modify the returned set, whether direct or - * via its collection views, result in an - * {@code UnsupportedOperationException}. - * - * <p>The returned navigable set will be serializable if the specified - * navigable set is serializable. - * - * @param set the navigable set for which an unmodifiable view is to be - * returned - * @return an unmodifiable view of the specified navigable set - * @since 12.0 + * Creates a view of Set<B> for a Set<A>, given a bijection between A and B. + * (Modelled for now as InvertibleFunction<A, B>, can't be Converter<A, B> + * because that's not in Guava, though both designs are less than optimal). + * Note that the bijection is treated as undefined for values not in the + * given Set<A> - it doesn't have to define a true bijection for those. + * + * <p>Note that the returned Set's contains method is unsafe - + * you *must* pass an instance of B to it, since the bijection + * can only invert B's (not any Object) back to A, so we can + * then delegate the call to the original Set<A>. */ - @GwtIncompatible("NavigableSet") - public static <E> NavigableSet<E> unmodifiableNavigableSet( - NavigableSet<E> set) { - if (set instanceof ImmutableSortedSet - || set instanceof UnmodifiableNavigableSet) { - return set; - } - return new UnmodifiableNavigableSet<E>(set); + static <A, B> Set<B> transform( + Set<A> set, InvertibleFunction<A, B> bijection) { + return new TransformedSet<A, B>( + Preconditions.checkNotNull(set, "set"), + Preconditions.checkNotNull(bijection, "bijection") + ); } - @GwtIncompatible("NavigableSet") - static final class UnmodifiableNavigableSet<E> - extends ForwardingSortedSet<E> implements NavigableSet<E>, Serializable { - private final NavigableSet<E> delegate; - - UnmodifiableNavigableSet(NavigableSet<E> delegate) { - this.delegate = checkNotNull(delegate); - } - - @Override - protected SortedSet<E> delegate() { - return Collections.unmodifiableSortedSet(delegate); - } + /** + * Stop-gap measure since there is no bijection related type in Guava. + */ + abstract static class InvertibleFunction<A, B> implements Function<A, B> { + abstract A invert(B b); - @Override - public E lower(E e) { - return delegate.lower(e); - } + public InvertibleFunction<B, A> inverse() { + return new InvertibleFunction<B, A>() { + @Override public A apply(B b) { + return InvertibleFunction.this.invert(b); + } - @Override - public E floor(E e) { - return delegate.floor(e); - } + @Override B invert(A a) { + return InvertibleFunction.this.apply(a); + } - @Override - public E ceiling(E e) { - return delegate.ceiling(e); + // Not required per se, but just for good karma. + @Override public InvertibleFunction<A, B> inverse() { + return InvertibleFunction.this; + } + }; } + } - @Override - public E higher(E e) { - return delegate.higher(e); - } + private static class TransformedSet<A, B> extends AbstractSet<B> { + final Set<A> delegate; + final InvertibleFunction<A, B> bijection; - @Override - public E pollFirst() { - throw new UnsupportedOperationException(); + TransformedSet(Set<A> delegate, InvertibleFunction<A, B> bijection) { + this.delegate = delegate; + this.bijection = bijection; } - @Override - public E pollLast() { - throw new UnsupportedOperationException(); + @Override public Iterator<B> iterator() { + return Iterators.transform(delegate.iterator(), bijection); } - private transient UnmodifiableNavigableSet<E> descendingSet; - - @Override - public NavigableSet<E> descendingSet() { - UnmodifiableNavigableSet<E> result = descendingSet; - if (result == null) { - result = descendingSet = new UnmodifiableNavigableSet<E>( - delegate.descendingSet()); - result.descendingSet = this; - } - return result; + @Override public int size() { + return delegate.size(); } - @Override - public Iterator<E> descendingIterator() { - return Iterators.unmodifiableIterator(delegate.descendingIterator()); + @SuppressWarnings("unchecked") // unsafe, passed object *must* be B + @Override public boolean contains(Object o) { + B b = (B) o; + A a = bijection.invert(b); + /* + * Mathematically, Converter<A, B> defines a bijection between ALL A's + * on ALL B's. Here we concern ourselves with a subset + * of this relation: we only want the part that is defined by a *subset* + * of all A's (defined by that Set<A> delegate), and the image + * of *that* on B (which is this set). We don't care whether + * the converter is *not* a bijection for A's that are not in Set<A> + * or B's not in this Set<B>. + * + * We only want to return true if and only f the user passes a B instance + * that is contained in precisely in the image of Set<A>. + * + * The first test is whether the inverse image of this B is indeed + * in Set<A>. But we don't know whether that B belongs in this Set<B> + * or not; if not, the converter is free to return + * anything it wants, even an element of Set<A> (and this relationship + * is not part of the Set<A> <--> Set<B> bijection), and we must not + * be confused by that. So we have to do a final check to see if the + * image of that A is really equivalent to the passed B, which proves + * that the given B belongs indeed in the image of Set<A>. + */ + return delegate.contains(a) && Objects.equal(bijection.apply(a), o); } - @Override - public NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return unmodifiableNavigableSet(delegate.subSet( - fromElement, - fromInclusive, - toElement, - toInclusive)); + @Override public boolean add(B b) { + return delegate.add(bijection.invert(b)); } - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return unmodifiableNavigableSet(delegate.headSet(toElement, inclusive)); + @SuppressWarnings("unchecked") // unsafe, passed object *must* be B + @Override public boolean remove(Object o) { + return contains(o) && delegate.remove(bijection.invert((B) o)); } - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return unmodifiableNavigableSet( - delegate.tailSet(fromElement, inclusive)); + @Override public void clear() { + delegate.clear(); } - - private static final long serialVersionUID = 0; - } - - /** - * Returns a synchronized (thread-safe) navigable set backed by the specified - * navigable set. In order to guarantee serial access, it is critical that - * <b>all</b> access to the backing navigable set is accomplished - * through the returned navigable set (or its views). - * - * <p>It is imperative that the user manually synchronize on the returned - * sorted set when iterating over it or any of its {@code descendingSet}, - * {@code subSet}, {@code headSet}, or {@code tailSet} views. <pre> {@code - * - * NavigableSet<E> set = synchronizedNavigableSet(new TreeSet<E>()); - * ... - * synchronized (set) { - * // Must be in the synchronized block - * Iterator<E> it = set.iterator(); - * while (it.hasNext()){ - * foo(it.next()); - * } - * }}</pre> - * - * or: <pre> {@code - * - * NavigableSet<E> set = synchronizedNavigableSet(new TreeSet<E>()); - * NavigableSet<E> set2 = set.descendingSet().headSet(foo); - * ... - * synchronized (set) { // Note: set, not set2!!! - * // Must be in the synchronized block - * Iterator<E> it = set2.descendingIterator(); - * while (it.hasNext()) - * foo(it.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned navigable set will be serializable if the specified - * navigable set is serializable. - * - * @param navigableSet the navigable set to be "wrapped" in a synchronized - * navigable set. - * @return a synchronized view of the specified navigable set. - * @since 13.0 - */ - @GwtIncompatible("NavigableSet") - public static <E> NavigableSet<E> synchronizedNavigableSet( - NavigableSet<E> navigableSet) { - return Synchronized.navigableSet(navigableSet); } /** * Remove each element in an iterable from a set. */ - static boolean removeAllImpl(Set<?> set, Iterator<?> iterator) { + static boolean removeAllImpl(Set<?> set, Iterable<?> iterable) { + // TODO(jlevy): Have ForwardingSet.standardRemoveAll() call this method. boolean changed = false; - while (iterator.hasNext()) { - changed |= set.remove(iterator.next()); + for (Object o : iterable) { + changed |= set.remove(o); } return changed; } - - static boolean removeAllImpl(Set<?> set, Collection<?> collection) { - checkNotNull(collection); // for GWT - if (collection instanceof Multiset) { - collection = ((Multiset<?>) collection).elementSet(); - } - /* - * AbstractSet.removeAll(List) has quadratic behavior if the list size - * is just less than the set's size. We augment the test by - * assuming that sets have fast contains() performance, and other - * collections don't. See - * http://code.google.com/p/guava-libraries/issues/detail?id=1013 - */ - if (collection instanceof Set && collection.size() > set.size()) { - Iterator<?> setIterator = set.iterator(); - boolean changed = false; - while (setIterator.hasNext()) { - if (collection.contains(setIterator.next())) { - changed = true; - setIterator.remove(); - } - } - return changed; - } else { - return removeAllImpl(set, collection.iterator()); - } - } - - @GwtIncompatible("NavigableSet") - static class DescendingSet<E> extends ForwardingNavigableSet<E> { - private final NavigableSet<E> forward; - - DescendingSet(NavigableSet<E> forward) { - this.forward = forward; - } - - @Override - protected NavigableSet<E> delegate() { - return forward; - } - - @Override - public E lower(E e) { - return forward.higher(e); - } - - @Override - public E floor(E e) { - return forward.ceiling(e); - } - - @Override - public E ceiling(E e) { - return forward.floor(e); - } - - @Override - public E higher(E e) { - return forward.lower(e); - } - - @Override - public E pollFirst() { - return forward.pollLast(); - } - - @Override - public E pollLast() { - return forward.pollFirst(); - } - - @Override - public NavigableSet<E> descendingSet() { - return forward; - } - - @Override - public Iterator<E> descendingIterator() { - return forward.iterator(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return forward.tailSet(toElement, inclusive).descendingSet(); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return forward.headSet(fromElement, inclusive).descendingSet(); - } - - @SuppressWarnings("unchecked") - @Override - public Comparator<? super E> comparator() { - Comparator<? super E> forwardComparator = forward.comparator(); - if (forwardComparator == null) { - return (Comparator) Ordering.natural().reverse(); - } else { - return reverse(forwardComparator); - } - } - - // If we inline this, we get a javac error. - private static <T> Ordering<T> reverse(Comparator<T> forward) { - return Ordering.from(forward).reverse(); - } - - @Override - public E first() { - return forward.last(); - } - - @Override - public SortedSet<E> headSet(E toElement) { - return standardHeadSet(toElement); - } - - @Override - public E last() { - return forward.first(); - } - - @Override - public SortedSet<E> subSet(E fromElement, E toElement) { - return standardSubSet(fromElement, toElement); - } - - @Override - public SortedSet<E> tailSet(E fromElement) { - return standardTailSet(fromElement); - } - - @Override - public Iterator<E> iterator() { - return forward.descendingIterator(); - } - - @Override - public Object[] toArray() { - return standardToArray(); - } - - @Override - public <T> T[] toArray(T[] array) { - return standardToArray(array); - } - - @Override - public String toString() { - return standardToString(); - } - } - - /** - * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 - */ - static <T> SortedSet<T> cast(Iterable<T> iterable) { - return (SortedSet<T>) iterable; - } } diff --git a/guava/src/com/google/common/collect/SingletonImmutableBiMap.java b/guava/src/com/google/common/collect/SingletonImmutableBiMap.java deleted file mode 100644 index 2b6f462..0000000 --- a/guava/src/com/google/common/collect/SingletonImmutableBiMap.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link ImmutableMap} with exactly one entry. - * - * @author Jesse Wilson - * @author Kevin Bourrillion - */ -@GwtCompatible(serializable = true, emulated = true) -@SuppressWarnings("serial") // uses writeReplace(), not default serialization -final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { - - final transient K singleKey; - final transient V singleValue; - - SingletonImmutableBiMap(K singleKey, V singleValue) { - this.singleKey = singleKey; - this.singleValue = singleValue; - } - - private SingletonImmutableBiMap(K singleKey, V singleValue, - ImmutableBiMap<V, K> inverse) { - this.singleKey = singleKey; - this.singleValue = singleValue; - this.inverse = inverse; - } - - SingletonImmutableBiMap(Entry<K, V> entry) { - this(entry.getKey(), entry.getValue()); - } - - @Override public V get(@Nullable Object key) { - return singleKey.equals(key) ? singleValue : null; - } - - @Override - public int size() { - return 1; - } - - @Override public boolean containsKey(@Nullable Object key) { - return singleKey.equals(key); - } - - @Override public boolean containsValue(@Nullable Object value) { - return singleValue.equals(value); - } - - @Override boolean isPartialView() { - return false; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return ImmutableSet.of(Maps.immutableEntry(singleKey, singleValue)); - } - - @Override - ImmutableSet<K> createKeySet() { - return ImmutableSet.of(singleKey); - } - - transient ImmutableBiMap<V, K> inverse; - - @Override - public ImmutableBiMap<V, K> inverse() { - // racy single-check idiom - ImmutableBiMap<V, K> result = inverse; - if (result == null) { - return inverse = new SingletonImmutableBiMap<V, K>( - singleValue, singleKey, this); - } else { - return result; - } - } -} diff --git a/guava/src/com/google/common/collect/SingletonImmutableList.java b/guava/src/com/google/common/collect/SingletonImmutableList.java index 2e20c41..e546d2b 100644 --- a/guava/src/com/google/common/collect/SingletonImmutableList.java +++ b/guava/src/com/google/common/collect/SingletonImmutableList.java @@ -22,6 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import java.util.List; +import java.util.NoSuchElementException; import javax.annotation.Nullable; @@ -55,7 +56,47 @@ final class SingletonImmutableList<E> extends ImmutableList<E> { } @Override public int lastIndexOf(@Nullable Object object) { - return indexOf(object); + return element.equals(object) ? 0 : -1; + } + + @Override public UnmodifiableListIterator<E> listIterator(final int start) { + Preconditions.checkPositionIndex(start, 1); + return new UnmodifiableListIterator<E>() { + + boolean hasNext = start == 0; + + @Override public boolean hasNext() { + return hasNext; + } + + @Override public boolean hasPrevious() { + return !hasNext; + } + + @Override public E next() { + if (!hasNext) { + throw new NoSuchElementException(); + } + hasNext = false; + return element; + } + + @Override public int nextIndex() { + return hasNext ? 0 : 1; + } + + @Override public E previous() { + if (hasNext) { + throw new NoSuchElementException(); + } + hasNext = true; + return element; + } + + @Override public int previousIndex() { + return hasNext ? -1 : 0; + } + }; } @Override @@ -76,7 +117,7 @@ final class SingletonImmutableList<E> extends ImmutableList<E> { return element.equals(object); } - @Override public boolean equals(@Nullable Object object) { + @Override public boolean equals(Object object) { if (object == this) { return true; } diff --git a/guava/src/com/google/common/collect/SingletonImmutableMap.java b/guava/src/com/google/common/collect/SingletonImmutableMap.java new file mode 100644 index 0000000..9dd6e60 --- /dev/null +++ b/guava/src/com/google/common/collect/SingletonImmutableMap.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008 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 com.google.common.annotations.GwtCompatible; + +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * Implementation of {@link ImmutableMap} with exactly one entry. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class SingletonImmutableMap<K, V> extends ImmutableMap<K, V> { + + final transient K singleKey; + final transient V singleValue; + + private transient Entry<K, V> entry; + + SingletonImmutableMap(K singleKey, V singleValue) { + this.singleKey = singleKey; + this.singleValue = singleValue; + } + + SingletonImmutableMap(Entry<K, V> entry) { + this.entry = entry; + this.singleKey = entry.getKey(); + this.singleValue = entry.getValue(); + } + + private Entry<K, V> entry() { + Entry<K, V> e = entry; + return (e == null) + ? (entry = Maps.immutableEntry(singleKey, singleValue)) : e; + } + + @Override public V get(@Nullable Object key) { + return singleKey.equals(key) ? singleValue : null; + } + + @Override + public int size() { + return 1; + } + + @Override public boolean isEmpty() { + return false; + } + + @Override public boolean containsKey(@Nullable Object key) { + return singleKey.equals(key); + } + + @Override public boolean containsValue(@Nullable Object value) { + return singleValue.equals(value); + } + + @Override boolean isPartialView() { + return false; + } + + private transient ImmutableSet<Entry<K, V>> entrySet; + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = ImmutableSet.of(entry())) : es; + } + + private transient ImmutableSet<K> keySet; + + @Override public ImmutableSet<K> keySet() { + ImmutableSet<K> ks = keySet; + return (ks == null) ? (keySet = ImmutableSet.of(singleKey)) : ks; + } + + private transient ImmutableCollection<V> values; + + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(singleValue)) : v; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + final V singleValue; + + Values(V singleValue) { + this.singleValue = singleValue; + } + + @Override public boolean contains(Object object) { + return singleValue.equals(object); + } + + @Override public boolean isEmpty() { + return false; + } + + @Override + public int size() { + return 1; + } + + @Override public UnmodifiableIterator<V> iterator() { + return Iterators.singletonIterator(singleValue); + } + + @Override boolean isPartialView() { + return true; + } + } + + @Override public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + if (that.size() != 1) { + return false; + } + Entry<?, ?> entry = that.entrySet().iterator().next(); + return singleKey.equals(entry.getKey()) + && singleValue.equals(entry.getValue()); + } + return false; + } + + @Override public int hashCode() { + return singleKey.hashCode() ^ singleValue.hashCode(); + } + + @Override public String toString() { + return new StringBuilder() + .append('{') + .append(singleKey.toString()) + .append('=') + .append(singleValue.toString()) + .append('}') + .toString(); + } +} diff --git a/guava/src/com/google/common/collect/SingletonImmutableTable.java b/guava/src/com/google/common/collect/SingletonImmutableTable.java index e6e850c..4e33aea 100644 --- a/guava/src/com/google/common/collect/SingletonImmutableTable.java +++ b/guava/src/com/google/common/collect/SingletonImmutableTable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Guava Authors + * 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. @@ -28,7 +28,7 @@ import javax.annotation.Nullable; /** * An implementation of {@link ImmutableTable} that holds a single cell. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible final class SingletonImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { @@ -119,7 +119,7 @@ final class SingletonImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; if (that.size() == 1) { Cell<?, ?, ?> thatCell = that.cellSet().iterator().next(); diff --git a/guava/src/com/google/common/collect/SortedIterables.java b/guava/src/com/google/common/collect/SortedIterables.java index 2158e9f..8089b10 100644 --- a/guava/src/com/google/common/collect/SortedIterables.java +++ b/guava/src/com/google/common/collect/SortedIterables.java @@ -17,8 +17,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.collect.Multiset.Entry; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.SortedSet; /** @@ -39,22 +47,149 @@ final class SortedIterables { checkNotNull(elements); Comparator<?> comparator2; if (elements instanceof SortedSet) { - comparator2 = comparator((SortedSet<?>) elements); + SortedSet<?> sortedSet = (SortedSet<?>) elements; + comparator2 = sortedSet.comparator(); + if (comparator2 == null) { + comparator2 = (Comparator) Ordering.natural(); + } } else if (elements instanceof SortedIterable) { comparator2 = ((SortedIterable<?>) elements).comparator(); } else { - return false; + comparator2 = null; } return comparator.equals(comparator2); } + /** + * Returns a sorted collection of the unique elements according to the specified comparator. Does + * not check for null. + */ + @SuppressWarnings("unchecked") + public static <E> Collection<E> sortedUnique( + Comparator<? super E> comparator, Iterator<E> elements) { + SortedSet<E> sortedSet = Sets.newTreeSet(comparator); + Iterators.addAll(sortedSet, elements); + return sortedSet; + } + + /** + * Returns a sorted collection of the unique elements according to the specified comparator. Does + * not check for null. + */ @SuppressWarnings("unchecked") - // if sortedSet.comparator() is null, the set must be naturally ordered - public static <E> Comparator<? super E> comparator(SortedSet<E> sortedSet) { - Comparator<? super E> result = sortedSet.comparator(); - if (result == null) { - result = (Comparator<? super E>) Ordering.natural(); + public static <E> Collection<E> sortedUnique( + Comparator<? super E> comparator, Iterable<E> elements) { + if (elements instanceof Multiset) { + elements = ((Multiset<E>) elements).elementSet(); + } + if (elements instanceof Set) { + if (hasSameComparator(comparator, elements)) { + return (Set<E>) elements; + } + List<E> list = Lists.newArrayList(elements); + Collections.sort(list, comparator); + return list; + } + E[] array = (E[]) Iterables.toArray(elements); + if (!hasSameComparator(comparator, elements)) { + Arrays.sort(array, comparator); + } + return uniquifySortedArray(comparator, array); + } + + private static <E> Collection<E> uniquifySortedArray( + Comparator<? super E> comparator, E[] array) { + if (array.length == 0) { + return Collections.emptySet(); } - return result; + int length = 1; + for (int i = 1; i < array.length; i++) { + int cmp = comparator.compare(array[i], array[length - 1]); + if (cmp != 0) { + array[length++] = array[i]; + } + } + if (length < array.length) { + array = ObjectArrays.arraysCopyOf(array, length); + } + return Arrays.asList(array); + } + + /** + * Returns a collection of multiset entries representing the counts of the distinct elements, in + * sorted order. Does not check for null. + */ + public static <E> Collection<Multiset.Entry<E>> sortedCounts( + Comparator<? super E> comparator, Iterator<E> elements) { + TreeMultiset<E> multiset = TreeMultiset.create(comparator); + Iterators.addAll(multiset, elements); + return multiset.entrySet(); + } + + /** + * Returns a collection of multiset entries representing the counts of the distinct elements, in + * sorted order. Does not check for null. + */ + public static <E> Collection<Multiset.Entry<E>> sortedCounts( + Comparator<? super E> comparator, Iterable<E> elements) { + if (elements instanceof Multiset) { + Multiset<E> multiset = (Multiset<E>) elements; + if (hasSameComparator(comparator, elements)) { + return multiset.entrySet(); + } + List<Multiset.Entry<E>> entries = Lists.newArrayList(multiset.entrySet()); + Collections.sort( + entries, Ordering.from(comparator).onResultOf(new Function<Multiset.Entry<E>, E>() { + @Override + public E apply(Entry<E> entry) { + return entry.getElement(); + } + })); + return entries; + } else if (elements instanceof Set) { // known distinct + Collection<E> sortedElements; + if (hasSameComparator(comparator, elements)) { + sortedElements = (Collection<E>) elements; + } else { + List<E> list = Lists.newArrayList(elements); + Collections.sort(list, comparator); + sortedElements = list; + } + return singletonEntries(sortedElements); + } else if (hasSameComparator(comparator, elements)) { + E current = null; + int currentCount = 0; + List<Multiset.Entry<E>> sortedEntries = Lists.newArrayList(); + for (E e : elements) { + if (currentCount > 0) { + if (comparator.compare(current, e) == 0) { + currentCount++; + } else { + sortedEntries.add(Multisets.immutableEntry(current, currentCount)); + current = e; + currentCount = 1; + } + } else { + current = e; + currentCount = 1; + } + } + if (currentCount > 0) { + sortedEntries.add(Multisets.immutableEntry(current, currentCount)); + } + return sortedEntries; + } + TreeMultiset<E> multiset = TreeMultiset.create(comparator); + Iterables.addAll(multiset, elements); + return multiset.entrySet(); + } + + static <E> Collection<Multiset.Entry<E>> singletonEntries(Collection<E> set) { + return Collections2.transform(set, new Function<E, Multiset.Entry<E>>() { + @Override + public Entry<E> apply(E elem) { + return Multisets.immutableEntry(elem, 1); + } + }); } } diff --git a/guava/src/com/google/common/collect/SortedLists.java b/guava/src/com/google/common/collect/SortedLists.java index 6fc0d71..afcec59 100644 --- a/guava/src/com/google/common/collect/SortedLists.java +++ b/guava/src/com/google/common/collect/SortedLists.java @@ -143,7 +143,7 @@ import javax.annotation.Nullable; */ NEXT_LOWER { @Override - int resultIndex(int higherIndex) { + <E> int resultIndex(int higherIndex) { return higherIndex - 1; } }, @@ -153,7 +153,7 @@ import javax.annotation.Nullable; */ NEXT_HIGHER { @Override - public int resultIndex(int higherIndex) { + public <E> int resultIndex(int higherIndex) { return higherIndex; } }, @@ -171,12 +171,12 @@ import javax.annotation.Nullable; */ INVERTED_INSERTION_INDEX { @Override - public int resultIndex(int higherIndex) { + public <E> int resultIndex(int higherIndex) { return ~higherIndex; } }; - abstract int resultIndex(int higherIndex); + abstract <E> int resultIndex(int higherIndex); } /** @@ -200,7 +200,7 @@ import javax.annotation.Nullable; * KeyAbsentBehavior)} using {@link Ordering#natural}. */ public static <E, K extends Comparable> int binarySearch(List<E> list, - Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior, + Function<? super E, K> keyFunction, K key, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { return binarySearch( list, @@ -221,7 +221,7 @@ import javax.annotation.Nullable; public static <E, K> int binarySearch( List<E> list, Function<? super E, K> keyFunction, - @Nullable K key, + K key, Comparator<? super K> keyComparator, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { diff --git a/guava/src/com/google/common/collect/SortedMapDifference.java b/guava/src/com/google/common/collect/SortedMapDifference.java index 07f75ee..f44aa1a 100644 --- a/guava/src/com/google/common/collect/SortedMapDifference.java +++ b/guava/src/com/google/common/collect/SortedMapDifference.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.SortedMap; @@ -26,6 +27,7 @@ import java.util.SortedMap; * @author Louis Wasserman * @since 8.0 */ +@Beta @GwtCompatible public interface SortedMapDifference<K, V> extends MapDifference<K, V> { diff --git a/guava/src/com/google/common/collect/SortedMaps.java b/guava/src/com/google/common/collect/SortedMaps.java new file mode 100644 index 0000000..0055cbd --- /dev/null +++ b/guava/src/com/google/common/collect/SortedMaps.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2010 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 java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Maps.EntryTransformer; + +/** + * Static utility methods pertaining to {@link SortedMap} instances. + * + * @author Louis Wasserman + * @since 8.0 + * @deprecated Use the identical methods in {@link Maps}. This class is + * scheduled for deletion from Guava in Guava release 12.0. + */ +@Beta +@Deprecated +@GwtCompatible +public final class SortedMaps { + private SortedMaps() {} + + /** + * Returns a view of a sorted map where each value is transformed by a + * function. All other properties of the map, such as iteration order, are + * left intact. For example, the code: <pre> {@code + * + * SortedMap<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9); + * Function<Integer, Double> sqrt = + * new Function<Integer, Double>() { + * public Double apply(Integer in) { + * return Math.sqrt((int) in); + * } + * }; + * SortedMap<String, Double> transformed = + * Maps.transformSortedValues(map, sqrt); + * System.out.println(transformed);}</pre> + * + * ... prints {@code {a=2.0, b=3.0}}. + * + * <p>Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + * <p>It's acceptable for the underlying map to contain null keys, and even + * null values provided that the function is capable of accepting null input. + * The transformed map might contain null values, if the function sometimes + * gives a null result. + * + * <p>The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + * <p>The function is applied lazily, invoked when needed. This is necessary + * for the returned map to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Map#containsValue} and + * {@code Map.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned map doesn't need to be + * a view, copy the returned map into a new map of your choosing. + * + * @deprecated Use {@link Maps#transformValues(SortedMap, Function)} + */ + @Deprecated public static <K, V1, V2> SortedMap<K, V2> transformValues( + SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) { + return Maps.transformValues(fromMap, function); + } + + /** + * Returns a view of a sorted map whose values are derived from the original + * sorted map's entries. In contrast to {@link #transformValues}, this + * method's entry-transformation logic may depend on the key as well as the + * value. + * + * <p>All other properties of the transformed map, such as iteration order, + * are left intact. For example, the code: <pre> {@code + * + * Map<String, Boolean> options = + * ImmutableSortedMap.of("verbose", true, "sort", false); + * EntryTransformer<String, Boolean, String> flagPrefixer = + * new EntryTransformer<String, Boolean, String>() { + * public String transformEntry(String key, Boolean value) { + * return value ? key : "yes" + key; + * } + * }; + * SortedMap<String, String> transformed = + * LabsMaps.transformSortedEntries(options, flagPrefixer); + * System.out.println(transformed);}</pre> + * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + * <p>Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + * <p>It's acceptable for the underlying map to contain null keys and null + * values provided that the transformer is capable of accepting null inputs. + * The transformed map might contain null values if the transformer sometimes + * gives a null result. + * + * <p>The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + * <p>The transformer is applied lazily, invoked when needed. This is + * necessary for the returned map to be a view, but it means that the + * transformer will be applied many times for bulk operations like {@link + * Map#containsValue} and {@link Object#toString}. For this to perform well, + * {@code transformer} should be fast. To avoid lazy evaluation when the + * returned map doesn't need to be a view, copy the returned map into a new + * map of your choosing. + * + * <p><b>Warning:</b> This method assumes that for any instance {@code k} of + * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies + * that {@code k2} is also of type {@code K}. Using an {@code + * EntryTransformer} key type for which this may not hold, such as {@code + * ArrayList}, may risk a {@code ClassCastException} when calling methods on + * the transformed map. + * + * @deprecated Use {@link Maps#transformEntries(SortedMap, EntryTransformer)} + */ + @Deprecated public static <K, V1, V2> SortedMap<K, V2> transformEntries( + final SortedMap<K, V1> fromMap, + EntryTransformer<? super K, ? super V1, V2> transformer) { + return Maps.transformEntries(fromMap, transformer); + } + + /** + * Computes the difference between two sorted maps, using the comparator of + * the left map, or {@code Ordering.natural()} if the left map uses the + * natural ordering of its elements. This difference is an immutable snapshot + * of the state of the maps at the time this method is called. It will never + * change, even if the maps change at a later time. + * + * <p>Since this method uses {@code TreeMap} instances internally, the keys of + * the right map must all compare as distinct according to the comparator + * of the left map. + * + * <p><b>Note:</b>If you only need to know whether two sorted maps have the + * same mappings, call {@code left.equals(right)} instead of this method. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @return the difference between the two maps + * @deprecated Use {@link Maps#difference(SortedMap, Map)} + */ + @Deprecated public static <K, V> SortedMapDifference<K, V> difference( + SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) { + return Maps.difference(left, right); + } + + /** + * Returns the specified comparator if not null; otherwise returns {@code + * Ordering.natural()}. 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") + static <E> Comparator<? super E> orNaturalOrder( + @Nullable Comparator<? super E> comparator) { + if (comparator != null) { // can't use ? : because of javac bug 5080917 + return comparator; + } + return (Comparator<E>) Ordering.natural(); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * keys satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a key that + * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} + * methods throw an {@link IllegalArgumentException}. + * + * <p>When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose keys satisfy the + * filter will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterKeys( + SortedMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { + // TODO: Return a subclass of Maps.FilteredKeyMap for slightly better + // performance. + checkNotNull(keyPredicate); + Predicate<Entry<K, V>> entryPredicate = new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; + return filterEntries(unfiltered, entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * values satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a value + * that doesn't satisfy the predicate, the map's {@code put()}, {@code + * putAll()}, and {@link Entry#setValue} methods throw an {@link + * IllegalArgumentException}. + * + * <p>When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose values satisfy the + * filter will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterValues( + SortedMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + * <p>The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a + * key/value pair that doesn't satisfy the predicate, the map's {@code put()} + * and {@code putAll()} methods throw an {@link IllegalArgumentException}. + * Similarly, the map's entries have a {@link Entry#setValue} method that + * throws an {@link IllegalArgumentException} when the existing key and the + * provided value don't satisfy the predicate. + * + * <p>When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings that satisfy the filter + * will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterEntries( + SortedMap<K, V> unfiltered, + Predicate<? super Entry<K, V>> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredSortedMap) + ? filterFiltered((FilteredSortedMap<K, V>) unfiltered, entryPredicate) + : new FilteredSortedMap<K, V>(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when + * filtering a filtered sorted map. + */ + private static <K, V> SortedMap<K, V> filterFiltered( + FilteredSortedMap<K, V> map, + Predicate<? super Entry<K, V>> entryPredicate) { + Predicate<Entry<K, V>> predicate + = Predicates.and(map.predicate, entryPredicate); + return new FilteredSortedMap<K, V>(map.sortedMap(), predicate); + } + + private static class FilteredSortedMap<K, V> + extends Maps.FilteredEntryMap<K, V> implements SortedMap<K, V> { + + FilteredSortedMap(SortedMap<K, V> unfiltered, + Predicate<? super Entry<K, V>> entryPredicate) { + super(unfiltered, entryPredicate); + } + + SortedMap<K, V> sortedMap() { + return (SortedMap<K, V>) unfiltered; + } + + @Override public Comparator<? super K> comparator() { + return sortedMap().comparator(); + } + + @Override public K firstKey() { + // correctly throws NoSuchElementException when filtered map is empty. + return keySet().iterator().next(); + } + + @Override public K lastKey() { + SortedMap<K, V> headMap = sortedMap(); + while (true) { + // correctly throws NoSuchElementException when filtered map is empty. + K key = headMap.lastKey(); + if (apply(key, unfiltered.get(key))) { + return key; + } + headMap = sortedMap().headMap(key); + } + } + + @Override public SortedMap<K, V> headMap(K toKey) { + return new FilteredSortedMap<K, V>(sortedMap().headMap(toKey), predicate); + } + + @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { + return new FilteredSortedMap<K, V>( + sortedMap().subMap(fromKey, toKey), predicate); + } + + @Override public SortedMap<K, V> tailMap(K fromKey) { + return new FilteredSortedMap<K, V>( + sortedMap().tailMap(fromKey), predicate); + } + } +} diff --git a/guava/src/com/google/common/collect/SortedMultiset.java b/guava/src/com/google/common/collect/SortedMultiset.java index 9e14a80..0713151 100644 --- a/guava/src/com/google/common/collect/SortedMultiset.java +++ b/guava/src/com/google/common/collect/SortedMultiset.java @@ -22,7 +22,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; +import java.util.SortedSet; /** * A {@link Multiset} which maintains the ordering of its elements, according to @@ -36,16 +36,12 @@ import java.util.NavigableSet; * resulting multiset will violate the {@link Collection} contract, which it is * specified in terms of {@link Object#equals}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. - * * @author Louis Wasserman * @since 11.0 */ @Beta -@GwtCompatible(emulated = true) -public interface SortedMultiset<E> extends SortedMultisetBridge<E>, SortedIterable<E> { +@GwtCompatible +public interface SortedMultiset<E> extends Multiset<E>, SortedIterable<E> { /** * Returns the comparator that orders this multiset, or * {@link Ordering#natural()} if the natural ordering of the elements is used. @@ -77,11 +73,9 @@ public interface SortedMultiset<E> extends SortedMultisetBridge<E>, SortedIterab Entry<E> pollLastEntry(); /** - * Returns a {@link NavigableSet} view of the distinct elements in this multiset. - * - * @since 14.0 (present with return type {@code SortedSet} since 11.0) + * Returns a {@link SortedSet} view of the distinct elements in this multiset. */ - @Override NavigableSet<E> elementSet(); + @Override SortedSet<E> elementSet(); /** * {@inheritDoc} diff --git a/guava/src/com/google/common/collect/SortedMultisetBridge.java b/guava/src/com/google/common/collect/SortedMultisetBridge.java deleted file mode 100644 index 669b54d..0000000 --- a/guava/src/com/google/common/collect/SortedMultisetBridge.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.SortedSet; - -/** - * Superinterface of {@link SortedMultiset} to introduce a bridge method for - * {@code elementSet()}, to ensure binary compatibility with older Guava versions - * that specified {@code elementSet()} to return {@code SortedSet}. - * - * @author Louis Wasserman - */ -interface SortedMultisetBridge<E> extends Multiset<E> { - @Override - SortedSet<E> elementSet(); -} diff --git a/guava/src/com/google/common/collect/SortedMultisets.java b/guava/src/com/google/common/collect/SortedMultisets.java index 1055664..ff18b74 100644 --- a/guava/src/com/google/common/collect/SortedMultisets.java +++ b/guava/src/com/google/common/collect/SortedMultisets.java @@ -1,12 +1,12 @@ /* * 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 @@ -16,28 +16,22 @@ package com.google.common.collect; -import static com.google.common.collect.BoundType.CLOSED; -import static com.google.common.collect.BoundType.OPEN; - import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.Multiset.Entry; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; import java.util.NoSuchElementException; +import java.util.Set; import java.util.SortedSet; -import javax.annotation.Nullable; - /** * Provides static utility methods for creating and working with * {@link SortedMultiset} instances. - * + * * @author Louis Wasserman */ -@GwtCompatible(emulated = true) +@GwtCompatible final class SortedMultisets { private SortedMultisets() { } @@ -45,32 +39,26 @@ final class SortedMultisets { /** * A skeleton implementation for {@link SortedMultiset#elementSet}. */ - static class ElementSet<E> extends Multisets.ElementSet<E> implements + static abstract class ElementSet<E> extends Multisets.ElementSet<E> implements SortedSet<E> { - private final SortedMultiset<E> multiset; - - ElementSet(SortedMultiset<E> multiset) { - this.multiset = multiset; - } - - @Override final SortedMultiset<E> multiset() { - return multiset; - } + @Override abstract SortedMultiset<E> multiset(); @Override public Comparator<? super E> comparator() { return multiset().comparator(); } @Override public SortedSet<E> subSet(E fromElement, E toElement) { - return multiset().subMultiset(fromElement, CLOSED, toElement, OPEN).elementSet(); + return multiset().subMultiset(fromElement, BoundType.CLOSED, toElement, + BoundType.OPEN).elementSet(); } @Override public SortedSet<E> headSet(E toElement) { - return multiset().headMultiset(toElement, OPEN).elementSet(); + return multiset().headMultiset(toElement, BoundType.OPEN).elementSet(); } @Override public SortedSet<E> tailSet(E fromElement) { - return multiset().tailMultiset(fromElement, CLOSED).elementSet(); + return multiset().tailMultiset(fromElement, BoundType.CLOSED) + .elementSet(); } @Override public E first() { @@ -82,84 +70,127 @@ final class SortedMultisets { } } + private static <E> E getElementOrThrow(Entry<E> entry) { + if (entry == null) { + throw new NoSuchElementException(); + } + return entry.getElement(); + } + /** - * A skeleton navigable implementation for {@link SortedMultiset#elementSet}. + * A skeleton implementation of a descending multiset. Only needs + * {@code forwardMultiset()} and {@code entryIterator()}. */ - @GwtIncompatible("Navigable") - static class NavigableElementSet<E> extends ElementSet<E> implements NavigableSet<E> { - NavigableElementSet(SortedMultiset<E> multiset) { - super(multiset); + static abstract class DescendingMultiset<E> extends ForwardingMultiset<E> + implements SortedMultiset<E> { + abstract SortedMultiset<E> forwardMultiset(); + + private transient Comparator<? super E> comparator; + + @Override public Comparator<? super E> comparator() { + Comparator<? super E> result = comparator; + if (result == null) { + return comparator = + Ordering.from(forwardMultiset().comparator()).<E>reverse(); + } + return result; } - @Override - public E lower(E e) { - return getElementOrNull(multiset().headMultiset(e, OPEN).lastEntry()); + private transient SortedSet<E> elementSet; + + @Override public SortedSet<E> elementSet() { + SortedSet<E> result = elementSet; + if (result == null) { + return elementSet = new SortedMultisets.ElementSet<E>() { + @Override SortedMultiset<E> multiset() { + return DescendingMultiset.this; + } + }; + } + return result; } - @Override - public E floor(E e) { - return getElementOrNull(multiset().headMultiset(e, CLOSED).lastEntry()); + @Override public Entry<E> pollFirstEntry() { + return forwardMultiset().pollLastEntry(); } - @Override - public E ceiling(E e) { - return getElementOrNull(multiset().tailMultiset(e, CLOSED).firstEntry()); + @Override public Entry<E> pollLastEntry() { + return forwardMultiset().pollFirstEntry(); } - @Override - public E higher(E e) { - return getElementOrNull(multiset().tailMultiset(e, OPEN).firstEntry()); + @Override public SortedMultiset<E> headMultiset(E toElement, + BoundType boundType) { + return forwardMultiset().tailMultiset(toElement, boundType) + .descendingMultiset(); } - @Override - public NavigableSet<E> descendingSet() { - return new NavigableElementSet<E>(multiset().descendingMultiset()); + @Override public SortedMultiset<E> subMultiset(E fromElement, + BoundType fromBoundType, E toElement, BoundType toBoundType) { + return forwardMultiset().subMultiset(toElement, toBoundType, fromElement, + fromBoundType).descendingMultiset(); } - @Override - public Iterator<E> descendingIterator() { - return descendingSet().iterator(); + @Override public SortedMultiset<E> tailMultiset(E fromElement, + BoundType boundType) { + return forwardMultiset().headMultiset(fromElement, boundType) + .descendingMultiset(); } - @Override - public E pollFirst() { - return getElementOrNull(multiset().pollFirstEntry()); + @Override protected Multiset<E> delegate() { + return forwardMultiset(); } - @Override - public E pollLast() { - return getElementOrNull(multiset().pollLastEntry()); + @Override public SortedMultiset<E> descendingMultiset() { + return forwardMultiset(); } - @Override - public NavigableSet<E> subSet( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return new NavigableElementSet<E>(multiset().subMultiset( - fromElement, BoundType.forBoolean(fromInclusive), - toElement, BoundType.forBoolean(toInclusive))); + @Override public Entry<E> firstEntry() { + return forwardMultiset().lastEntry(); } - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return new NavigableElementSet<E>( - multiset().headMultiset(toElement, BoundType.forBoolean(inclusive))); + @Override public Entry<E> lastEntry() { + return forwardMultiset().firstEntry(); } - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return new NavigableElementSet<E>( - multiset().tailMultiset(fromElement, BoundType.forBoolean(inclusive))); + abstract Iterator<Entry<E>> entryIterator(); + + private transient Set<Entry<E>> entrySet; + + @Override public Set<Entry<E>> entrySet() { + Set<Entry<E>> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; } - } - private static <E> E getElementOrThrow(Entry<E> entry) { - if (entry == null) { - throw new NoSuchElementException(); + Set<Entry<E>> createEntrySet() { + return new Multisets.EntrySet<E>() { + @Override Multiset<E> multiset() { + return DescendingMultiset.this; + } + + @Override public Iterator<Entry<E>> iterator() { + return entryIterator(); + } + + @Override public int size() { + return forwardMultiset().entrySet().size(); + } + }; + } + + @Override public Iterator<E> iterator() { + return Multisets.iteratorImpl(this); + } + + @Override public Object[] toArray() { + return standardToArray(); + } + + @Override public <T> T[] toArray(T[] array) { + return standardToArray(array); } - return entry.getElement(); - } - private static <E> E getElementOrNull(@Nullable Entry<E> entry) { - return (entry == null) ? null : entry.getElement(); + @Override public String toString() { + return entrySet().toString(); + } } } diff --git a/guava/src/com/google/common/collect/SortedSetMultimap.java b/guava/src/com/google/common/collect/SortedSetMultimap.java index 1b9c641..4785de3 100644 --- a/guava/src/com/google/common/collect/SortedSetMultimap.java +++ b/guava/src/com/google/common/collect/SortedSetMultimap.java @@ -31,18 +31,13 @@ import javax.annotation.Nullable; * that is, they comprise a {@link SortedSet}. It cannot hold duplicate * key-value pairs; adding a key-value pair that's already in the multimap has * no effect. This interface does not specify the ordering of the multimap's - * keys. See the {@link Multimap} documentation for information common to all - * multimaps. + * keys. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link SortedSet} of values, while {@link Multimap#entries()} * returns a {@link Set} of map entries. Though the method signature doesn't say * so explicitly, the map returned by {@link #asMap} has {@code SortedSet} * values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. * * @author Jared Levy * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/StandardTable.java b/guava/src/com/google/common/collect/StandardTable.java index 22c0a90..5edee5b 100644 --- a/guava/src/com/google/common/collect/StandardTable.java +++ b/guava/src/com/google/common/collect/StandardTable.java @@ -391,13 +391,17 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { @Override public V remove(Object key) { - Map<C, V> backingRowMap = backingRowMap(); - if (backingRowMap == null) { + try { + Map<C, V> backingRowMap = backingRowMap(); + if (backingRowMap == null) { + return null; + } + V result = backingRowMap.remove(key); + maintainEmptyInvariant(); + return result; + } catch (ClassCastException e) { return null; } - V result = Maps.safeRemove(backingRowMap, key); - maintainEmptyInvariant(); - return result; } @Override @@ -555,7 +559,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return changed; } - class EntrySet extends Sets.ImprovedAbstractSet<Entry<R, V>> { + class EntrySet extends AbstractSet<Entry<R, V>> { @Override public Iterator<Entry<R, V>> iterator() { return new EntrySetIterator(); } @@ -595,6 +599,14 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return false; } + @Override public boolean removeAll(Collection<?> c) { + boolean changed = false; + for (Object obj : c) { + changed |= remove(obj); + } + return changed; + } + @Override public boolean retainAll(Collection<?> c) { return removePredicate(Predicates.not(Predicates.in(c))); } @@ -631,9 +643,9 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return result == null ? keySet = new KeySet() : result; } - class KeySet extends Sets.ImprovedAbstractSet<R> { + class KeySet extends AbstractSet<R> { @Override public Iterator<R> iterator() { - return Maps.keyIterator(Column.this.entrySet().iterator()); + return keyIteratorImpl(Column.this); } @Override public int size() { @@ -656,6 +668,14 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { entrySet().clear(); } + @Override public boolean removeAll(final Collection<?> c) { + boolean changed = false; + for (Object obj : c) { + changed |= remove(obj); + } + return changed; + } + @Override public boolean retainAll(final Collection<?> c) { checkNotNull(c); Predicate<Entry<R, V>> predicate = new Predicate<Entry<R, V>>() { @@ -670,7 +690,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class Values extends AbstractCollection<V> { @Override public Iterator<V> iterator() { - return Maps.valueIterator(Column.this.entrySet().iterator()); + return valueIteratorImpl(Column.this); } @Override public int size() { @@ -736,7 +756,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class RowKeySet extends TableSet<R> { @Override public Iterator<R> iterator() { - return Maps.keyIterator(rowMap().entrySet().iterator()); + return keyIteratorImpl(rowMap()); } @Override public int size() { @@ -890,10 +910,16 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { private class Values extends TableCollection<V> { @Override public Iterator<V> iterator() { - return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) { - @Override - V transform(Cell<R, C, V> cell) { - return cell.getValue(); + final Iterator<Cell<R, C, V>> cellIterator = cellSet().iterator(); + return new Iterator<V>() { + @Override public boolean hasNext() { + return cellIterator.hasNext(); + } + @Override public V next() { + return cellIterator.next().getValue(); + } + @Override public void remove() { + cellIterator.remove(); } }; } @@ -935,13 +961,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class EntrySet extends TableSet<Entry<R, Map<C, V>>> { @Override public Iterator<Entry<R, Map<C, V>>> iterator() { - return new TransformedIterator<R, Entry<R, Map<C, V>>>( - backingMap.keySet().iterator()) { - @Override - Entry<R, Map<C, V>> transform(R rowKey) { - return new ImmutableEntry<R, Map<C, V>>(rowKey, row(rowKey)); - } - }; + return new EntryIterator(); } @Override public int size() { @@ -968,6 +988,23 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return false; } } + + class EntryIterator implements Iterator<Entry<R, Map<C, V>>> { + final Iterator<R> delegate = backingMap.keySet().iterator(); + + @Override public boolean hasNext() { + return delegate.hasNext(); + } + + @Override public Entry<R, Map<C, V>> next() { + R rowKey = delegate.next(); + return new ImmutableEntry<R, Map<C, V>>(rowKey, row(rowKey)); + } + + @Override public void remove() { + delegate.remove(); + } + } } private transient ColumnMap columnMap; @@ -1011,10 +1048,13 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class ColumnMapEntrySet extends TableSet<Entry<C, Map<R, V>>> { @Override public Iterator<Entry<C, Map<R, V>>> iterator() { - return new TransformedIterator<C, Entry<C, Map<R, V>>>( - columnKeySet().iterator()) { - @Override - Entry<C, Map<R, V>> transform(C columnKey) { + final Iterator<C> columnIterator = columnKeySet().iterator(); + return new UnmodifiableIterator<Entry<C, Map<R, V>>>() { + @Override public boolean hasNext() { + return columnIterator.hasNext(); + } + @Override public Entry<C, Map<R, V>> next() { + C columnKey = columnIterator.next(); return new ImmutableEntry<C, Map<R, V>>( columnKey, column(columnKey)); } @@ -1071,7 +1111,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { private class ColumnMapValues extends TableCollection<Map<R, V>> { @Override public Iterator<Map<R, V>> iterator() { - return Maps.valueIterator(ColumnMap.this.entrySet().iterator()); + return valueIteratorImpl(ColumnMap.this); } @Override public boolean remove(Object obj) { @@ -1115,4 +1155,44 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { } private static final long serialVersionUID = 0; + + // TODO(kevinb): Move keyIteratorImpl and valueIteratorImpl to Maps, reuse + + /** + * Generates the iterator of a map's key set from the map's entry set + * iterator. + */ + static <K, V> Iterator<K> keyIteratorImpl(Map<K, V> map) { + final Iterator<Entry<K, V>> entryIterator = map.entrySet().iterator(); + return new Iterator<K>() { + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override public K next() { + return entryIterator.next().getKey(); + } + @Override public void remove() { + entryIterator.remove(); + } + }; + } + + /** + * Generates the iterator of a map's value collection from the map's entry set + * iterator. + */ + static <K, V> Iterator<V> valueIteratorImpl(Map<K, V> map) { + final Iterator<Entry<K, V>> entryIterator = map.entrySet().iterator(); + return new Iterator<V>() { + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override public V next() { + return entryIterator.next().getValue(); + } + @Override public void remove() { + entryIterator.remove(); + } + }; + } } diff --git a/guava/src/com/google/common/collect/Synchronized.java b/guava/src/com/google/common/collect/Synchronized.java index bbf4c1e..c021c55 100644 --- a/guava/src/com/google/common/collect/Synchronized.java +++ b/guava/src/com/google/common/collect/Synchronized.java @@ -31,10 +31,6 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.Queue; import java.util.RandomAccess; import java.util.Set; import java.util.SortedMap; @@ -211,7 +207,7 @@ final class Synchronized { static class SynchronizedSet<E> extends SynchronizedCollection<E> implements Set<E> { - + SynchronizedSet(Set<E> delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -851,7 +847,7 @@ final class Synchronized { } @Override public Map.Entry<K, Collection<V>> next() { - final Map.Entry<K, Collection<V>> entry = super.next(); + final Map.Entry<K, Collection<V>> entry = iterator.next(); return new ForwardingMapEntry<K, Collection<V>>() { @Override protected Map.Entry<K, Collection<V>> delegate() { return entry; @@ -1043,12 +1039,12 @@ final class Synchronized { private static final long serialVersionUID = 0; } - + static <K, V> SortedMap<K, V> sortedMap( SortedMap<K, V> sortedMap, @Nullable Object mutex) { return new SynchronizedSortedMap<K, V>(sortedMap, mutex); } - + static class SynchronizedSortedMap<K, V> extends SynchronizedMap<K, V> implements SortedMap<K, V> { @@ -1212,410 +1208,11 @@ final class Synchronized { return iterator; } @Override public Collection<V> next() { - return typePreservingCollection(super.next(), mutex); + return typePreservingCollection(iterator.next(), mutex); } }; } private static final long serialVersionUID = 0; } - - @GwtIncompatible("NavigableSet") - @VisibleForTesting - static class SynchronizedNavigableSet<E> extends SynchronizedSortedSet<E> - implements NavigableSet<E> { - SynchronizedNavigableSet(NavigableSet<E> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @Override NavigableSet<E> delegate() { - return (NavigableSet<E>) super.delegate(); - } - - @Override public E ceiling(E e) { - synchronized (mutex) { - return delegate().ceiling(e); - } - } - - @Override public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); // manually synchronized - } - - transient NavigableSet<E> descendingSet; - - @Override public NavigableSet<E> descendingSet() { - synchronized (mutex) { - if (descendingSet == null) { - NavigableSet<E> dS = - Synchronized.navigableSet(delegate().descendingSet(), mutex); - descendingSet = dS; - return dS; - } - return descendingSet; - } - } - - @Override public E floor(E e) { - synchronized (mutex) { - return delegate().floor(e); - } - } - - @Override public NavigableSet<E> headSet(E toElement, boolean inclusive) { - synchronized (mutex) { - return Synchronized.navigableSet( - delegate().headSet(toElement, inclusive), mutex); - } - } - - @Override public E higher(E e) { - synchronized (mutex) { - return delegate().higher(e); - } - } - - @Override public E lower(E e) { - synchronized (mutex) { - return delegate().lower(e); - } - } - - @Override public E pollFirst() { - synchronized (mutex) { - return delegate().pollFirst(); - } - } - - @Override public E pollLast() { - synchronized (mutex) { - return delegate().pollLast(); - } - } - - @Override public NavigableSet<E> subSet(E fromElement, - boolean fromInclusive, E toElement, boolean toInclusive) { - synchronized (mutex) { - return Synchronized.navigableSet(delegate().subSet( - fromElement, fromInclusive, toElement, toInclusive), mutex); - } - } - - @Override public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - synchronized (mutex) { - return Synchronized.navigableSet( - delegate().tailSet(fromElement, inclusive), mutex); - } - } - - @Override public SortedSet<E> headSet(E toElement) { - return headSet(toElement, false); - } - - @Override public SortedSet<E> subSet(E fromElement, E toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override public SortedSet<E> tailSet(E fromElement) { - return tailSet(fromElement, true); - } - - private static final long serialVersionUID = 0; - } - - @GwtIncompatible("NavigableSet") - static <E> NavigableSet<E> navigableSet( - NavigableSet<E> navigableSet, @Nullable Object mutex) { - return new SynchronizedNavigableSet<E>(navigableSet, mutex); - } - - @GwtIncompatible("NavigableSet") - static <E> NavigableSet<E> navigableSet(NavigableSet<E> navigableSet) { - return navigableSet(navigableSet, null); - } - - @GwtIncompatible("NavigableMap") - static <K, V> NavigableMap<K, V> navigableMap( - NavigableMap<K, V> navigableMap) { - return navigableMap(navigableMap, null); - } - - @GwtIncompatible("NavigableMap") - static <K, V> NavigableMap<K, V> navigableMap( - NavigableMap<K, V> navigableMap, @Nullable Object mutex) { - return new SynchronizedNavigableMap<K, V>(navigableMap, mutex); - } - - @GwtIncompatible("NavigableMap") - @VisibleForTesting static class SynchronizedNavigableMap<K, V> - extends SynchronizedSortedMap<K, V> implements NavigableMap<K, V> { - - SynchronizedNavigableMap( - NavigableMap<K, V> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @Override NavigableMap<K, V> delegate() { - return (NavigableMap<K, V>) super.delegate(); - } - - @Override public Entry<K, V> ceilingEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().ceilingEntry(key), mutex); - } - } - - @Override public K ceilingKey(K key) { - synchronized (mutex) { - return delegate().ceilingKey(key); - } - } - - transient NavigableSet<K> descendingKeySet; - - @Override public NavigableSet<K> descendingKeySet() { - synchronized (mutex) { - if (descendingKeySet == null) { - return descendingKeySet = - Synchronized.navigableSet(delegate().descendingKeySet(), mutex); - } - return descendingKeySet; - } - } - - transient NavigableMap<K, V> descendingMap; - - @Override public NavigableMap<K, V> descendingMap() { - synchronized (mutex) { - if (descendingMap == null) { - return descendingMap = - navigableMap(delegate().descendingMap(), mutex); - } - return descendingMap; - } - } - - @Override public Entry<K, V> firstEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().firstEntry(), mutex); - } - } - - @Override public Entry<K, V> floorEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().floorEntry(key), mutex); - } - } - - @Override public K floorKey(K key) { - synchronized (mutex) { - return delegate().floorKey(key); - } - } - - @Override public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - synchronized (mutex) { - return navigableMap( - delegate().headMap(toKey, inclusive), mutex); - } - } - - @Override public Entry<K, V> higherEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().higherEntry(key), mutex); - } - } - - @Override public K higherKey(K key) { - synchronized (mutex) { - return delegate().higherKey(key); - } - } - - @Override public Entry<K, V> lastEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().lastEntry(), mutex); - } - } - - @Override public Entry<K, V> lowerEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().lowerEntry(key), mutex); - } - } - - @Override public K lowerKey(K key) { - synchronized (mutex) { - return delegate().lowerKey(key); - } - } - - @Override public Set<K> keySet() { - return navigableKeySet(); - } - - transient NavigableSet<K> navigableKeySet; - - @Override public NavigableSet<K> navigableKeySet() { - synchronized (mutex) { - if (navigableKeySet == null) { - return navigableKeySet = - Synchronized.navigableSet(delegate().navigableKeySet(), mutex); - } - return navigableKeySet; - } - } - - @Override public Entry<K, V> pollFirstEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().pollFirstEntry(), mutex); - } - } - - @Override public Entry<K, V> pollLastEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().pollLastEntry(), mutex); - } - } - - @Override public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - synchronized (mutex) { - return navigableMap( - delegate().subMap(fromKey, fromInclusive, toKey, toInclusive), - mutex); - } - } - - @Override public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - synchronized (mutex) { - return navigableMap( - delegate().tailMap(fromKey, inclusive), mutex); - } - } - - @Override public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - private static final long serialVersionUID = 0; - } - - @GwtIncompatible("works but is needed only for NavigableMap") - private static <K, V> Entry<K, V> nullableSynchronizedEntry( - @Nullable Entry<K, V> entry, @Nullable Object mutex) { - if (entry == null) { - return null; - } - return new SynchronizedEntry<K, V>(entry, mutex); - } - - @GwtIncompatible("works but is needed only for NavigableMap") - private static class SynchronizedEntry<K, V> extends SynchronizedObject - implements Entry<K, V> { - - SynchronizedEntry(Entry<K, V> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @SuppressWarnings("unchecked") // guaranteed by the constructor - @Override Entry<K, V> delegate() { - return (Entry<K, V>) super.delegate(); - } - - @Override public boolean equals(Object obj) { - synchronized (mutex) { - return delegate().equals(obj); - } - } - - @Override public int hashCode() { - synchronized (mutex) { - return delegate().hashCode(); - } - } - - @Override public K getKey() { - synchronized (mutex) { - return delegate().getKey(); - } - } - - @Override public V getValue() { - synchronized (mutex) { - return delegate().getValue(); - } - } - - @Override public V setValue(V value) { - synchronized (mutex) { - return delegate().setValue(value); - } - } - - private static final long serialVersionUID = 0; - } - - static <E> Queue<E> queue(Queue<E> queue, @Nullable Object mutex) { - return (queue instanceof SynchronizedQueue) - ? queue - : new SynchronizedQueue<E>(queue, mutex); - } - - private static class SynchronizedQueue<E> extends SynchronizedCollection<E> - implements Queue<E> { - - SynchronizedQueue(Queue<E> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @Override Queue<E> delegate() { - return (Queue<E>) super.delegate(); - } - - @Override - public E element() { - synchronized (mutex) { - return delegate().element(); - } - } - - @Override - public boolean offer(E e) { - synchronized (mutex) { - return delegate().offer(e); - } - } - - @Override - public E peek() { - synchronized (mutex) { - return delegate().peek(); - } - } - - @Override - public E poll() { - synchronized (mutex) { - return delegate().poll(); - } - } - - @Override - public E remove() { - synchronized (mutex) { - return delegate().remove(); - } - } - - private static final long serialVersionUID = 0; - } } diff --git a/guava/src/com/google/common/collect/Table.java b/guava/src/com/google/common/collect/Table.java index c384bf8..f2313be 100644 --- a/guava/src/com/google/common/collect/Table.java +++ b/guava/src/com/google/common/collect/Table.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; @@ -43,10 +44,6 @@ import javax.annotation.Nullable; * <p>All methods that modify the table are optional, and the views returned by * the table may or may not be modifiable. When modification isn't supported, * those methods will throw an {@link UnsupportedOperationException}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table"> - * {@code Table}</a>. * * @author Jared Levy * @param <R> the type of the table row keys @@ -55,6 +52,7 @@ import javax.annotation.Nullable; * @since 7.0 */ @GwtCompatible +@Beta public interface Table<R, C, V> { // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. @@ -261,6 +259,7 @@ public interface Table<R, C, V> { * * @since 7.0 */ + @Beta interface Cell<R, C, V> { /** * Returns the row key of this cell. diff --git a/guava/src/com/google/common/collect/Tables.java b/guava/src/com/google/common/collect/Tables.java index 028aba3..a9c69b9 100644 --- a/guava/src/com/google/common/collect/Tables.java +++ b/guava/src/com/google/common/collect/Tables.java @@ -39,16 +39,13 @@ import javax.annotation.Nullable; /** * Provides static methods that involve a {@code Table}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Tables"> - * {@code Tables}</a>. * * @author Jared Levy * @author Louis Wasserman * @since 7.0 */ @GwtCompatible +@Beta public final class Tables { private Tables() {} @@ -364,7 +361,6 @@ public final class Tables { * @throws IllegalArgumentException if {@code backingMap} is not empty * @since 10.0 */ - @Beta public static <R, C, V> Table<R, C, V> newCustomTable( Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { checkArgument(backingMap.isEmpty()); @@ -399,7 +395,6 @@ public final class Tables { * * @since 10.0 */ - @Beta public static <R, C, V1, V2> Table<R, C, V2> transformValues( Table<R, C, V1> fromTable, Function<? super V1, V2> function) { return new TransformedTable<R, C, V1, V2>(fromTable, function); @@ -701,7 +696,6 @@ public final class Tables { * @return an unmodifiable view of the specified table * @since 11.0 */ - @Beta public static <R, C, V> RowSortedTable<R, C, V> unmodifiableRowSortedTable( RowSortedTable<R, ? extends C, ? extends V> table) { /* diff --git a/guava/src/com/google/common/collect/TransformedImmutableList.java b/guava/src/com/google/common/collect/TransformedImmutableList.java new file mode 100644 index 0000000..d9e8588 --- /dev/null +++ b/guava/src/com/google/common/collect/TransformedImmutableList.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010 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 com.google.common.annotations.GwtCompatible; + +import java.util.List; + +import javax.annotation.Nullable; + +/** + * A transforming wrapper around an ImmutableList. For internal use only. {@link + * #transform(Object)} must be functional. + * + * @author Louis Wasserman + */ +@GwtCompatible +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +abstract class TransformedImmutableList<D, E> extends ImmutableList<E> { + private class TransformedView extends TransformedImmutableList<D, E> { + TransformedView(ImmutableList<D> backingList) { + super(backingList); + } + + @Override E transform(D d) { + return TransformedImmutableList.this.transform(d); + } + } + + private transient final ImmutableList<D> backingList; + + TransformedImmutableList(ImmutableList<D> backingList) { + this.backingList = checkNotNull(backingList); + } + + abstract E transform(D d); + + @Override public int indexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + for (int i = 0; i < size(); i++) { + if (get(i).equals(object)) { + return i; + } + } + return -1; + } + + @Override public int lastIndexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + for (int i = size() - 1; i >= 0; i--) { + if (get(i).equals(object)) { + return i; + } + } + return -1; + } + + @Override public E get(int index) { + return transform(backingList.get(index)); + } + + @Override public UnmodifiableListIterator<E> listIterator(int index) { + return new AbstractIndexedListIterator<E>(size(), index) { + @Override protected E get(int index) { + return TransformedImmutableList.this.get(index); + } + }; + } + + @Override public int size() { + return backingList.size(); + } + + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + return new TransformedView(backingList.subList(fromIndex, toIndex)); + } + + @Override public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof List) { + List<?> list = (List<?>) obj; + return size() == list.size() + && Iterators.elementsEqual(iterator(), list.iterator()); + } + return false; + } + + @Override public int hashCode() { + int hashCode = 1; + for (E e : this) { + hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); + } + return hashCode; + } + + @Override public Object[] toArray() { + return ObjectArrays.toArrayImpl(this); + } + + @Override public <T> T[] toArray(T[] array) { + return ObjectArrays.toArrayImpl(this, array); + } + + @Override boolean isPartialView() { + return true; + } +} diff --git a/guava/src/com/google/common/collect/TransformedIterator.java b/guava/src/com/google/common/collect/TransformedIterator.java deleted file mode 100644 index c082d7d..0000000 --- a/guava/src/com/google/common/collect/TransformedIterator.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; - -import java.util.Iterator; - -/** - * An iterator that transforms a backing iterator; for internal use. This avoids - * the object overhead of constructing a {@link Function} for internal methods. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class TransformedIterator<F, T> implements Iterator<T> { - final Iterator<? extends F> backingIterator; - - TransformedIterator(Iterator<? extends F> backingIterator) { - this.backingIterator = checkNotNull(backingIterator); - } - - abstract T transform(F from); - - @Override - public final boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override - public final T next() { - return transform(backingIterator.next()); - } - - @Override - public final void remove() { - backingIterator.remove(); - } -} diff --git a/guava/src/com/google/common/collect/TransformedListIterator.java b/guava/src/com/google/common/collect/TransformedListIterator.java deleted file mode 100644 index c743030..0000000 --- a/guava/src/com/google/common/collect/TransformedListIterator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; -import com.google.common.base.Function; - -import java.util.ListIterator; - -/** - * An iterator that transforms a backing list iterator; for internal use. This - * avoids the object overhead of constructing a {@link Function} for internal - * methods. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class TransformedListIterator<F, T> extends TransformedIterator<F, T> - implements ListIterator<T> { - TransformedListIterator(ListIterator<? extends F> backingIterator) { - super(backingIterator); - } - - private ListIterator<? extends F> backingIterator() { - return Iterators.cast(backingIterator); - } - - @Override - public final boolean hasPrevious() { - return backingIterator().hasPrevious(); - } - - @Override - public final T previous() { - return transform(backingIterator().previous()); - } - - @Override - public final int nextIndex() { - return backingIterator().nextIndex(); - } - - @Override - public final int previousIndex() { - return backingIterator().previousIndex(); - } - - @Override - public void set(T element) { - throw new UnsupportedOperationException(); - } - - @Override - public void add(T element) { - throw new UnsupportedOperationException(); - } -} diff --git a/guava/src/com/google/common/collect/TreeBasedTable.java b/guava/src/com/google/common/collect/TreeBasedTable.java index 2ead64e..d996c27 100644 --- a/guava/src/com/google/common/collect/TreeBasedTable.java +++ b/guava/src/com/google/common/collect/TreeBasedTable.java @@ -66,10 +66,6 @@ import javax.annotation.Nullable; * access this table concurrently and one of the threads modifies the table, it * must be synchronized externally. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table"> - * {@code Table}</a>. - * * @author Jared Levy * @author Louis Wasserman * @since 7.0 diff --git a/guava/src/com/google/common/collect/TreeMultimap.java b/guava/src/com/google/common/collect/TreeMultimap.java index 433774d..efd11be 100644 --- a/guava/src/com/google/common/collect/TreeMultimap.java +++ b/guava/src/com/google/common/collect/TreeMultimap.java @@ -26,14 +26,11 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collection; import java.util.Comparator; -import java.util.NavigableMap; -import java.util.NavigableSet; +import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; -import javax.annotation.Nullable; - /** * Implementation of {@code Multimap} whose keys and values are ordered by * their natural ordering or by supplied comparators. In all cases, this @@ -67,16 +64,11 @@ import javax.annotation.Nullable; * update operations, wrap your multimap with a call to {@link * Multimaps#synchronizedSortedSetMultimap}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. - * * @author Jared Levy - * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(serializable = true, emulated = true) -public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<K, V> { +public class TreeMultimap<K, V> extends AbstractSortedSetMultimap<K, V> { private transient Comparator<? super K> keyComparator; private transient Comparator<? super V> valueComparator; @@ -142,14 +134,6 @@ public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<K, V> return new TreeSet<V>(valueComparator); } - @Override - Collection<V> createCollection(@Nullable K key) { - if (key == null) { - keyComparator().compare(key, key); - } - return super.createCollection(key); - } - /** * Returns the comparator that orders the multimap keys. */ @@ -162,79 +146,26 @@ public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<K, V> return valueComparator; } - /* - * The following @GwtIncompatible methods override the methods in - * AbstractSortedKeySortedSetMultimap, so GWT will fall back to the ASKSSM implementations, - * which return SortedSets and SortedMaps. - */ - - @Override - @GwtIncompatible("NavigableMap") - NavigableMap<K, Collection<V>> backingMap() { - return (NavigableMap<K, Collection<V>>) super.backingMap(); - } - - /** - * @since 14.0 (present with return type {@code SortedSet} since 2.0) - */ - @Override - @GwtIncompatible("NavigableSet") - public NavigableSet<V> get(@Nullable K key) { - return (NavigableSet<V>) super.get(key); - } - - @Override - @GwtIncompatible("NavigableSet") - Collection<V> unmodifiableCollectionSubclass(Collection<V> collection) { - return Sets.unmodifiableNavigableSet((NavigableSet<V>) collection); - } - - @Override - @GwtIncompatible("NavigableSet") - Collection<V> wrapCollection(K key, Collection<V> collection) { - return new WrappedNavigableSet(key, (NavigableSet<V>) collection, null); - } - /** * {@inheritDoc} * * <p>Because a {@code TreeMultimap} has unique sorted keys, this method - * returns a {@link NavigableSet}, instead of the {@link java.util.Set} specified + * returns a {@link SortedSet}, instead of the {@link java.util.Set} specified * in the {@link Multimap} interface. - * - * @since 14.0 (present with return type {@code SortedSet} since 2.0) */ - @Override - @GwtIncompatible("NavigableSet") - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - @GwtIncompatible("NavigableSet") - NavigableSet<K> createKeySet() { - return new NavigableKeySet(backingMap()); + @Override public SortedSet<K> keySet() { + return (SortedSet<K>) super.keySet(); } /** * {@inheritDoc} * * <p>Because a {@code TreeMultimap} has unique sorted keys, this method - * returns a {@link NavigableMap}, instead of the {@link java.util.Map} specified + * returns a {@link SortedMap}, instead of the {@link java.util.Map} specified * in the {@link Multimap} interface. - * - * @since 14.0 (present with return type {@code SortedMap} since 2.0) */ - @Override - @GwtIncompatible("NavigableMap") - public NavigableMap<K, Collection<V>> asMap() { - return (NavigableMap<K, Collection<V>>) super.asMap(); - } - - @Override - @GwtIncompatible("NavigableMap") - NavigableMap<K, Collection<V>> createAsMap() { - return new NavigableAsMap(backingMap()); + @Override public SortedMap<K, Collection<V>> asMap() { + return (SortedMap<K, Collection<V>>) super.asMap(); } /** diff --git a/guava/src/com/google/common/collect/TreeMultiset.java b/guava/src/com/google/common/collect/TreeMultiset.java index 6876cd0..d44a4bb 100644 --- a/guava/src/com/google/common/collect/TreeMultiset.java +++ b/guava/src/com/google/common/collect/TreeMultiset.java @@ -17,12 +17,10 @@ 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 com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Objects; -import com.google.common.primitives.Ints; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; import java.io.IOException; import java.io.ObjectInputStream; @@ -31,207 +29,160 @@ import java.io.Serializable; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; -import java.util.NoSuchElementException; import javax.annotation.Nullable; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.primitives.Ints; + /** - * 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. + * 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= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. + * <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}. * * @author Louis Wasserman * @author Jared Levy * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(emulated = true) -public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements Serializable { +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 {@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}. + * 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. + * <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 <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}. + * 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. + * @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) { + public static <E> TreeMultiset<E> create( + @Nullable Comparator<? super E> comparator) { return (comparator == null) - ? new TreeMultiset<E>((Comparator) Ordering.natural()) - : new TreeMultiset<E>(comparator); + ? 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. + * 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>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. + * <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) { + 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; + /** + * Returns an iterator over the elements contained in this collection. + */ + @Override + public Iterator<E> iterator() { + // Needed to avoid Javadoc bug. + return super.iterator(); } - TreeMultiset(Comparator<? super E> comparator) { + private 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>>(); + this.rootReference = new Reference<Node<E>>(); } - /** - * A function which can be summed across a subtree. - */ - private enum Aggregate { - SIZE { - @Override - int nodeAggregate(AvlNode<?> node) { - return node.elemCount; - } + private TreeMultiset(GeneralRange<E> range, Reference<Node<E>> root) { + super(range.comparator()); + this.range = range; + this.rootReference = root; + } - @Override - long treeAggregate(@Nullable AvlNode<?> root) { - return (root == null) ? 0 : root.totalCount; - } - }, - DISTINCT { - @Override - int nodeAggregate(AvlNode<?> node) { - return 1; - } + @SuppressWarnings("unchecked") + E checkElement(Object o) { + return (E) o; + } - @Override - long treeAggregate(@Nullable AvlNode<?> root) { - return (root == null) ? 0 : root.distinctElements; - } - }; - abstract int nodeAggregate(AvlNode<?> node); + private transient final GeneralRange<E> range; - abstract long treeAggregate(@Nullable AvlNode<?> root); - } + private transient final Reference<Node<E>> rootReference; - 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; - } + static final class Reference<T> { + T value; - 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); - } - } + public Reference() {} - private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode<E> node) { - if (node == null) { - return 0; + public T get() { + return value; } - 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(); + + public boolean compareAndSet(T expected, T newValue) { + if (value == expected) { + value = newValue; + return true; } - } else { - return aggr.treeAggregate(node.right) + aggr.nodeAggregate(node) - + aggregateAboveRange(aggr, node.left); + return false; } } @Override - public int size() { - return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE)); + int distinctElements() { + Node<E> root = rootReference.get(); + return Ints.checkedCast(BstRangeOps.totalInRange(distinctAggregate(), range, root)); } @Override - int distinctElements() { - return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT)); + public int size() { + Node<E> root = rootReference.get(); + return Ints.saturatedCast(BstRangeOps.totalInRange(sizeAggregate(), range, root)); } @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; + E e = checkElement(element); + if (range.contains(e)) { + Node<E> node = BstOperations.seek(comparator(), rootReference.get(), e); + return countOrZero(node); } - return root.count(comparator(), e); + return 0; } catch (ClassCastException e) { return 0; } catch (NullPointerException e) { @@ -239,718 +190,360 @@ public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements } } + private int mutate(@Nullable E e, MultisetModifier modifier) { + BstMutationRule<E, Node<E>> mutationRule = BstMutationRule.createRule( + modifier, + BstCountBasedBalancePolicies. + <E, Node<E>>singleRebalancePolicy(distinctAggregate()), + nodeFactory()); + BstMutationResult<E, Node<E>> mutationResult = + BstOperations.mutate(comparator(), mutationRule, rootReference.get(), e); + if (!rootReference.compareAndSet( + mutationResult.getOriginalRoot(), mutationResult.getChangedRoot())) { + throw new ConcurrentModificationException(); + } + Node<E> original = mutationResult.getOriginalTarget(); + return countOrZero(original); + } + @Override - public int add(@Nullable E element, int occurrences) { - checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences); + public int add(E element, int occurrences) { + checkElement(element); 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]; + return mutate(element, new AddModifier(occurrences)); } @Override public int remove(@Nullable Object element, int occurrences) { - checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences); - if (occurrences == 0) { + if (element == null) { + return 0; + } else 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); + E e = checkElement(element); + return range.contains(e) ? mutate(e, new RemoveModifier(occurrences)) : 0; } catch (ClassCastException e) { return 0; - } catch (NullPointerException e) { - return 0; } - rootReference.checkAndSet(root, newRoot); - return result[0]; } @Override - public int setCount(@Nullable E element, int count) { - checkArgument(count >= 0); - 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]; + public boolean setCount(E element, int oldCount, int newCount) { + checkElement(element); + checkArgument(range.contains(element)); + return mutate(element, new ConditionalSetCountModifier(oldCount, newCount)) + == oldCount; } @Override - public boolean setCount(@Nullable E element, int oldCount, int newCount) { - checkArgument(newCount >= 0); - checkArgument(oldCount >= 0); + public int setCount(E element, int count) { + checkElement(element); 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; + return mutate(element, new SetCountModifier(count)); } - 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; - } - } - }; + private BstPathFactory<Node<E>, BstInOrderPath<Node<E>>> pathFactory() { + return BstInOrderPath.inOrderFactory(); } - /** - * Returns the first node in the tree that is in range. - */ - @Nullable private 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; + @Override + Iterator<Entry<E>> entryIterator() { + Node<E> root = rootReference.get(); + final BstInOrderPath<Node<E>> startingPath = + BstRangeOps.furthestPath(range, LEFT, pathFactory(), root); + return iteratorInDirection(startingPath, RIGHT); } - @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; + @Override + Iterator<Entry<E>> descendingEntryIterator() { + Node<E> root = rootReference.get(); + final BstInOrderPath<Node<E>> startingPath = + BstRangeOps.furthestPath(range, RIGHT, pathFactory(), root); + return iteratorInDirection(startingPath, LEFT); } - @Override - Iterator<Entry<E>> entryIterator() { + private Iterator<Entry<E>> iteratorInDirection( + @Nullable BstInOrderPath<Node<E>> start, final BstSide direction) { + final Iterator<BstInOrderPath<Node<E>>> pathIterator = + new AbstractLinkedIterator<BstInOrderPath<Node<E>>>(start) { + @Override + protected BstInOrderPath<Node<E>> computeNext(BstInOrderPath<Node<E>> previous) { + if (!previous.hasNext(direction)) { + return null; + } + BstInOrderPath<Node<E>> next = previous.next(direction); + // TODO(user): only check against one side + return range.contains(next.getTip().getKey()) ? next : null; + } + }; return new Iterator<Entry<E>>() { - AvlNode<E> current = firstNode(); - Entry<E> prevEntry; + E toRemove = null; @Override public boolean hasNext() { - if (current == null) { - return false; - } else if (range.tooHigh(current.getElement())) { - current = null; - return false; - } else { - return true; - } + return pathIterator.hasNext(); } @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; + BstInOrderPath<Node<E>> path = pathIterator.next(); + return new LiveEntry( + toRemove = path.getTip().getKey(), path.getTip().elemCount()); } @Override public void remove() { - checkState(prevEntry != null); - setCount(prevEntry.getElement(), 0); - prevEntry = null; + checkState(toRemove != null); + setCount(toRemove, 0); + toRemove = null; } }; } - @Override - Iterator<Entry<E>> descendingEntryIterator() { - return new Iterator<Entry<E>>() { - AvlNode<E> current = lastNode(); - Entry<E> prevEntry = null; + class LiveEntry extends Multisets.AbstractEntry<E> { + private Node<E> expectedRoot; + private final E element; + private int count; - @Override - public boolean hasNext() { - if (current == null) { - return false; - } else if (range.tooLow(current.getElement())) { - current = null; - return false; - } else { - return true; - } - } + private LiveEntry(E element, int count) { + this.expectedRoot = rootReference.get(); + this.element = element; + this.count = count; + } - @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 E getElement() { + return element; + } - @Override - public void remove() { - checkState(prevEntry != null); - setCount(prevEntry.getElement(), 0); - prevEntry = null; + @Override + public int getCount() { + if (rootReference.get() == expectedRoot) { + return count; + } else { + // check for updates + expectedRoot = rootReference.get(); + return count = TreeMultiset.this.count(element); } - }; + } } @Override - public SortedMultiset<E> headMultiset(@Nullable E upperBound, BoundType boundType) { - return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.upTo( - comparator(), - upperBound, - boundType)), header); + public void clear() { + Node<E> root = rootReference.get(); + Node<E> cleared = BstRangeOps.minusRange(range, + BstCountBasedBalancePolicies.<E, Node<E>>fullRebalancePolicy(distinctAggregate()), + nodeFactory(), root); + if (!rootReference.compareAndSet(root, cleared)) { + throw new ConcurrentModificationException(); + } } @Override - public SortedMultiset<E> tailMultiset(@Nullable E lowerBound, BoundType boundType) { - return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.downTo( - comparator(), - lowerBound, - boundType)), header); + public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { + checkNotNull(upperBound); + return new TreeMultiset<E>( + range.intersect(GeneralRange.upTo(comparator, upperBound, boundType)), rootReference); } - static int distinctElements(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.distinctElements; + @Override + public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + checkNotNull(lowerBound); + return new TreeMultiset<E>( + range.intersect(GeneralRange.downTo(comparator, lowerBound, boundType)), rootReference); } - private static final class Reference<T> { - @Nullable private T value; - - @Nullable public T get() { - return value; - } - - public void checkAndSet(@Nullable T expected, T newValue) { - if (value != expected) { - throw new ConcurrentModificationException(); - } - value = newValue; - } + /** + * {@inheritDoc} + * + * @since 11.0 + */ + @Override + public Comparator<? super E> comparator() { + return super.comparator(); } - private static final class AvlNode<E> extends Multisets.AbstractEntry<E> { - @Nullable private final E elem; - - // elemCount is 0 iff this node has been deleted. - private int elemCount; + private static final class Node<E> extends BstNode<E, Node<E>> implements Serializable { + private final long size; + private final int distinct; - private int distinctElements; - private long totalCount; - private int height; - private AvlNode<E> left; - private AvlNode<E> right; - private AvlNode<E> pred; - private AvlNode<E> succ; - - AvlNode(@Nullable E elem, int elemCount) { + private Node(E key, int elemCount, @Nullable Node<E> left, + @Nullable Node<E> right) { + super(key, left, right); 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; - } + this.size = (long) elemCount + sizeOrZero(left) + sizeOrZero(right); + this.distinct = 1 + distinctOrZero(left) + distinctOrZero(right); } - 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; + int elemCount() { + long result = size - sizeOrZero(childOrNull(LEFT)) + - sizeOrZero(childOrNull(RIGHT)); + return Ints.checkedCast(result); } - 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; + private Node(E key, int elemCount) { + this(key, elemCount, null, null); } - 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; - } + private static final long serialVersionUID = 0; + } - left = initLeft.remove(comparator, e, count, result); + private static long sizeOrZero(@Nullable Node<?> node) { + return (node == null) ? 0 : node.size; + } - 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; - } + private static int distinctOrZero(@Nullable Node<?> node) { + return (node == null) ? 0 : node.distinct; + } - right = initRight.remove(comparator, e, count, result); + private static int countOrZero(@Nullable Node<?> entry) { + return (entry == null) ? 0 : entry.elemCount(); + } - if (result[0] > 0) { - if (count >= result[0]) { - this.distinctElements--; - this.totalCount -= result[0]; - } else { - this.totalCount -= count; - } - } - return rebalance(); - } + @SuppressWarnings("unchecked") + private BstAggregate<Node<E>> distinctAggregate() { + return (BstAggregate) DISTINCT_AGGREGATE; + } - // removing count from me! - result[0] = elemCount; - if (count >= elemCount) { - return deleteMe(); - } else { - this.elemCount -= count; - this.totalCount -= count; - return this; - } + private static final BstAggregate<Node<Object>> DISTINCT_AGGREGATE = + new BstAggregate<Node<Object>>() { + @Override + public int entryValue(Node<Object> entry) { + return 1; } - 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; + @Override + public long treeValue(@Nullable Node<Object> tree) { + return distinctOrZero(tree); } + }; - 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); + @SuppressWarnings("unchecked") + private BstAggregate<Node<E>> sizeAggregate() { + return (BstAggregate) SIZE_AGGREGATE; + } - 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; + private static final BstAggregate<Node<Object>> SIZE_AGGREGATE = + new BstAggregate<Node<Object>>() { + @Override + public int entryValue(Node<Object> entry) { + return entry.elemCount(); } - 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]; + @Override + public long treeValue(@Nullable Node<Object> tree) { + return sizeOrZero(tree); } - 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; - } + @SuppressWarnings("unchecked") + private BstNodeFactory<Node<E>> nodeFactory() { + return (BstNodeFactory) NODE_FACTORY; + } - 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(); - } - } + private static final BstNodeFactory<Node<Object>> NODE_FACTORY = + new BstNodeFactory<Node<Object>>() { + @Override + public Node<Object> createNode(Node<Object> source, @Nullable Node<Object> left, + @Nullable Node<Object> right) { + return new Node<Object>(source.getKey(), source.elemCount(), left, right); + } + }; - // 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(); - } - } + private abstract class MultisetModifier implements BstModifier<E, Node<E>> { + abstract int newCount(int oldCount); - // Removes the maximum node from this subtree to be reused elsewhere - private AvlNode<E> removeMax(AvlNode<E> node) { - if (right == null) { - return left; + @Nullable + @Override + public BstModificationResult<Node<E>> modify(E key, @Nullable Node<E> originalEntry) { + int oldCount = countOrZero(originalEntry); + int newCount = newCount(oldCount); + if (oldCount == newCount) { + return BstModificationResult.identity(originalEntry); + } else if (newCount == 0) { + return BstModificationResult.rebalancingChange(originalEntry, null); + } else if (oldCount == 0) { + return BstModificationResult.rebalancingChange(null, new Node<E>(key, newCount)); } else { - right = right.removeMax(node); - distinctElements--; - totalCount -= node.elemCount; - return rebalance(); + return BstModificationResult.rebuildingChange(originalEntry, + new Node<E>(originalEntry.getKey(), newCount)); } } + } - 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 final class AddModifier extends MultisetModifier { + private final int countToAdd; - private void recompute() { - recomputeMultiset(); - recomputeHeight(); + private AddModifier(int countToAdd) { + checkArgument(countToAdd > 0); + this.countToAdd = countToAdd; } - 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; - } + @Override + int newCount(int oldCount) { + checkArgument(countToAdd <= Integer.MAX_VALUE - oldCount, "Cannot add this many elements"); + return oldCount + countToAdd; } + } - private int balanceFactor() { - return height(left) - height(right); - } + private final class RemoveModifier extends MultisetModifier { + private final int countToRemove; - 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 RemoveModifier(int countToRemove) { + checkArgument(countToRemove > 0); + this.countToRemove = countToRemove; } - 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; + @Override + int newCount(int oldCount) { + return Math.max(0, oldCount - countToRemove); } + } - private static long totalCount(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.totalCount; - } + private final class SetCountModifier extends MultisetModifier { + private final int countToSet; - private static int height(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.height; - } - - @Nullable private AvlNode<E> ceiling(Comparator<? super E> comparator, E e) { - int cmp = comparator.compare(e, elem); - if (cmp < 0) { - return (left == null) ? this : Objects.firstNonNull(left.ceiling(comparator, e), this); - } else if (cmp == 0) { - return this; - } else { - return (right == null) ? null : right.ceiling(comparator, e); - } - } - - @Nullable private AvlNode<E> floor(Comparator<? super E> comparator, E e) { - int cmp = comparator.compare(e, elem); - if (cmp > 0) { - return (right == null) ? this : Objects.firstNonNull(right.floor(comparator, e), this); - } else if (cmp == 0) { - return this; - } else { - return (left == null) ? null : left.floor(comparator, e); - } + private SetCountModifier(int countToSet) { + checkArgument(countToSet >= 0); + this.countToSet = countToSet; } @Override - public E getElement() { - return elem; + int newCount(int oldCount) { + return countToSet; } + } - @Override - public int getCount() { - return elemCount; + private final class ConditionalSetCountModifier extends MultisetModifier { + private final int expectedCount; + private final int setCount; + + private ConditionalSetCountModifier(int expectedCount, int setCount) { + checkArgument(setCount >= 0 & expectedCount >= 0); + this.expectedCount = expectedCount; + this.setCount = setCount; } @Override - public String toString() { - return Multisets.immutableEntry(getElement(), getCount()).toString(); + int newCount(int oldCount) { + return (oldCount == expectedCount) ? setCount : oldCount; } } - 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. + * 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. */ /** - * @serialData the 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 { @@ -960,23 +553,19 @@ public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements } @GwtIncompatible("java.io.ObjectInputStream") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { stream.defaultReadObject(); - @SuppressWarnings("unchecked") - // reading data stored by writeObject + @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, + 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.getFieldSetter(TreeMultiset.class, "rootReference").set(this, + new Reference<Node<E>>()); Serialization.populateMultiset(this, stream); } - @GwtIncompatible("not needed in emulated source") private static final long serialVersionUID = 1; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 1; } diff --git a/guava/src/com/google/common/collect/TreeRangeMap.java b/guava/src/com/google/common/collect/TreeRangeMap.java deleted file mode 100644 index e5b5f47..0000000 --- a/guava/src/com/google/common/collect/TreeRangeMap.java +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Copyright (C) 2012 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.Predicates.compose; -import static com.google.common.base.Predicates.in; -import static com.google.common.base.Predicates.not; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; - -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting - * all optional operations. - * - * <p>Like all {@code RangeMap} implementations, this supports neither null - * keys nor null values. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("NavigableMap") -public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> { - - private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound; - - public static <K extends Comparable, V> TreeRangeMap<K, V> create() { - return new TreeRangeMap<K, V>(); - } - - private TreeRangeMap() { - this.entriesByLowerBound = Maps.newTreeMap(); - } - - private static final class RangeMapEntry<K extends Comparable, V> - extends AbstractMapEntry<Range<K>, V> { - private final Range<K> range; - private final V value; - - RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { - this(Range.create(lowerBound, upperBound), value); - } - - RangeMapEntry(Range<K> range, V value) { - this.range = range; - this.value = value; - } - - @Override - public Range<K> getKey() { - return range; - } - - @Override - public V getValue() { - return value; - } - - public boolean contains(K value) { - return range.contains(value); - } - - Cut<K> getLowerBound() { - return range.lowerBound; - } - - Cut<K> getUpperBound() { - return range.upperBound; - } - } - - @Override - @Nullable - public V get(K key) { - Entry<Range<K>, V> entry = getEntry(key); - return (entry == null) ? null : entry.getValue(); - } - - @Override - @Nullable - public Entry<Range<K>, V> getEntry(K key) { - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry = - entriesByLowerBound.floorEntry(Cut.belowValue(key)); - if (mapEntry != null && mapEntry.getValue().contains(key)) { - return mapEntry.getValue(); - } else { - return null; - } - } - - @Override - public void put(Range<K> range, V value) { - if (!range.isEmpty()) { - checkNotNull(value); - remove(range); - entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value)); - } - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - for (Map.Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - @Override - public void clear() { - entriesByLowerBound.clear(); - } - - @Override - public Range<K> span() { - Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry(); - Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry(); - if (firstEntry == null) { - throw new NoSuchElementException(); - } - return Range.create( - firstEntry.getValue().getKey().lowerBound, - lastEntry.getValue().getKey().upperBound); - } - - private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { - entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value)); - } - - @Override - public void remove(Range<K> rangeToRemove) { - if (rangeToRemove.isEmpty()) { - return; - } - - /* - * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to - * indicate the bounds of ranges in the range map. - */ - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate = - entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound); - if (mapEntryBelowToTruncate != null) { - // we know ( [ - RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue(); - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { - // we know ( [ ) - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { - // we know ( [ ] ), so insert the range ] ) back into the map -- - // it's being split apart - putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), - mapEntryBelowToTruncate.getValue().getValue()); - } - // overwrite mapEntryToTruncateBelow with a truncated range - putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound, - mapEntryBelowToTruncate.getValue().getValue()); - } - } - - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate = - entriesByLowerBound.lowerEntry(rangeToRemove.upperBound); - if (mapEntryAboveToTruncate != null) { - // we know ( ] - RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue(); - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { - // we know ( ] ), and since we dealt with truncating below already, - // we know [ ( ] ) - putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), - mapEntryAboveToTruncate.getValue().getValue()); - entriesByLowerBound.remove(rangeToRemove.lowerBound); - } - } - entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); - } - - @Override - public Map<Range<K>, V> asMapOfRanges() { - return new AsMapOfRanges(); - } - - private final class AsMapOfRanges extends AbstractMap<Range<K>, V> { - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public V get(@Nullable Object key) { - if (key instanceof Range) { - Range<?> range = (Range<?>) key; - RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound); - if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { - return rangeMapEntry.getValue(); - } - } - return null; - } - - @Override - public Set<Entry<Range<K>, V>> entrySet() { - return new AbstractSet<Entry<Range<K>, V>>() { - - @SuppressWarnings("unchecked") // it's safe to upcast iterators - @Override - public Iterator<Entry<Range<K>, V>> iterator() { - return (Iterator) entriesByLowerBound.values().iterator(); - } - - @Override - public int size() { - return entriesByLowerBound.size(); - } - }; - } - } - - @Override - public RangeMap<K, V> subRangeMap(Range<K> subRange) { - if (subRange.equals(Range.all())) { - return this; - } else { - return new SubRangeMap(subRange); - } - } - - @SuppressWarnings("unchecked") - private RangeMap<K, V> emptySubRangeMap() { - return EMPTY_SUB_RANGE_MAP; - } - - private static final RangeMap EMPTY_SUB_RANGE_MAP = - new RangeMap() { - @Override - @Nullable - public Object get(Comparable key) { - return null; - } - - @Override - @Nullable - public Entry<Range, Object> getEntry(Comparable key) { - return null; - } - - @Override - public Range span() { - throw new NoSuchElementException(); - } - - @Override - public void put(Range range, Object value) { - checkNotNull(range); - throw new IllegalArgumentException( - "Cannot insert range " + range + " into an empty subRangeMap"); - } - - @Override - public void putAll(RangeMap rangeMap) { - if (!rangeMap.asMapOfRanges().isEmpty()) { - throw new IllegalArgumentException( - "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap"); - } - } - - @Override - public void clear() {} - - @Override - public void remove(Range range) { - checkNotNull(range); - } - - @Override - public Map<Range, Object> asMapOfRanges() { - return Collections.emptyMap(); - } - - @Override - public RangeMap subRangeMap(Range range) { - checkNotNull(range); - return this; - } - }; - - private class SubRangeMap implements RangeMap<K, V> { - - private final Range<K> subRange; - - SubRangeMap(Range<K> subRange) { - this.subRange = subRange; - } - - @Override - @Nullable - public V get(K key) { - return subRange.contains(key) - ? TreeRangeMap.this.get(key) - : null; - } - - @Override - @Nullable - public Entry<Range<K>, V> getEntry(K key) { - if (subRange.contains(key)) { - Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key); - if (entry != null) { - return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); - } - } - return null; - } - - @Override - public Range<K> span() { - Cut<K> lowerBound; - Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry = - entriesByLowerBound.floorEntry(subRange.lowerBound); - if (lowerEntry != null && - lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) { - lowerBound = subRange.lowerBound; - } else { - lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound); - if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) { - throw new NoSuchElementException(); - } - } - - Cut<K> upperBound; - Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry = - entriesByLowerBound.lowerEntry(subRange.upperBound); - if (upperEntry == null) { - throw new NoSuchElementException(); - } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) { - upperBound = subRange.upperBound; - } else { - upperBound = upperEntry.getValue().getUpperBound(); - } - return Range.create(lowerBound, upperBound); - } - - @Override - public void put(Range<K> range, V value) { - checkArgument(subRange.encloses(range), - "Cannot put range %s into a subRangeMap(%s)", range, subRange); - TreeRangeMap.this.put(range, value); - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - if (rangeMap.asMapOfRanges().isEmpty()) { - return; - } - Range<K> span = rangeMap.span(); - checkArgument(subRange.encloses(span), - "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, subRange); - TreeRangeMap.this.putAll(rangeMap); - } - - @Override - public void clear() { - TreeRangeMap.this.remove(subRange); - } - - @Override - public void remove(Range<K> range) { - if (range.isConnected(subRange)) { - TreeRangeMap.this.remove(range.intersection(subRange)); - } - } - - @Override - public RangeMap<K, V> subRangeMap(Range<K> range) { - if (!range.isConnected(subRange)) { - return emptySubRangeMap(); - } else { - return TreeRangeMap.this.subRangeMap(range.intersection(subRange)); - } - } - - @Override - public Map<Range<K>, V> asMapOfRanges() { - return new SubRangeMapAsMap(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public String toString() { - return asMapOfRanges().toString(); - } - - class SubRangeMapAsMap extends AbstractMap<Range<K>, V> { - - @Override - public boolean containsKey(Object key) { - return get(key) != null; - } - - @Override - public V get(Object key) { - try { - if (key instanceof Range) { - @SuppressWarnings("unchecked") // we catch ClassCastExceptions - Range<K> r = (Range<K>) key; - if (!subRange.encloses(r) || r.isEmpty()) { - return null; - } - RangeMapEntry<K, V> candidate = null; - if (r.lowerBound.compareTo(subRange.lowerBound) == 0) { - // r could be truncated on the left - Entry<Cut<K>, RangeMapEntry<K, V>> entry = - entriesByLowerBound.floorEntry(r.lowerBound); - if (entry != null) { - candidate = entry.getValue(); - } - } else { - candidate = entriesByLowerBound.get(r.lowerBound); - } - - if (candidate != null && candidate.getKey().isConnected(subRange) - && candidate.getKey().intersection(subRange).equals(r)) { - return candidate.getValue(); - } - } - } catch (ClassCastException e) { - return null; - } - return null; - } - - @Override - public V remove(Object key) { - V value = get(key); - if (value != null) { - @SuppressWarnings("unchecked") // it's definitely in the map, so safe - Range<K> range = (Range<K>) key; - TreeRangeMap.this.remove(range); - return value; - } - return null; - } - - @Override - public void clear() { - SubRangeMap.this.clear(); - } - - private boolean removeIf(Predicate<? super Entry<Range<K>, V>> predicate) { - List<Range<K>> toRemove = Lists.newArrayList(); - for (Entry<Range<K>, V> entry : entrySet()) { - if (predicate.apply(entry)) { - toRemove.add(entry.getKey()); - } - } - for (Range<K> range : toRemove) { - TreeRangeMap.this.remove(range); - } - return !toRemove.isEmpty(); - } - - @Override - public Set<Range<K>> keySet() { - return new Maps.KeySet<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public boolean remove(@Nullable Object o) { - return SubRangeMapAsMap.this.remove(o) != null; - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<Range<K>>keyFunction())); - } - }; - } - - @Override - public Set<Entry<Range<K>, V>> entrySet() { - return new Maps.EntrySet<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public Iterator<Entry<Range<K>, V>> iterator() { - if (subRange.isEmpty()) { - return Iterators.emptyIterator(); - } - Cut<K> cutToStart = Objects.firstNonNull( - entriesByLowerBound.floorKey(subRange.lowerBound), - subRange.lowerBound); - final Iterator<RangeMapEntry<K, V>> backingItr = - entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); - return new AbstractIterator<Entry<Range<K>, V>>() { - - @Override - protected Entry<Range<K>, V> computeNext() { - while (backingItr.hasNext()) { - RangeMapEntry<K, V> entry = backingItr.next(); - if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { - break; - } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { - // this might not be true e.g. at the start of the iteration - return Maps.immutableEntry( - entry.getKey().intersection(subRange), entry.getValue()); - } - } - return endOfData(); - } - }; - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - - @Override - public int size() { - return Iterators.size(iterator()); - } - - @Override - public boolean isEmpty() { - return !iterator().hasNext(); - } - }; - } - - @Override - public Collection<V> values() { - return new Maps.Values<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<V>valueFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<V>valueFunction())); - } - }; - } - } - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public String toString() { - return entriesByLowerBound.values().toString(); - } -} diff --git a/guava/src/com/google/common/collect/TreeRangeSet.java b/guava/src/com/google/common/collect/TreeRangeSet.java deleted file mode 100644 index d67c5f4..0000000 --- a/guava/src/com/google/common/collect/TreeRangeSet.java +++ /dev/null @@ -1,851 +0,0 @@ -/* - * 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.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.TreeMap; - -import javax.annotation.Nullable; - -/** - * An implementation of {@link RangeSet} backed by a {@link TreeMap}. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("uses NavigableMap") -public class TreeRangeSet<C extends Comparable<?>> - extends AbstractRangeSet<C> { - - @VisibleForTesting - final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - - /** - * Creates an empty {@code TreeRangeSet} instance. - */ - public static <C extends Comparable<?>> TreeRangeSet<C> create() { - return new TreeRangeSet<C>(new TreeMap<Cut<C>, Range<C>>()); - } - - /** - * Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. - */ - public static <C extends Comparable<?>> TreeRangeSet<C> create(RangeSet<C> rangeSet) { - TreeRangeSet<C> result = create(); - result.addAll(rangeSet); - return result; - } - - private TreeRangeSet(NavigableMap<Cut<C>, Range<C>> rangesByLowerCut) { - this.rangesByLowerBound = rangesByLowerCut; - } - - private transient Set<Range<C>> asRanges; - - @Override - public Set<Range<C>> asRanges() { - Set<Range<C>> result = asRanges; - return (result == null) ? asRanges = new AsRanges() : result; - } - - final class AsRanges extends ForwardingCollection<Range<C>> implements Set<Range<C>> { - @Override - protected Collection<Range<C>> delegate() { - return rangesByLowerBound.values(); - } - - @Override - public int hashCode() { - return Sets.hashCodeImpl(this); - } - - @Override - public boolean equals(@Nullable Object o) { - return Sets.equalsImpl(this, o); - } - } - - @Override - @Nullable - public Range<C> rangeContaining(C value) { - checkNotNull(value); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); - if (floorEntry != null && floorEntry.getValue().contains(value)) { - return floorEntry.getValue(); - } else { - // TODO(kevinb): revisit this design choice - return null; - } - } - - @Override - public boolean encloses(Range<C> range) { - checkNotNull(range); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); - return floorEntry != null && floorEntry.getValue().encloses(range); - } - - @Nullable - private Range<C> rangeEnclosing(Range<C> range) { - checkNotNull(range); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); - return (floorEntry != null && floorEntry.getValue().encloses(range)) - ? floorEntry.getValue() - : null; - } - - @Override - public Range<C> span() { - Entry<Cut<C>, Range<C>> firstEntry = rangesByLowerBound.firstEntry(); - Entry<Cut<C>, Range<C>> lastEntry = rangesByLowerBound.lastEntry(); - if (firstEntry == null) { - throw new NoSuchElementException(); - } - return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); - } - - @Override - public void add(Range<C> rangeToAdd) { - checkNotNull(rangeToAdd); - - if (rangeToAdd.isEmpty()) { - return; - } - - // We will use { } to illustrate ranges currently in the range set, and < > - // to illustrate rangeToAdd. - Cut<C> lbToAdd = rangeToAdd.lowerBound; - Cut<C> ubToAdd = rangeToAdd.upperBound; - - Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); - if (entryBelowLB != null) { - // { < - Range<C> rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { - // { < }, and we will need to coalesce - if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { - // { < > } - ubToAdd = rangeBelowLB.upperBound; - /* - * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If - * not, add tests to demonstrate the problem with each approach - */ - } - lbToAdd = rangeBelowLB.lowerBound; - } - } - - Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); - if (entryBelowUB != null) { - // { > - Range<C> rangeBelowUB = entryBelowUB.getValue(); - if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { - // { > }, and we need to coalesce - ubToAdd = rangeBelowUB.upperBound; - } - } - - // Remove ranges which are strictly enclosed. - rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); - - replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); - } - - @Override - public void remove(Range<C> rangeToRemove) { - checkNotNull(rangeToRemove); - - if (rangeToRemove.isEmpty()) { - return; - } - - // We will use { } to illustrate ranges currently in the range set, and < > - // to illustrate rangeToRemove. - - Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); - if (entryBelowLB != null) { - // { < - Range<C> rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { - // { < }, and we will need to subdivide - if (rangeToRemove.hasUpperBound() - && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { - // { < > } - replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); - } - replaceRangeWithSameLowerBound( - Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); - } - } - - Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); - if (entryBelowUB != null) { - // { > - Range<C> rangeBelowUB = entryBelowUB.getValue(); - if (rangeToRemove.hasUpperBound() - && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { - // { > } - replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); - } - } - - rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); - } - - private void replaceRangeWithSameLowerBound(Range<C> range) { - if (range.isEmpty()) { - rangesByLowerBound.remove(range.lowerBound); - } else { - rangesByLowerBound.put(range.lowerBound, range); - } - } - - private transient RangeSet<C> complement; - - @Override - public RangeSet<C> complement() { - RangeSet<C> result = complement; - return (result == null) ? complement = new Complement() : result; - } - - @VisibleForTesting - static final class RangesByUpperBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - - /** - * upperBoundWindow represents the headMap/subMap/tailMap view of the entire "ranges by upper - * bound" map; it's a constraint on the *keys*, and does not affect the values. - */ - private final Range<Cut<C>> upperBoundWindow; - - RangesByUpperBound(NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { - this.rangesByLowerBound = rangesByLowerBound; - this.upperBoundWindow = Range.all(); - } - - private RangesByUpperBound( - NavigableMap<Cut<C>, Range<C>> rangesByLowerBound, Range<Cut<C>> upperBoundWindow) { - this.rangesByLowerBound = rangesByLowerBound; - this.upperBoundWindow = upperBoundWindow; - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { - if (window.isConnected(upperBoundWindow)) { - return new RangesByUpperBound<C>(rangesByLowerBound, window.intersection(upperBoundWindow)); - } else { - return ImmutableSortedMap.of(); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), - toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public Range<C> get(@Nullable Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") // we catch CCEs - Cut<C> cut = (Cut<C>) key; - if (!upperBoundWindow.contains(cut)) { - return null; - } - Entry<Cut<C>, Range<C>> candidate = rangesByLowerBound.lowerEntry(cut); - if (candidate != null && candidate.getValue().upperBound.equals(cut)) { - return candidate.getValue(); - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - /* - * We want to start the iteration at the first range where the upper bound is in - * upperBoundWindow. - */ - final Iterator<Range<C>> backingItr; - if (!upperBoundWindow.hasLowerBound()) { - backingItr = rangesByLowerBound.values().iterator(); - } else { - Entry<Cut<C>, Range<C>> lowerEntry = - rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); - if (lowerEntry == null) { - backingItr = rangesByLowerBound.values().iterator(); - } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { - backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); - } else { - backingItr = rangesByLowerBound.tailMap(upperBoundWindow.lowerEndpoint(), true) - .values().iterator(); - } - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!backingItr.hasNext()) { - return endOfData(); - } - Range<C> range = backingItr.next(); - if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { - return endOfData(); - } else { - return Maps.immutableEntry(range.upperBound, range); - } - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - Collection<Range<C>> candidates; - if (upperBoundWindow.hasUpperBound()) { - candidates = rangesByLowerBound.headMap(upperBoundWindow.upperEndpoint(), false) - .descendingMap().values(); - } else { - candidates = rangesByLowerBound.descendingMap().values(); - } - final PeekingIterator<Range<C>> backingItr = Iterators.peekingIterator(candidates.iterator()); - if (backingItr.hasNext() - && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { - backingItr.next(); - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!backingItr.hasNext()) { - return endOfData(); - } - Range<C> range = backingItr.next(); - return upperBoundWindow.lowerBound.isLessThan(range.upperBound) - ? Maps.immutableEntry(range.upperBound, range) - : endOfData(); - } - }; - } - - @Override - public int size() { - if (upperBoundWindow.equals(Range.all())) { - return rangesByLowerBound.size(); - } - return Iterators.size(entryIterator()); - } - - @Override - public boolean isEmpty() { - return upperBoundWindow.equals(Range.all()) - ? rangesByLowerBound.isEmpty() - : !entryIterator().hasNext(); - } - } - - private static final class ComplementRangesByLowerBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - private final NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound; - private final NavigableMap<Cut<C>, Range<C>> positiveRangesByUpperBound; - - /** - * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the entire - * "complement ranges by lower bound" map; it's a constraint on the *keys*, and does not affect - * the values. - */ - private final Range<Cut<C>> complementLowerBoundWindow; - - ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound) { - this(positiveRangesByLowerBound, Range.<Cut<C>>all()); - } - - private ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound, - Range<Cut<C>> window) { - this.positiveRangesByLowerBound = positiveRangesByLowerBound; - this.positiveRangesByUpperBound = new RangesByUpperBound<C>(positiveRangesByLowerBound); - this.complementLowerBoundWindow = window; - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> subWindow) { - if (!complementLowerBoundWindow.isConnected(subWindow)) { - return ImmutableSortedMap.of(); - } else { - subWindow = subWindow.intersection(complementLowerBoundWindow); - return new ComplementRangesByLowerBound<C>(positiveRangesByLowerBound, subWindow); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), - toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - /* - * firstComplementRangeLowerBound is the first complement range lower bound inside - * complementLowerBoundWindow. Complement range lower bounds are either positive range upper - * bounds, or Cut.belowAll(). - * - * positiveItr starts at the first positive range with lower bound greater than - * firstComplementRangeLowerBound. (Positive range lower bounds correspond to complement range - * upper bounds.) - */ - Collection<Range<C>> positiveRanges; - if (complementLowerBoundWindow.hasLowerBound()) { - positiveRanges = positiveRangesByUpperBound.tailMap( - complementLowerBoundWindow.lowerEndpoint(), - complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values(); - } else { - positiveRanges = positiveRangesByUpperBound.values(); - } - final PeekingIterator<Range<C>> positiveItr = Iterators.peekingIterator( - positiveRanges.iterator()); - final Cut<C> firstComplementRangeLowerBound; - if (complementLowerBoundWindow.contains(Cut.<C>belowAll()) && - (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.<C>belowAll())) { - firstComplementRangeLowerBound = Cut.belowAll(); - } else if (positiveItr.hasNext()) { - firstComplementRangeLowerBound = positiveItr.next().upperBound; - } else { - return Iterators.emptyIterator(); - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - Cut<C> nextComplementRangeLowerBound = firstComplementRangeLowerBound; - - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) - || nextComplementRangeLowerBound == Cut.<C>aboveAll()) { - return endOfData(); - } - Range<C> negativeRange; - if (positiveItr.hasNext()) { - Range<C> positiveRange = positiveItr.next(); - negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); - nextComplementRangeLowerBound = positiveRange.upperBound; - } else { - negativeRange = Range.create(nextComplementRangeLowerBound, Cut.<C>aboveAll()); - nextComplementRangeLowerBound = Cut.aboveAll(); - } - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - Iterator<Range<C>> itr; - /* - * firstComplementRangeUpperBound is the upper bound of the last complement range with lower - * bound inside complementLowerBoundWindow. - * - * positiveItr starts at the first positive range with upper bound less than - * firstComplementRangeUpperBound. (Positive range upper bounds correspond to complement range - * lower bounds.) - */ - Cut<C> startingPoint = complementLowerBoundWindow.hasUpperBound() - ? complementLowerBoundWindow.upperEndpoint() - : Cut.<C>aboveAll(); - boolean inclusive = complementLowerBoundWindow.hasUpperBound() - && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; - final PeekingIterator<Range<C>> positiveItr = - Iterators.peekingIterator(positiveRangesByUpperBound.headMap(startingPoint, inclusive) - .descendingMap().values().iterator()); - Cut<C> cut; - if (positiveItr.hasNext()) { - cut = (positiveItr.peek().upperBound == Cut.<C>aboveAll()) - ? positiveItr.next().lowerBound - : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); - } else if (!complementLowerBoundWindow.contains(Cut.<C>belowAll()) - || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { - return Iterators.emptyIterator(); - } else { - cut = positiveRangesByLowerBound.higherKey(Cut.<C>belowAll()); - } - final Cut<C> firstComplementRangeUpperBound = Objects.firstNonNull(cut, Cut.<C>aboveAll()); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - Cut<C> nextComplementRangeUpperBound = firstComplementRangeUpperBound; - - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (nextComplementRangeUpperBound == Cut.<C>belowAll()) { - return endOfData(); - } else if (positiveItr.hasNext()) { - Range<C> positiveRange = positiveItr.next(); - Range<C> negativeRange = - Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); - nextComplementRangeUpperBound = positiveRange.lowerBound; - if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); - } - } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.<C>belowAll())) { - Range<C> negativeRange = - Range.create(Cut.<C>belowAll(), nextComplementRangeUpperBound); - nextComplementRangeUpperBound = Cut.belowAll(); - return Maps.immutableEntry(Cut.<C>belowAll(), negativeRange); - } - return endOfData(); - } - }; - } - - @Override - public int size() { - return Iterators.size(entryIterator()); - } - - @Override - @Nullable - public Range<C> get(Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") - Cut<C> cut = (Cut<C>) key; - // tailMap respects the current window - Entry<Cut<C>, Range<C>> firstEntry = tailMap(cut, true).firstEntry(); - if (firstEntry != null && firstEntry.getKey().equals(cut)) { - return firstEntry.getValue(); - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - public boolean containsKey(Object key) { - return get(key) != null; - } - } - - private final class Complement extends TreeRangeSet<C> { - Complement() { - super(new ComplementRangesByLowerBound<C>(TreeRangeSet.this.rangesByLowerBound)); - } - - @Override - public void add(Range<C> rangeToAdd) { - TreeRangeSet.this.remove(rangeToAdd); - } - - @Override - public void remove(Range<C> rangeToRemove) { - TreeRangeSet.this.add(rangeToRemove); - } - - @Override - public boolean contains(C value) { - return !TreeRangeSet.this.contains(value); - } - - @Override - public RangeSet<C> complement() { - return TreeRangeSet.this; - } - } - - private static final class SubRangeSetRangesByLowerBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - /** - * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not - * affect the values. - */ - private final Range<Cut<C>> lowerBoundWindow; - - /** - * restriction is the subRangeSet view; ranges are truncated to their intersection with - * restriction. - */ - private final Range<C> restriction; - - private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - private final NavigableMap<Cut<C>, Range<C>> rangesByUpperBound; - - private SubRangeSetRangesByLowerBound(Range<Cut<C>> lowerBoundWindow, Range<C> restriction, - NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { - this.lowerBoundWindow = checkNotNull(lowerBoundWindow); - this.restriction = checkNotNull(restriction); - this.rangesByLowerBound = checkNotNull(rangesByLowerBound); - this.rangesByUpperBound = new RangesByUpperBound<C>(rangesByLowerBound); - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { - if (!window.isConnected(lowerBoundWindow)) { - return ImmutableSortedMap.of(); - } else { - return new SubRangeSetRangesByLowerBound<C>( - lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - @Nullable - public Range<C> get(@Nullable Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") // we catch CCE's - Cut<C> cut = (Cut<C>) key; - if (!lowerBoundWindow.contains(cut) || cut.compareTo(restriction.lowerBound) < 0 - || cut.compareTo(restriction.upperBound) >= 0) { - return null; - } else if (cut.equals(restriction.lowerBound)) { - // it might be present, truncated on the left - Range<C> candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); - if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { - return candidate.intersection(restriction); - } - } else { - Range<C> result = rangesByLowerBound.get(cut); - if (result != null) { - return result.intersection(restriction); - } - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - if (restriction.isEmpty()) { - return Iterators.emptyIterator(); - } - final Iterator<Range<C>> completeRangeItr; - if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { - return Iterators.emptyIterator(); - } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { - // starts at the first range with upper bound strictly greater than restriction.lowerBound - completeRangeItr = - rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); - } else { - // starts at the first range with lower bound above lowerBoundWindow.lowerBound - completeRangeItr = rangesByLowerBound.tailMap(lowerBoundWindow.lowerBound.endpoint(), - lowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values().iterator(); - } - final Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() - .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!completeRangeItr.hasNext()) { - return endOfData(); - } - Range<C> nextRange = completeRangeItr.next(); - if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { - return endOfData(); - } else { - nextRange = nextRange.intersection(restriction); - return Maps.immutableEntry(nextRange.lowerBound, nextRange); - } - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - if (restriction.isEmpty()) { - return Iterators.emptyIterator(); - } - Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() - .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); - final Iterator<Range<C>> completeRangeItr = rangesByLowerBound.headMap( - upperBoundOnLowerBounds.endpoint(), - upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) - .descendingMap().values().iterator(); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!completeRangeItr.hasNext()) { - return endOfData(); - } - Range<C> nextRange = completeRangeItr.next(); - if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { - return endOfData(); - } - nextRange = nextRange.intersection(restriction); - if (lowerBoundWindow.contains(nextRange.lowerBound)) { - return Maps.immutableEntry(nextRange.lowerBound, nextRange); - } else { - return endOfData(); - } - } - }; - } - - @Override - public int size() { - return Iterators.size(entryIterator()); - } - } - - @Override - public RangeSet<C> subRangeSet(Range<C> view) { - return view.equals(Range.<C>all()) ? this : new SubRangeSet(view); - } - - private final class SubRangeSet extends TreeRangeSet<C> { - private final Range<C> restriction; - - SubRangeSet(Range<C> restriction) { - super(new SubRangeSetRangesByLowerBound<C>( - Range.<Cut<C>>all(), restriction, TreeRangeSet.this.rangesByLowerBound)); - this.restriction = restriction; - } - - @Override - public boolean encloses(Range<C> range) { - if (!restriction.isEmpty() && restriction.encloses(range)) { - Range<C> enclosing = TreeRangeSet.this.rangeEnclosing(range); - return enclosing != null && !enclosing.intersection(restriction).isEmpty(); - } - return false; - } - - @Override - @Nullable - public Range<C> rangeContaining(C value) { - if (!restriction.contains(value)) { - return null; - } - Range<C> result = TreeRangeSet.this.rangeContaining(value); - return (result == null) ? null : result.intersection(restriction); - } - - @Override - public void add(Range<C> rangeToAdd) { - checkArgument(restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", - rangeToAdd, restriction); - super.add(rangeToAdd); - } - - @Override - public void remove(Range<C> rangeToRemove) { - if (rangeToRemove.isConnected(restriction)) { - TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); - } - } - - @Override - public boolean contains(C value) { - return restriction.contains(value) && TreeRangeSet.this.contains(value); - } - - @Override - public void clear() { - TreeRangeSet.this.remove(restriction); - } - - @Override - public RangeSet<C> subRangeSet(Range<C> view) { - if (view.encloses(restriction)) { - return this; - } else if (view.isConnected(restriction)) { - return new SubRangeSet(restriction.intersection(view)); - } else { - return ImmutableRangeSet.of(); - } - } - } -} diff --git a/guava/src/com/google/common/collect/UnmodifiableIterator.java b/guava/src/com/google/common/collect/UnmodifiableIterator.java index 55ceef9..5cff61b 100644 --- a/guava/src/com/google/common/collect/UnmodifiableIterator.java +++ b/guava/src/com/google/common/collect/UnmodifiableIterator.java @@ -30,14 +30,12 @@ import java.util.Iterator; public abstract class UnmodifiableIterator<E> implements Iterator<E> { /** Constructor for use by subclasses. */ protected UnmodifiableIterator() {} - + /** * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void remove() { throw new UnsupportedOperationException(); diff --git a/guava/src/com/google/common/collect/UnmodifiableListIterator.java b/guava/src/com/google/common/collect/UnmodifiableListIterator.java index 8e535a3..fa71bdc 100644 --- a/guava/src/com/google/common/collect/UnmodifiableListIterator.java +++ b/guava/src/com/google/common/collect/UnmodifiableListIterator.java @@ -37,9 +37,8 @@ public abstract class UnmodifiableListIterator<E> * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void add(E e) { + @Override public final void add(E e) { throw new UnsupportedOperationException(); } @@ -47,9 +46,8 @@ public abstract class UnmodifiableListIterator<E> * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void set(E e) { + @Override public final void set(E e) { throw new UnsupportedOperationException(); } } diff --git a/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java b/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java deleted file mode 100644 index 4b353cb..0000000 --- a/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2012 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 com.google.common.annotations.GwtCompatible; -import com.google.common.collect.Multisets.UnmodifiableMultiset; - -import java.util.Comparator; -import java.util.NavigableSet; - -/** - * Implementation of {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}, - * split out into its own file so it can be GWT emulated (to deal with the differing - * elementSet() types in GWT and non-GWT). - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -final class UnmodifiableSortedMultiset<E> - extends UnmodifiableMultiset<E> implements SortedMultiset<E> { - UnmodifiableSortedMultiset(SortedMultiset<E> delegate) { - super(delegate); - } - - @Override - protected SortedMultiset<E> delegate() { - return (SortedMultiset<E>) super.delegate(); - } - - @Override - public Comparator<? super E> comparator() { - return delegate().comparator(); - } - - @Override - NavigableSet<E> createElementSet() { - return Sets.unmodifiableNavigableSet(delegate().elementSet()); - } - - @Override - public NavigableSet<E> elementSet() { - return (NavigableSet<E>) super.elementSet(); - } - - private transient UnmodifiableSortedMultiset<E> descendingMultiset; - - @Override - public SortedMultiset<E> descendingMultiset() { - UnmodifiableSortedMultiset<E> result = descendingMultiset; - if (result == null) { - result = new UnmodifiableSortedMultiset<E>( - delegate().descendingMultiset()); - result.descendingMultiset = this; - return descendingMultiset = result; - } - return result; - } - - @Override - public Entry<E> firstEntry() { - return delegate().firstEntry(); - } - - @Override - public Entry<E> lastEntry() { - return delegate().lastEntry(); - } - - @Override - public Entry<E> pollFirstEntry() { - throw new UnsupportedOperationException(); - } - - @Override - public Entry<E> pollLastEntry() { - throw new UnsupportedOperationException(); - } - - @Override - public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { - return Multisets.unmodifiableSortedMultiset( - delegate().headMultiset(upperBound, boundType)); - } - - @Override - public SortedMultiset<E> subMultiset( - E lowerBound, BoundType lowerBoundType, - E upperBound, BoundType upperBoundType) { - return Multisets.unmodifiableSortedMultiset(delegate().subMultiset( - lowerBound, lowerBoundType, upperBound, upperBoundType)); - } - - @Override - public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { - return Multisets.unmodifiableSortedMultiset( - delegate().tailMultiset(lowerBound, boundType)); - } - - private static final long serialVersionUID = 0; -}
\ No newline at end of file diff --git a/guava/src/com/google/common/collect/UsingToStringOrdering.java b/guava/src/com/google/common/collect/UsingToStringOrdering.java index adb6aa7..d1c9feb 100644 --- a/guava/src/com/google/common/collect/UsingToStringOrdering.java +++ b/guava/src/com/google/common/collect/UsingToStringOrdering.java @@ -20,10 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.io.Serializable; -/** - * An ordering that uses the natural order of the string representation of the - * values. - */ +/** An ordering that uses the reverse of the natural order of the values. */ @GwtCompatible(serializable = true) final class UsingToStringOrdering extends Ordering<Object> implements Serializable { diff --git a/guava/src/com/google/common/collect/WellBehavedMap.java b/guava/src/com/google/common/collect/WellBehavedMap.java index c68cc5e..e8aa1f6 100644 --- a/guava/src/com/google/common/collect/WellBehavedMap.java +++ b/guava/src/com/google/common/collect/WellBehavedMap.java @@ -18,35 +18,32 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.util.Iterator; import java.util.Map; import java.util.Set; /** - * Workaround for + * Workaround for * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312706"> * EnumMap bug</a>. If you want to pass an {@code EnumMap}, with the * intention of using its {@code entrySet()} method, you should - * wrap the {@code EnumMap} in this class instead. - * - * <p>This class is not thread-safe even if the underlying map is. - * + * wrap the {@code EnumMap} in this class instead. + * * @author Dimitris Andreou */ @GwtCompatible final class WellBehavedMap<K, V> extends ForwardingMap<K, V> { private final Map<K, V> delegate; private Set<Entry<K, V>> entrySet; - + private WellBehavedMap(Map<K, V> delegate) { this.delegate = delegate; } - + /** * Wraps the given map into a {@code WellBehavedEntriesMap}, which - * intercepts its {@code entrySet()} method by taking the + * intercepts its {@code entrySet()} method by taking the * {@code Set<K> keySet()} and transforming it to - * {@code Set<Entry<K, V>>}. All other invocations are delegated as-is. + * {@code Set<Entry<K, V>>}. All other invocations are delegated as-is. */ static <K, V> WellBehavedMap<K, V> wrap(Map<K, V> delegate) { return new WellBehavedMap<K, V>(delegate); @@ -61,38 +58,34 @@ final class WellBehavedMap<K, V> extends ForwardingMap<K, V> { if (es != null) { return es; } - return entrySet = new EntrySet(); + return entrySet = Sets.transform( + delegate.keySet(), new KeyToEntryConverter<K, V>(this)); } - - private final class EntrySet extends Maps.EntrySet<K, V> { - @Override - Map<K, V> map() { - return WellBehavedMap.this; + + private static class KeyToEntryConverter<K, V> + extends Sets.InvertibleFunction<K, Map.Entry<K, V>> { + final Map<K, V> map; + + KeyToEntryConverter(Map<K, V> map) { + this.map = map; } - @Override - public Iterator<Entry<K, V>> iterator() { - return new TransformedIterator<K, Entry<K, V>>(keySet().iterator()) { - @Override - Entry<K, V> transform(final K key) { - return new AbstractMapEntry<K, V>() { - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return get(key); - } - - @Override - public V setValue(V value) { - return put(key, value); - } - }; + @Override public Map.Entry<K, V> apply(final K key) { + return new AbstractMapEntry<K, V>() { + @Override public K getKey() { + return key; + } + @Override public V getValue() { + return map.get(key); + } + @Override public V setValue(V value) { + return map.put(key, value); } }; } + + @Override public K invert(Map.Entry<K, V> entry) { + return entry.getKey(); + } } } diff --git a/guava/src/com/google/common/collect/package-info.java b/guava/src/com/google/common/collect/package-info.java index 3ffbad9..f5c833c 100644 --- a/guava/src/com/google/common/collect/package-info.java +++ b/guava/src/com/google/common/collect/package-info.java @@ -85,7 +85,7 @@ * <ul> * <li>{@link com.google.common.collect.ImmutableSet} * <li>{@link com.google.common.collect.ImmutableSortedSet} - * <li>{@link com.google.common.collect.ContiguousSet} (see {@code Range}) + * <li>{@link com.google.common.collect.ContiguousSet} (see {@code Ranges}) * </ul> * * <h3>of {@link java.util.Map}</h3> @@ -147,10 +147,10 @@ * <li>{@link com.google.common.collect.Iterables} * <li>{@link com.google.common.collect.Lists} * <li>{@link com.google.common.collect.Maps} - * <li>{@link com.google.common.collect.Queues} * <li>{@link com.google.common.collect.Sets} * <li>{@link com.google.common.collect.Multisets} * <li>{@link com.google.common.collect.Multimaps} + * <li>{@link com.google.common.collect.SortedMaps} * <li>{@link com.google.common.collect.Tables} * <li>{@link com.google.common.collect.ObjectArrays} * </ul> @@ -166,7 +166,7 @@ * * <ul> * <li>{@link com.google.common.collect.AbstractIterator} - * <li>{@link com.google.common.collect.AbstractSequentialIterator} + * <li>{@link com.google.common.collect.AbstractLinkedIterator} * <li>{@link com.google.common.collect.ImmutableCollection} * <li>{@link com.google.common.collect.UnmodifiableIterator} * <li>{@link com.google.common.collect.UnmodifiableListIterator} @@ -176,6 +176,7 @@ * * <ul> * <li>{@link com.google.common.collect.Range} + * <li>{@link com.google.common.collect.Ranges} * <li>{@link com.google.common.collect.DiscreteDomain} * <li>{@link com.google.common.collect.DiscreteDomains} * <li>{@link com.google.common.collect.ContiguousSet} @@ -209,13 +210,12 @@ * <li>{@link com.google.common.collect.ForwardingMapEntry} * <li>{@link com.google.common.collect.ForwardingMultimap} * <li>{@link com.google.common.collect.ForwardingMultiset} - * <li>{@link com.google.common.collect.ForwardingNavigableMap} - * <li>{@link com.google.common.collect.ForwardingNavigableSet} * <li>{@link com.google.common.collect.ForwardingObject} * <li>{@link com.google.common.collect.ForwardingQueue} * <li>{@link com.google.common.collect.ForwardingSet} * <li>{@link com.google.common.collect.ForwardingSetMultimap} * <li>{@link com.google.common.collect.ForwardingSortedMap} + * <li>{@link com.google.common.collect.ForwardingSortedMultiset} * <li>{@link com.google.common.collect.ForwardingSortedSet} * <li>{@link com.google.common.collect.ForwardingSortedSetMultimap} * <li>{@link com.google.common.collect.ForwardingTable} |