aboutsummaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/collect/Sets.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/collect/Sets.java')
-rw-r--r--guava/src/com/google/common/collect/Sets.java851
1 files changed, 237 insertions, 614 deletions
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;
- }
}