Sort methods.
This commit is contained in:
parent
26bebdafe4
commit
15a3044690
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.commons.collections4;
|
package org.apache.commons.collections4;
|
||||||
|
|
||||||
import java.util.AbstractSet;
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -47,12 +48,54 @@ import org.apache.commons.collections4.set.UnmodifiableSortedSet;
|
||||||
public class SetUtils {
|
public class SetUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a typed empty unmodifiable Set.
|
* An unmodifiable <b>view</b> of a set that may be backed by other sets.
|
||||||
|
* <p>
|
||||||
|
* If the decorated sets change, this view will change as well. The contents
|
||||||
|
* of this view can be transferred to another instance via the {@link #copyInto(Set)}
|
||||||
|
* and {@link #toSet()} methods.
|
||||||
|
*
|
||||||
* @param <E> the element type
|
* @param <E> the element type
|
||||||
* @return an empty Set
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public static <E> Set<E> emptySet() {
|
public static abstract class SetView<E> extends AbstractSet<E> {
|
||||||
return Collections.<E>emptySet();
|
|
||||||
|
/**
|
||||||
|
* Copies the contents of this view into the provided set.
|
||||||
|
*
|
||||||
|
* @param <S> the set type
|
||||||
|
* @param set the set for copying the contents
|
||||||
|
*/
|
||||||
|
public <S extends Set<E>> void copyInto(final S set) {
|
||||||
|
CollectionUtils.addAll(set, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an iterator for this view; the returned iterator is
|
||||||
|
* not required to be unmodifiable.
|
||||||
|
* @return a new iterator for this view
|
||||||
|
*/
|
||||||
|
protected abstract Iterator<E> createIterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return IteratorUtils.unmodifiableIterator(createIterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return IteratorUtils.size(iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new set containing the contents of this view.
|
||||||
|
*
|
||||||
|
* @return a new set containing all elements of this view
|
||||||
|
*/
|
||||||
|
public Set<E> toSet() {
|
||||||
|
final Set<E> set = new HashSet<>(size());
|
||||||
|
copyInto(set);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,410 +106,6 @@ public class SetUtils {
|
||||||
public static final SortedSet EMPTY_SORTED_SET =
|
public static final SortedSet EMPTY_SORTED_SET =
|
||||||
UnmodifiableSortedSet.unmodifiableSortedSet(new TreeSet<>());
|
UnmodifiableSortedSet.unmodifiableSortedSet(new TreeSet<>());
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a typed empty unmodifiable sorted set.
|
|
||||||
* @param <E> the element type
|
|
||||||
* @return an empty sorted Set
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked") // empty set is OK for any type
|
|
||||||
public static <E> SortedSet<E> emptySortedSet() {
|
|
||||||
return EMPTY_SORTED_SET;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>SetUtils</code> should not normally be instantiated.
|
|
||||||
*/
|
|
||||||
private SetUtils() {}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an immutable empty set if the argument is <code>null</code>,
|
|
||||||
* or the argument itself otherwise.
|
|
||||||
*
|
|
||||||
* @param <T> the element type
|
|
||||||
* @param set the set, possibly <code>null</code>
|
|
||||||
* @return an empty set if the argument is <code>null</code>
|
|
||||||
*/
|
|
||||||
public static <T> Set<T> emptyIfNull(final Set<T> set) {
|
|
||||||
return set == null ? Collections.<T>emptySet() : set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests two sets for equality as per the <code>equals()</code> contract
|
|
||||||
* in {@link java.util.Set#equals(java.lang.Object)}.
|
|
||||||
* <p>
|
|
||||||
* This method is useful for implementing <code>Set</code> when you cannot
|
|
||||||
* extend AbstractSet. The method takes Collection instances to enable other
|
|
||||||
* collection types to use the Set implementation algorithm.
|
|
||||||
* <p>
|
|
||||||
* The relevant text (slightly paraphrased as this is a static method) is:
|
|
||||||
* <blockquote>
|
|
||||||
* <p>Two sets are considered equal if they have
|
|
||||||
* the same size, and every member of the first set is contained in
|
|
||||||
* the second. This ensures that the {@code equals} method works
|
|
||||||
* properly across different implementations of the {@code Set}
|
|
||||||
* interface.</p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This implementation first checks if the two sets are the same object:
|
|
||||||
* if so it returns {@code true}. Then, it checks if the two sets are
|
|
||||||
* identical in size; if not, it returns false. If so, it returns
|
|
||||||
* {@code a.containsAll((Collection) b)}.</p>
|
|
||||||
* </blockquote>
|
|
||||||
*
|
|
||||||
* @see java.util.Set
|
|
||||||
* @param set1 the first set, may be null
|
|
||||||
* @param set2 the second set, may be null
|
|
||||||
* @return whether the sets are equal by value comparison
|
|
||||||
*/
|
|
||||||
public static boolean isEqualSet(final Collection<?> set1, final Collection<?> set2) {
|
|
||||||
if (set1 == set2) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (set1 == null || set2 == null || set1.size() != set2.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return set1.containsAll(set2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a hash code using the algorithm specified in
|
|
||||||
* {@link java.util.Set#hashCode()}.
|
|
||||||
* <p>
|
|
||||||
* This method is useful for implementing <code>Set</code> when you cannot
|
|
||||||
* extend AbstractSet. The method takes Collection instances to enable other
|
|
||||||
* collection types to use the Set implementation algorithm.
|
|
||||||
*
|
|
||||||
* @param <T> the element type
|
|
||||||
* @see java.util.Set#hashCode()
|
|
||||||
* @param set the set to calculate the hash code for, may be null
|
|
||||||
* @return the hash code
|
|
||||||
*/
|
|
||||||
public static <T> int hashCodeForSet(final Collection<T> set) {
|
|
||||||
if (set == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hashCode = 0;
|
|
||||||
for (final T obj : set) {
|
|
||||||
if (obj != null) {
|
|
||||||
hashCode += obj.hashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new hash set that matches elements based on <code>==</code> not
|
|
||||||
* <code>equals()</code>.
|
|
||||||
* <p>
|
|
||||||
* <strong>This set will violate the detail of various Set contracts.</strong>
|
|
||||||
* As a general rule, don't compare this set to other sets. In particular, you can't
|
|
||||||
* use decorators like {@link ListOrderedSet} on it, which silently assume that these
|
|
||||||
* contracts are fulfilled.
|
|
||||||
* <p>
|
|
||||||
* <strong>Note that the returned set is not synchronized and is not thread-safe.</strong>
|
|
||||||
* If you wish to use this set from multiple threads concurrently, you must use
|
|
||||||
* appropriate synchronization. The simplest approach is to wrap this map
|
|
||||||
* using {@link java.util.Collections#synchronizedSet(Set)}. This class may throw
|
|
||||||
* exceptions when accessed by concurrent threads without synchronization.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @return a new identity hash set
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> newIdentityHashSet() {
|
|
||||||
return Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* Returns a synchronized set backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* You must manually synchronize on the returned set's iterator to
|
|
||||||
* avoid non-deterministic behavior:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* Set s = SetUtils.synchronizedSet(mySet);
|
|
||||||
* synchronized (s) {
|
|
||||||
* Iterator i = s.iterator();
|
|
||||||
* while (i.hasNext()) {
|
|
||||||
* process (i.next());
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* This method is just a wrapper for {@link Collections#synchronizedSet(Set)}.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to synchronize, must not be null
|
|
||||||
* @return a synchronized set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> synchronizedSet(final Set<E> set) {
|
|
||||||
return Collections.synchronizedSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an unmodifiable set backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* This method uses the implementation in the decorators subpackage.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to make unmodifiable, must not be null
|
|
||||||
* @return an unmodifiable set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> unmodifiableSet(final Set<? extends E> set) {
|
|
||||||
return UnmodifiableSet.unmodifiableSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a predicated (validating) set backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* Only objects that pass the test in the given predicate can be added to the set.
|
|
||||||
* Trying to add an invalid object results in an IllegalArgumentException.
|
|
||||||
* It is important not to use the original set after invoking this method,
|
|
||||||
* as it is a backdoor for adding invalid objects.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to predicate, must not be null
|
|
||||||
* @param predicate the predicate for the set, must not be null
|
|
||||||
* @return a predicated set backed by the given set
|
|
||||||
* @throws NullPointerException if the set or predicate is null
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> predicatedSet(final Set<E> set, final Predicate<? super E> predicate) {
|
|
||||||
return PredicatedSet.predicatedSet(set, predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a transformed set backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* Each object is passed through the transformer as it is added to the
|
|
||||||
* Set. It is important not to use the original set after invoking this
|
|
||||||
* method, as it is a backdoor for adding untransformed objects.
|
|
||||||
* <p>
|
|
||||||
* Existing entries in the specified set will not be transformed.
|
|
||||||
* If you want that behaviour, see {@link TransformedSet#transformedSet}.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to transform, must not be null
|
|
||||||
* @param transformer the transformer for the set, must not be null
|
|
||||||
* @return a transformed set backed by the given set
|
|
||||||
* @throws NullPointerException if the set or transformer is null
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> transformedSet(final Set<E> set,
|
|
||||||
final Transformer<? super E, ? extends E> transformer) {
|
|
||||||
return TransformedSet.transformingSet(set, transformer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a set that maintains the order of elements that are added
|
|
||||||
* backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* If an element is added twice, the order is determined by the first add.
|
|
||||||
* The order is observed through the iterator or toArray.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to order, must not be null
|
|
||||||
* @return an ordered set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
*/
|
|
||||||
public static <E> Set<E> orderedSet(final Set<E> set) {
|
|
||||||
return ListOrderedSet.listOrderedSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortedSet
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* Returns a synchronized sorted set backed by the given sorted set.
|
|
||||||
* <p>
|
|
||||||
* You must manually synchronize on the returned set's iterator to
|
|
||||||
* avoid non-deterministic behavior:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* Set s = SetUtils.synchronizedSortedSet(mySet);
|
|
||||||
* synchronized (s) {
|
|
||||||
* Iterator i = s.iterator();
|
|
||||||
* while (i.hasNext()) {
|
|
||||||
* process (i.next());
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* This method is just a wrapper for {@link Collections#synchronizedSortedSet(SortedSet)}.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the sorted set to synchronize, must not be null
|
|
||||||
* @return a synchronized set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> synchronizedSortedSet(final SortedSet<E> set) {
|
|
||||||
return Collections.synchronizedSortedSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an unmodifiable sorted set backed by the given sorted set.
|
|
||||||
* <p>
|
|
||||||
* This method uses the implementation in the decorators subpackage.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the sorted set to make unmodifiable, must not be null
|
|
||||||
* @return an unmodifiable set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> unmodifiableSortedSet(final SortedSet<E> set) {
|
|
||||||
return UnmodifiableSortedSet.unmodifiableSortedSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a predicated (validating) sorted set backed by the given sorted set.
|
|
||||||
* <p>
|
|
||||||
* Only objects that pass the test in the given predicate can be added to the set.
|
|
||||||
* Trying to add an invalid object results in an IllegalArgumentException.
|
|
||||||
* It is important not to use the original set after invoking this method,
|
|
||||||
* as it is a backdoor for adding invalid objects.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the sorted set to predicate, must not be null
|
|
||||||
* @param predicate the predicate for the sorted set, must not be null
|
|
||||||
* @return a predicated sorted set backed by the given sorted set
|
|
||||||
* @throws NullPointerException if the set or predicate is null
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> predicatedSortedSet(final SortedSet<E> set,
|
|
||||||
final Predicate<? super E> predicate) {
|
|
||||||
return PredicatedSortedSet.predicatedSortedSet(set, predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a transformed sorted set backed by the given set.
|
|
||||||
* <p>
|
|
||||||
* Each object is passed through the transformer as it is added to the
|
|
||||||
* Set. It is important not to use the original set after invoking this
|
|
||||||
* method, as it is a backdoor for adding untransformed objects.
|
|
||||||
* <p>
|
|
||||||
* Existing entries in the specified set will not be transformed.
|
|
||||||
* If you want that behaviour, see {@link TransformedSortedSet#transformedSortedSet}.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the set to transform, must not be null
|
|
||||||
* @param transformer the transformer for the set, must not be null
|
|
||||||
* @return a transformed set backed by the given set
|
|
||||||
* @throws NullPointerException if the set or transformer is null
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> transformedSortedSet(final SortedSet<E> set,
|
|
||||||
final Transformer<? super E, ? extends E> transformer) {
|
|
||||||
return TransformedSortedSet.transformingSortedSet(set, transformer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NavigableSet
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* Returns an unmodifiable navigable set backed by the given navigable set.
|
|
||||||
* <p>
|
|
||||||
* This method uses the implementation in the decorators subpackage.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the navigable set to make unmodifiable, must not be null
|
|
||||||
* @return an unmodifiable set backed by the given set
|
|
||||||
* @throws NullPointerException if the set is null
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> unmodifiableNavigableSet(final NavigableSet<E> set) {
|
|
||||||
return UnmodifiableNavigableSet.unmodifiableNavigableSet(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a predicated (validating) navigable set backed by the given navigable set.
|
|
||||||
* <p>
|
|
||||||
* Only objects that pass the test in the given predicate can be added to the set.
|
|
||||||
* Trying to add an invalid object results in an IllegalArgumentException.
|
|
||||||
* It is important not to use the original set after invoking this method,
|
|
||||||
* as it is a backdoor for adding invalid objects.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the navigable set to predicate, must not be null
|
|
||||||
* @param predicate the predicate for the navigable set, must not be null
|
|
||||||
* @return a predicated navigable set backed by the given navigable set
|
|
||||||
* @throws NullPointerException if the set or predicate is null
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> predicatedNavigableSet(final NavigableSet<E> set,
|
|
||||||
final Predicate<? super E> predicate) {
|
|
||||||
return PredicatedNavigableSet.predicatedNavigableSet(set, predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a transformed navigable set backed by the given navigable set.
|
|
||||||
* <p>
|
|
||||||
* Each object is passed through the transformer as it is added to the
|
|
||||||
* Set. It is important not to use the original set after invoking this
|
|
||||||
* method, as it is a backdoor for adding untransformed objects.
|
|
||||||
* <p>
|
|
||||||
* Existing entries in the specified set will not be transformed.
|
|
||||||
* If you want that behaviour, see {@link TransformedNavigableSet#transformedNavigableSet}.
|
|
||||||
*
|
|
||||||
* @param <E> the element type
|
|
||||||
* @param set the navigable set to transform, must not be null
|
|
||||||
* @param transformer the transformer for the set, must not be null
|
|
||||||
* @return a transformed set backed by the given set
|
|
||||||
* @throws NullPointerException if the set or transformer is null
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> SortedSet<E> transformedNavigableSet(final NavigableSet<E> set,
|
|
||||||
final Transformer<? super E, ? extends E> transformer) {
|
|
||||||
return TransformedNavigableSet.transformingNavigableSet(set, transformer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set operations
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a unmodifiable <b>view</b> of the union of the given {@link Set}s.
|
|
||||||
* <p>
|
|
||||||
* The returned view contains all elements of {@code a} and {@code b}.
|
|
||||||
*
|
|
||||||
* @param <E> the generic type that is able to represent the types contained
|
|
||||||
* in both input sets.
|
|
||||||
* @param a the first set, must not be null
|
|
||||||
* @param b the second set, must not be null
|
|
||||||
* @return a view of the union of the two set
|
|
||||||
* @throws NullPointerException if either input set is null
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> SetView<E> union(final Set<? extends E> a, final Set<? extends E> b) {
|
|
||||||
if (a == null || b == null) {
|
|
||||||
throw new NullPointerException("Sets must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final SetView<E> bMinusA = difference(b, a);
|
|
||||||
|
|
||||||
return new SetView<E>() {
|
|
||||||
@Override
|
|
||||||
public boolean contains(final Object o) {
|
|
||||||
return a.contains(o) || b.contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<E> createIterator() {
|
|
||||||
return IteratorUtils.chainedIterator(a.iterator(), bMinusA.iterator());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return a.isEmpty() && b.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return a.size() + bMinusA.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a unmodifiable <b>view</b> containing the difference of the given
|
* Returns a unmodifiable <b>view</b> containing the difference of the given
|
||||||
* {@link Set}s, denoted by {@code a \ b} (or {@code a - b}).
|
* {@link Set}s, denoted by {@code a \ b} (or {@code a - b}).
|
||||||
|
@ -506,44 +145,6 @@ public class SetUtils {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a unmodifiable <b>view</b> of the intersection of the given {@link Set}s.
|
|
||||||
* <p>
|
|
||||||
* The returned view contains all elements that are members of both input sets
|
|
||||||
* ({@code a} and {@code b}).
|
|
||||||
*
|
|
||||||
* @param <E> the generic type that is able to represent the types contained
|
|
||||||
* in both input sets.
|
|
||||||
* @param a the first set, must not be null
|
|
||||||
* @param b the second set, must not be null
|
|
||||||
* @return a view of the intersection of the two sets
|
|
||||||
* @since 4.1
|
|
||||||
*/
|
|
||||||
public static <E> SetView<E> intersection(final Set<? extends E> a, final Set<? extends E> b) {
|
|
||||||
if (a == null || b == null) {
|
|
||||||
throw new NullPointerException("Sets must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final Predicate<E> containedInB = new Predicate<E>() {
|
|
||||||
@Override
|
|
||||||
public boolean evaluate(final E object) {
|
|
||||||
return b.contains(object);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return new SetView<E>() {
|
|
||||||
@Override
|
|
||||||
public boolean contains(final Object o) {
|
|
||||||
return a.contains(o) && b.contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<E> createIterator() {
|
|
||||||
return IteratorUtils.filteredIterator(a.iterator(), containedInB);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a unmodifiable <b>view</b> of the symmetric difference of the given
|
* Returns a unmodifiable <b>view</b> of the symmetric difference of the given
|
||||||
* {@link Set}s.
|
* {@link Set}s.
|
||||||
|
@ -592,53 +193,452 @@ public class SetUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unmodifiable <b>view</b> of a set that may be backed by other sets.
|
* Returns an immutable empty set if the argument is <code>null</code>,
|
||||||
* <p>
|
* or the argument itself otherwise.
|
||||||
* If the decorated sets change, this view will change as well. The contents
|
|
||||||
* of this view can be transferred to another instance via the {@link #copyInto(Set)}
|
|
||||||
* and {@link #toSet()} methods.
|
|
||||||
*
|
*
|
||||||
* @param <E> the element type
|
* @param <T> the element type
|
||||||
* @since 4.1
|
* @param set the set, possibly <code>null</code>
|
||||||
|
* @return an empty set if the argument is <code>null</code>
|
||||||
*/
|
*/
|
||||||
public static abstract class SetView<E> extends AbstractSet<E> {
|
public static <T> Set<T> emptyIfNull(final Set<T> set) {
|
||||||
|
return set == null ? Collections.<T>emptySet() : set;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
//-----------------------------------------------------------------------
|
||||||
public Iterator<E> iterator() {
|
|
||||||
return IteratorUtils.unmodifiableIterator(createIterator());
|
/**
|
||||||
|
* Get a typed empty unmodifiable Set.
|
||||||
|
* @param <E> the element type
|
||||||
|
* @return an empty Set
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> emptySet() {
|
||||||
|
return Collections.<E>emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an iterator for this view; the returned iterator is
|
* Get a typed empty unmodifiable sorted set.
|
||||||
* not required to be unmodifiable.
|
* @param <E> the element type
|
||||||
* @return a new iterator for this view
|
* @return an empty sorted Set
|
||||||
*/
|
*/
|
||||||
protected abstract Iterator<E> createIterator();
|
@SuppressWarnings("unchecked") // empty set is OK for any type
|
||||||
|
public static <E> SortedSet<E> emptySortedSet() {
|
||||||
|
return EMPTY_SORTED_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a hash code using the algorithm specified in
|
||||||
|
* {@link java.util.Set#hashCode()}.
|
||||||
|
* <p>
|
||||||
|
* This method is useful for implementing <code>Set</code> when you cannot
|
||||||
|
* extend AbstractSet. The method takes Collection instances to enable other
|
||||||
|
* collection types to use the Set implementation algorithm.
|
||||||
|
*
|
||||||
|
* @param <T> the element type
|
||||||
|
* @see java.util.Set#hashCode()
|
||||||
|
* @param set the set to calculate the hash code for, may be null
|
||||||
|
* @return the hash code
|
||||||
|
*/
|
||||||
|
public static <T> int hashCodeForSet(final Collection<T> set) {
|
||||||
|
if (set == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hashCode = 0;
|
||||||
|
for (final T obj : set) {
|
||||||
|
if (obj != null) {
|
||||||
|
hashCode += obj.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unmodifiable <b>view</b> of the intersection of the given {@link Set}s.
|
||||||
|
* <p>
|
||||||
|
* The returned view contains all elements that are members of both input sets
|
||||||
|
* ({@code a} and {@code b}).
|
||||||
|
*
|
||||||
|
* @param <E> the generic type that is able to represent the types contained
|
||||||
|
* in both input sets.
|
||||||
|
* @param a the first set, must not be null
|
||||||
|
* @param b the second set, must not be null
|
||||||
|
* @return a view of the intersection of the two sets
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> SetView<E> intersection(final Set<? extends E> a, final Set<? extends E> b) {
|
||||||
|
if (a == null || b == null) {
|
||||||
|
throw new NullPointerException("Sets must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Predicate<E> containedInB = new Predicate<E>() {
|
||||||
|
@Override
|
||||||
|
public boolean evaluate(final E object) {
|
||||||
|
return b.contains(object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new SetView<E>() {
|
||||||
|
@Override
|
||||||
|
public boolean contains(final Object o) {
|
||||||
|
return a.contains(o) && b.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> createIterator() {
|
||||||
|
return IteratorUtils.filteredIterator(a.iterator(), containedInB);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests two sets for equality as per the <code>equals()</code> contract
|
||||||
|
* in {@link java.util.Set#equals(java.lang.Object)}.
|
||||||
|
* <p>
|
||||||
|
* This method is useful for implementing <code>Set</code> when you cannot
|
||||||
|
* extend AbstractSet. The method takes Collection instances to enable other
|
||||||
|
* collection types to use the Set implementation algorithm.
|
||||||
|
* <p>
|
||||||
|
* The relevant text (slightly paraphrased as this is a static method) is:
|
||||||
|
* <blockquote>
|
||||||
|
* <p>Two sets are considered equal if they have
|
||||||
|
* the same size, and every member of the first set is contained in
|
||||||
|
* the second. This ensures that the {@code equals} method works
|
||||||
|
* properly across different implementations of the {@code Set}
|
||||||
|
* interface.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This implementation first checks if the two sets are the same object:
|
||||||
|
* if so it returns {@code true}. Then, it checks if the two sets are
|
||||||
|
* identical in size; if not, it returns false. If so, it returns
|
||||||
|
* {@code a.containsAll((Collection) b)}.</p>
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @see java.util.Set
|
||||||
|
* @param set1 the first set, may be null
|
||||||
|
* @param set2 the second set, may be null
|
||||||
|
* @return whether the sets are equal by value comparison
|
||||||
|
*/
|
||||||
|
public static boolean isEqualSet(final Collection<?> set1, final Collection<?> set2) {
|
||||||
|
if (set1 == set2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (set1 == null || set2 == null || set1.size() != set2.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return set1.containsAll(set2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new hash set that matches elements based on <code>==</code> not
|
||||||
|
* <code>equals()</code>.
|
||||||
|
* <p>
|
||||||
|
* <strong>This set will violate the detail of various Set contracts.</strong>
|
||||||
|
* As a general rule, don't compare this set to other sets. In particular, you can't
|
||||||
|
* use decorators like {@link ListOrderedSet} on it, which silently assume that these
|
||||||
|
* contracts are fulfilled.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note that the returned set is not synchronized and is not thread-safe.</strong>
|
||||||
|
* If you wish to use this set from multiple threads concurrently, you must use
|
||||||
|
* appropriate synchronization. The simplest approach is to wrap this map
|
||||||
|
* using {@link java.util.Collections#synchronizedSet(Set)}. This class may throw
|
||||||
|
* exceptions when accessed by concurrent threads without synchronization.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @return a new identity hash set
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> newIdentityHashSet() {
|
||||||
|
return Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set that maintains the order of elements that are added
|
||||||
|
* backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* If an element is added twice, the order is determined by the first add.
|
||||||
|
* The order is observed through the iterator or toArray.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the set to order, must not be null
|
||||||
|
* @return an ordered set backed by the given set
|
||||||
|
* @throws NullPointerException if the set is null
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> orderedSet(final Set<E> set) {
|
||||||
|
return ListOrderedSet.listOrderedSet(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicated (validating) navigable set backed by the given navigable set.
|
||||||
|
* <p>
|
||||||
|
* Only objects that pass the test in the given predicate can be added to the set.
|
||||||
|
* Trying to add an invalid object results in an IllegalArgumentException.
|
||||||
|
* It is important not to use the original set after invoking this method,
|
||||||
|
* as it is a backdoor for adding invalid objects.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the navigable set to predicate, must not be null
|
||||||
|
* @param predicate the predicate for the navigable set, must not be null
|
||||||
|
* @return a predicated navigable set backed by the given navigable set
|
||||||
|
* @throws NullPointerException if the set or predicate is null
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> predicatedNavigableSet(final NavigableSet<E> set,
|
||||||
|
final Predicate<? super E> predicate) {
|
||||||
|
return PredicatedNavigableSet.predicatedNavigableSet(set, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicated (validating) set backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* Only objects that pass the test in the given predicate can be added to the set.
|
||||||
|
* Trying to add an invalid object results in an IllegalArgumentException.
|
||||||
|
* It is important not to use the original set after invoking this method,
|
||||||
|
* as it is a backdoor for adding invalid objects.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the set to predicate, must not be null
|
||||||
|
* @param predicate the predicate for the set, must not be null
|
||||||
|
* @return a predicated set backed by the given set
|
||||||
|
* @throws NullPointerException if the set or predicate is null
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> predicatedSet(final Set<E> set, final Predicate<? super E> predicate) {
|
||||||
|
return PredicatedSet.predicatedSet(set, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a predicated (validating) sorted set backed by the given sorted set.
|
||||||
|
* <p>
|
||||||
|
* Only objects that pass the test in the given predicate can be added to the set.
|
||||||
|
* Trying to add an invalid object results in an IllegalArgumentException.
|
||||||
|
* It is important not to use the original set after invoking this method,
|
||||||
|
* as it is a backdoor for adding invalid objects.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the sorted set to predicate, must not be null
|
||||||
|
* @param predicate the predicate for the sorted set, must not be null
|
||||||
|
* @return a predicated sorted set backed by the given sorted set
|
||||||
|
* @throws NullPointerException if the set or predicate is null
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> predicatedSortedSet(final SortedSet<E> set,
|
||||||
|
final Predicate<? super E> predicate) {
|
||||||
|
return PredicatedSortedSet.predicatedSortedSet(set, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Returns a synchronized set backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* You must manually synchronize on the returned set's iterator to
|
||||||
|
* avoid non-deterministic behavior:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Set s = SetUtils.synchronizedSet(mySet);
|
||||||
|
* synchronized (s) {
|
||||||
|
* Iterator i = s.iterator();
|
||||||
|
* while (i.hasNext()) {
|
||||||
|
* process (i.next());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* This method is just a wrapper for {@link Collections#synchronizedSet(Set)}.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the set to synchronize, must not be null
|
||||||
|
* @return a synchronized set backed by the given set
|
||||||
|
* @throws NullPointerException if the set is null
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> synchronizedSet(final Set<E> set) {
|
||||||
|
return Collections.synchronizedSet(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortedSet
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Returns a synchronized sorted set backed by the given sorted set.
|
||||||
|
* <p>
|
||||||
|
* You must manually synchronize on the returned set's iterator to
|
||||||
|
* avoid non-deterministic behavior:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Set s = SetUtils.synchronizedSortedSet(mySet);
|
||||||
|
* synchronized (s) {
|
||||||
|
* Iterator i = s.iterator();
|
||||||
|
* while (i.hasNext()) {
|
||||||
|
* process (i.next());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* This method is just a wrapper for {@link Collections#synchronizedSortedSet(SortedSet)}.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the sorted set to synchronize, must not be null
|
||||||
|
* @return a synchronized set backed by the given set
|
||||||
|
* @throws NullPointerException if the set is null
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> synchronizedSortedSet(final SortedSet<E> set) {
|
||||||
|
return Collections.synchronizedSortedSet(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a transformed navigable set backed by the given navigable set.
|
||||||
|
* <p>
|
||||||
|
* Each object is passed through the transformer as it is added to the
|
||||||
|
* Set. It is important not to use the original set after invoking this
|
||||||
|
* method, as it is a backdoor for adding untransformed objects.
|
||||||
|
* <p>
|
||||||
|
* Existing entries in the specified set will not be transformed.
|
||||||
|
* If you want that behaviour, see {@link TransformedNavigableSet#transformedNavigableSet}.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the navigable set to transform, must not be null
|
||||||
|
* @param transformer the transformer for the set, must not be null
|
||||||
|
* @return a transformed set backed by the given set
|
||||||
|
* @throws NullPointerException if the set or transformer is null
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> transformedNavigableSet(final NavigableSet<E> set,
|
||||||
|
final Transformer<? super E, ? extends E> transformer) {
|
||||||
|
return TransformedNavigableSet.transformingNavigableSet(set, transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a transformed set backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* Each object is passed through the transformer as it is added to the
|
||||||
|
* Set. It is important not to use the original set after invoking this
|
||||||
|
* method, as it is a backdoor for adding untransformed objects.
|
||||||
|
* <p>
|
||||||
|
* Existing entries in the specified set will not be transformed.
|
||||||
|
* If you want that behaviour, see {@link TransformedSet#transformedSet}.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the set to transform, must not be null
|
||||||
|
* @param transformer the transformer for the set, must not be null
|
||||||
|
* @return a transformed set backed by the given set
|
||||||
|
* @throws NullPointerException if the set or transformer is null
|
||||||
|
*/
|
||||||
|
public static <E> Set<E> transformedSet(final Set<E> set,
|
||||||
|
final Transformer<? super E, ? extends E> transformer) {
|
||||||
|
return TransformedSet.transformingSet(set, transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a transformed sorted set backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* Each object is passed through the transformer as it is added to the
|
||||||
|
* Set. It is important not to use the original set after invoking this
|
||||||
|
* method, as it is a backdoor for adding untransformed objects.
|
||||||
|
* <p>
|
||||||
|
* Existing entries in the specified set will not be transformed.
|
||||||
|
* If you want that behaviour, see {@link TransformedSortedSet#transformedSortedSet}.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the set to transform, must not be null
|
||||||
|
* @param transformer the transformer for the set, must not be null
|
||||||
|
* @return a transformed set backed by the given set
|
||||||
|
* @throws NullPointerException if the set or transformer is null
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> transformedSortedSet(final SortedSet<E> set,
|
||||||
|
final Transformer<? super E, ? extends E> transformer) {
|
||||||
|
return TransformedSortedSet.transformingSortedSet(set, transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unmodifiable <b>view</b> of the union of the given {@link Set}s.
|
||||||
|
* <p>
|
||||||
|
* The returned view contains all elements of {@code a} and {@code b}.
|
||||||
|
*
|
||||||
|
* @param <E> the generic type that is able to represent the types contained
|
||||||
|
* in both input sets.
|
||||||
|
* @param a the first set, must not be null
|
||||||
|
* @param b the second set, must not be null
|
||||||
|
* @return a view of the union of the two set
|
||||||
|
* @throws NullPointerException if either input set is null
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> SetView<E> union(final Set<? extends E> a, final Set<? extends E> b) {
|
||||||
|
if (a == null || b == null) {
|
||||||
|
throw new NullPointerException("Sets must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final SetView<E> bMinusA = difference(b, a);
|
||||||
|
|
||||||
|
return new SetView<E>() {
|
||||||
|
@Override
|
||||||
|
public boolean contains(final Object o) {
|
||||||
|
return a.contains(o) || b.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> createIterator() {
|
||||||
|
return IteratorUtils.chainedIterator(a.iterator(), bMinusA.iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return a.isEmpty() && b.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return IteratorUtils.size(iterator());
|
return a.size() + bMinusA.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set operations
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NavigableSet
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Returns an unmodifiable navigable set backed by the given navigable set.
|
||||||
|
* <p>
|
||||||
|
* This method uses the implementation in the decorators subpackage.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param set the navigable set to make unmodifiable, must not be null
|
||||||
|
* @return an unmodifiable set backed by the given set
|
||||||
|
* @throws NullPointerException if the set is null
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> SortedSet<E> unmodifiableNavigableSet(final NavigableSet<E> set) {
|
||||||
|
return UnmodifiableNavigableSet.unmodifiableNavigableSet(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the contents of this view into the provided set.
|
* Returns an unmodifiable set backed by the given set.
|
||||||
|
* <p>
|
||||||
|
* This method uses the implementation in the decorators subpackage.
|
||||||
*
|
*
|
||||||
* @param <S> the set type
|
* @param <E> the element type
|
||||||
* @param set the set for copying the contents
|
* @param set the set to make unmodifiable, must not be null
|
||||||
|
* @return an unmodifiable set backed by the given set
|
||||||
|
* @throws NullPointerException if the set is null
|
||||||
*/
|
*/
|
||||||
public <S extends Set<E>> void copyInto(final S set) {
|
public static <E> Set<E> unmodifiableSet(final Set<? extends E> set) {
|
||||||
CollectionUtils.addAll(set, this);
|
return UnmodifiableSet.unmodifiableSet(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new set containing the contents of this view.
|
* Create an unmodifiable set from the given items. If the passed var-args argument is {@code
|
||||||
*
|
* null}, then the method returns {@code null}.
|
||||||
* @return a new set containing all elements of this view
|
* @param <E> the element type
|
||||||
|
* @return a set
|
||||||
*/
|
*/
|
||||||
public Set<E> toSet() {
|
public static <E> Set<E> unmodifiableSetSet(final E... items) {
|
||||||
final Set<E> set = new HashSet<>(size());
|
if (items == null) {
|
||||||
copyInto(set);
|
return null;
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(items)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>SetUtils</code> should not normally be instantiated.
|
||||||
|
*/
|
||||||
|
private SetUtils() {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue