Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.plumelib.util.CollectionsPlume Maven / Gradle / Ivy
Go to download
Utility libraries for Java. Complements Guava, Apache Commons, etc.
// If you edit this file, you must also edit its tests.
package org.plumelib.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Random;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.Positive;
import org.checkerframework.checker.lock.qual.GuardSatisfied;
import org.checkerframework.checker.mustcall.qual.MustCallUnknown;
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.KeyForBottom;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.checkerframework.checker.nullness.qual.UnknownKeyFor;
import org.checkerframework.checker.signedness.qual.Signed;
import org.checkerframework.dataflow.qual.Pure;
/** Utility functions for Collections, ArrayList, Iterator, and Map. */
public final class CollectionsPlume {
/** This class is a collection of methods; it does not represent anything. */
private CollectionsPlume() {
throw new Error("do not instantiate");
}
/** The system-specific line separator string. */
private static final String lineSep = System.lineSeparator();
///////////////////////////////////////////////////////////////////////////
/// Collections
///
/**
* Returns true iff the list does not contain duplicate elements, according to {@code equals()}.
*
* The implementation uses O(n) time and O(n) space.
*
* @param the type of the elements
* @param a a list
* @return true iff a does not contain duplicate elements
*/
@SuppressWarnings({"allcheckers:purity", "lock"}) // side effect to local state (HashSet)
@Pure
public static boolean hasDuplicates(List a) {
HashSet hs = new HashSet<>();
if (a instanceof RandomAccess) {
for (int i = 0; i < a.size(); i++) {
T elt = a.get(i);
if (!hs.add(elt)) {
return true;
}
}
} else {
for (T elt : a) {
if (!hs.add(elt)) {
return true;
}
}
}
return false;
}
/**
* Returns true iff the list does not contain duplicate elements, according to {@code equals()}.
*
* The implementation uses O(n) time and O(n) space.
*
* @param the type of the elements
* @param a a list
* @return true iff a does not contain duplicate elements
*/
@Pure
public static boolean hasNoDuplicates(List a) {
return !hasDuplicates(a);
}
/**
* Returns true iff the list does not contain duplicate elements, according to {@code equals()}.
*
* The implementation uses O(n) time and O(n) space.
*
* @param the type of the elements
* @param a a list
* @return true iff a does not contain duplicate elements
* @deprecated use {@link #hasNoDuplicates(List)}
*/
@Deprecated // 2023-11-30
// @InlineMe(
// replacement = "CollectionsPlume.hasNoDuplicates(a)",
// imports = "org.plumelib.util.CollectionsPlume")
@Pure
public static boolean noDuplicates(List a) {
return hasNoDuplicates(a);
}
/**
* Returns a copy of the list (never the original list) with duplicates (according to {@code
* equals()}) removed, but retaining the original order. The argument is not modified.
*
* @param type of elements of the list
* @param l a list to remove duplicates from
* @return a copy of the list with duplicates removed
* @deprecated use {@link withoutDuplicates} or {@link withoutDuplicatesComparable}
*/
@Deprecated // 2021-03-28
public static List removeDuplicates(List l) {
HashSet hs = new LinkedHashSet<>(l);
List result = new ArrayList<>(hs);
return result;
}
/**
* Returns a copy of the list with duplicates (according to {@code equals()}) removed, but
* retaining the original order. May return its argument if its argument has no duplicates, but is
* not guaranteed to do so. The argument is not modified.
*
* If the element type implements {@link Comparable}, use {@link #withoutDuplicatesSorted} or
* {@link #withoutDuplicatesComparable}.
*
* @param the type of elements in {@code values}
* @param values a list of values
* @return the values, with duplicates removed
*/
public static List withoutDuplicates(List values) {
Set s = ArraySet.newArraySetOrLinkedHashSet(values);
if (values.size() == s.size()) {
return values;
} else {
return new ArrayList<>(s);
}
}
/**
* Returns a list with the same contents as its argument, but sorted and without duplicates
* (according to {@code equals()}). May return its argument if its argument is sorted and has no
* duplicates, but is not guaranteed to do so. The argument is not modified.
*
* This is like {@link #withoutDuplicates}, but requires the list elements to implement {@link
* Comparable}, and thus can be more efficient.
*
* @see #withoutDuplicatesComparable
* @param the type of elements in {@code values}
* @param values a list of values
* @return the values, with duplicates removed
*/
public static > List withoutDuplicatesSorted(List values) {
// This adds O(n) time cost, and has the benefit of sometimes avoiding allocating a TreeSet.
if (isSortedNoDuplicates(values)) {
return values;
}
Set set = new TreeSet<>(values);
return new ArrayList<>(set);
}
/**
* Returns a list with the same contents as its argument, but without duplicates. May return its
* argument if its argument has no duplicates, but is not guaranteed to do so. The argument is not
* modified.
*
* This is like {@link #withoutDuplicatesSorted}, but it is not guaranteed to return a sorted
* list. Thus, it is occasionally more efficient.
*
*
This is like {@link #withoutDuplicates}, but requires the list elements to implement {@link
* Comparable}, and thus can be more efficient. If a new list is returned, this does not retain
* the original order; the result is sorted.
*
* @see #withoutDuplicatesSorted
* @param the type of elements in {@code values}
* @param values a list of values
* @return the values, with duplicates removed
*/
public static > List withoutDuplicatesComparable(List values) {
// This adds O(n) time cost, and has the benefit of sometimes avoiding allocating a TreeSet.
if (isSortedNoDuplicates(values)) {
return values;
}
Set set = new TreeSet<>(values);
if (values.size() == set.size()) {
return values;
} else {
return new ArrayList<>(set);
}
}
/**
* Returns the sorted version of the list. Does not alter the list. Simply calls {@code
* Collections.sort(List, Comparator super T>)} on a copy.
*
* @return a sorted version of the list
* @param type of elements of the list
* @param l a list to sort
* @param c a sorted version of the list
*/
public static List sortList(List l, Comparator<@MustCallUnknown ? super T> c) {
List result = new ArrayList<>(l);
Collections.sort(result, c);
return result;
}
/**
* Returns true if the given list is sorted.
*
* @param the component type of the list
* @param values a list
* @return true if the list is sorted
*/
public static > boolean isSorted(List values) {
if (values.isEmpty() || values.size() == 1) {
return true;
}
if (values instanceof RandomAccess) {
// Per the Javadoc of RandomAccess, an indexed for loop is faster than a foreach loop.
int size = values.size();
for (int i = 0; i < size - 1; i++) {
if (values.get(i).compareTo(values.get(i + 1)) > 0) {
return false;
}
}
return true;
} else {
Iterator iter = values.iterator();
T previous = iter.next();
while (iter.hasNext()) {
T current = iter.next();
if (previous.compareTo(current) > 0) {
return false;
}
previous = current;
}
return true;
}
}
/**
* Returns true if the given list is sorted and has no duplicates
*
* @param the component type of the list
* @param values a list
* @return true if the list is sorted and has no duplicates
*/
public static > boolean isSortedNoDuplicates(List values) {
if (values.size() < 2) {
return true;
}
if (values instanceof RandomAccess) {
// Per the Javadoc of RandomAccess, an indexed for loop is faster than a foreach loop.
int size = values.size();
for (int i = 0; i < size - 1; i++) {
if (values.get(i).compareTo(values.get(i + 1)) >= 0) {
return false;
}
}
return true;
} else {
Iterator iter = values.iterator();
T previous = iter.next();
while (iter.hasNext()) {
T current = iter.next();
if (previous.compareTo(current) >= 0) {
return false;
}
previous = current;
}
return true;
}
}
/**
* Returns the elements (once each) that appear more than once in the given collection.
*
* @param the type of elements
* @param c a collection
* @return the elements (once each) that appear more than once in the given collection
*/
public static Collection duplicates(Collection c) {
// Inefficient (because of streams) but simple implementation.
Set withoutDuplicates = new HashSet<>();
Set duplicates = new LinkedHashSet<>();
for (T elt : c) {
if (!withoutDuplicates.add(elt)) {
duplicates.add(elt);
}
}
return duplicates;
}
/** All calls to deepEquals that are currently underway. */
private static HashSet> deepEqualsUnderway =
new HashSet>();
/**
* Determines deep equality for the elements.
*
*
* If both are primitive arrays, uses java.util.Arrays.equals.
* If both are Object[], uses java.util.Arrays.deepEquals and does not recursively call this
* method.
* If both are lists, uses deepEquals recursively on each element.
* For other types, just uses equals() and does not recursively call this method.
*
*
* @param o1 first value to compare
* @param o2 second value to compare
* @return true iff o1 and o2 are deeply equal
*/
@SuppressWarnings({
"allcheckers:purity",
"lock"
}) // side effect to static field deepEqualsUnderway
@Pure
public static boolean deepEquals(@Nullable Object o1, @Nullable Object o2) {
@SuppressWarnings("interning")
boolean sameObject = (o1 == o2);
if (sameObject) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
if (o1 instanceof byte[] && o2 instanceof byte[]) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (o1 instanceof char[] && o2 instanceof char[]) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (o1 instanceof double[] && o2 instanceof double[]) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (o1 instanceof float[] && o2 instanceof float[]) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (o1 instanceof int[] && o2 instanceof int[]) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (o1 instanceof long[] && o2 instanceof long[]) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (o1 instanceof short[] && o2 instanceof short[]) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
WeakIdentityPair mypair = WeakIdentityPair.of(o1, o2);
if (deepEqualsUnderway.contains(mypair)) {
return true;
}
if (o1 instanceof Object[] && o2 instanceof Object[]) {
return Arrays.deepEquals((Object[]) o1, (Object[]) o2);
}
if (o1 instanceof List> && o2 instanceof List>) {
List extends @Signed Object> l1 = (List extends @Signed Object>) o1;
List extends @Signed Object> l2 = (List extends @Signed Object>) o2;
if (l1.size() != l2.size()) {
return false;
}
try {
deepEqualsUnderway.add(mypair);
for (int i = 0; i < l1.size(); i++) {
Object e1 = l1.get(i);
Object e2 = l2.get(i);
if (!deepEquals(e1, e2)) {
return false;
}
}
} finally {
deepEqualsUnderway.remove(mypair);
}
return true;
}
return o1.equals(o2);
}
/**
* Applies the function to each element of the given iterable, producing a new list of the
* results. The point of this method is to make mapping operations more concise. You can write
*
* {@code return mapList(LemmaAnnotation::get, tokens);}
*
* instead of
*
* {@code return tokens
* .stream()
* .map(LemmaAnnotation::get)
* .collect(Collectors.toList());}
*
* Import this method with
*
* import static org.plumelib.util.CollectionsPlume.mapList;
*
* This method is just like {@link #transform}, but with the arguments in the other order.
*
* To perform replacement in place, see {@code List.replaceAll}.
*
* @param the type of elements of the given iterable
* @param the type of elements of the result list
* @param f a function
* @param iterable an iterable
* @return a list of the results of applying {@code f} to the elements of {@code iterable}
*/
public static <
@KeyForBottom FROM extends @Nullable @UnknownKeyFor Object,
@KeyForBottom TO extends @Nullable @UnknownKeyFor Object>
List mapList(Function super FROM, ? extends TO> f, Iterable iterable) {
List result;
if (iterable instanceof RandomAccess) {
// Per the Javadoc of RandomAccess, an indexed for loop is faster than a foreach loop.
List list = (List) iterable;
int size = list.size();
result = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
result.add(f.apply(list.get(i)));
}
return result;
}
if (iterable instanceof Collection) {
result = new ArrayList<>(((Collection>) iterable).size());
} else {
result = new ArrayList<>(); // no information about size is available
}
for (FROM elt : iterable) {
result.add(f.apply(elt));
}
return result;
}
/**
* Applies the function to each element of the given array, producing a list of the results.
*
* This produces a list rather than an array because it is problematic to create an array with
* generic compontent type.
*
*
The point of this method is to make mapping operations more concise. Import it with
*
*
import static org.plumelib.util.CollectionsPlume.mapList;
*
* This method is just like {@link #transform}, but with the arguments in the other order.
*
* @param the type of elements of the given array
* @param the type of elements of the result list
* @param f a function
* @param a an array
* @return a list of the results of applying {@code f} to the elements of {@code a}
*/
public static <
@KeyForBottom FROM extends @Nullable @UnknownKeyFor Object,
@KeyForBottom TO extends @Nullable @UnknownKeyFor Object>
List mapList(
@MustCallUnknown Function<@MustCallUnknown ? super FROM, ? extends TO> f, FROM[] a) {
int size = a.length;
List result = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
result.add(f.apply(a[i]));
}
return result;
}
/**
* Applies the function to each element of the given iterable, producing a new list of the
* results. The point of this method is to make mapping operations more concise. You can write
*
* {@code return transform(tokens, LemmaAnnotation::get);}
*
* instead of
*
* {@code return tokens
* .stream()
* .map(LemmaAnnotation::get)
* .collect(Collectors.toList());}
*
* Import this method with
*
* import static org.plumelib.util.CollectionsPlume.transform;
*
* This method is just like {@link #mapList}, but with the arguments in the other order. To
* perform replacement in place, see {@code List.replaceAll}.
*
* @param the type of elements of the given collection
* @param the type of elements of the result list
* @param iterable an iterable
* @param f a function
* @return a list of the results of applying {@code f} to the elements of {@code list}
*/
public static <
@KeyForBottom FROM extends @Nullable @UnknownKeyFor Object,
@KeyForBottom TO extends @Nullable @UnknownKeyFor Object>
List transform(
Iterable iterable, Function<@MustCallUnknown ? super FROM, ? extends TO> f) {
return mapList(f, iterable);
}
/**
* Returns a copy of {@code orig}, where each element of the result is a clone of the
* corresponding element of {@code orig}.
*
* @param the type of elements of the collection
* @param the type of the collection
* @param orig a collection
* @return a copy of {@code orig}, as described above
*/
@SuppressWarnings({
"signedness", // problem with clone()
"nullness" // generics problem
})
public static > @PolyNull C cloneElements(@PolyNull C orig) {
if (orig == null) {
return null;
}
C result = UtilPlume.clone(orig);
result.clear();
for (T elt : orig) {
result.add(UtilPlume.clone(elt));
}
return result;
}
// A "deep copy" uses the deepCopy() method of the DeepCopyable interface.
/**
* Returns a copy of {@code orig}, where each element of the result is a deep copy (according to
* the {@code DeepCopyable} interface) of the corresponding element of {@code orig}.
*
* @param the type of elements of the collection
* @param the type of the collection
* @param orig a collection
* @return a copy of {@code orig}, as described above
*/
@SuppressWarnings({"signedness", "nullness:argument"}) // problem with clone()
public static , C extends @Nullable Collection> @PolyNull C deepCopy(@PolyNull C orig) {
if (orig == null) {
return null;
}
C result = UtilPlume.clone(orig);
result.clear();
for (T elt : orig) {
result.add(DeepCopyable.deepCopyOrNull(elt));
}
return result;
}
/**
* Returns a new list containing only the elements for which the filter returns true. To modify
* the collection in place, use {@code Collection#removeIf} instead of this method.
*
* Using streams gives an equivalent list but is less efficient and more verbose:
*
*
{@code
* coll.stream().filter(filter).collect(Collectors.toList());
* }
*
* @param the type of elements
* @param coll a collection
* @param filter a predicate
* @return a new list with the elements for which the filter returns true
* @deprecated use {@link #filter} instead
*/
@Deprecated // 2023-11-30
// @InlineMe(
// replacement = "CollectionsPlume.filter(coll, filter)",
// imports = "org.plumelib.util.CollectionsPlume")
public static List listFilter(Iterable coll, Predicate super T> filter) {
return filter(coll, filter);
}
// TODO: This should return a collection of the same type as the input. Currently it always
// returns a list.
/**
* Returns a new list containing only the elements for which the filter returns true. To modify
* the collection in place, use {@code Collection#removeIf} instead of this method.
*
* Using streams gives an equivalent list but is less efficient and more verbose:
*
*
{@code
* coll.stream().filter(filter).collect(Collectors.toList());
* }
*
* @param the type of elements
* @param coll a collection
* @param filter a predicate
* @return a new list with the elements for which the filter returns true
*/
public static List filter(Iterable coll, Predicate super T> filter) {
List result = new ArrayList<>();
for (T elt : coll) {
if (filter.test(elt)) {
result.add(elt);
}
}
return result;
}
/**
* Returns true if any element of the collection matches the predicate.
*
* Using streams gives an equivalent result but is less efficient:
*
*
{@code
* coll.stream().anyMatch(predicate);
* }
*
* @param the type of elements
* @param coll a collection
* @param predicate a non-interfering, stateless predicate
* @return true if any element of the collection matches the predicate
*/
public static boolean anyMatch(Iterable coll, Predicate super T> predicate) {
for (T elt : coll) {
if (predicate.test(elt)) {
return true;
}
}
return false;
}
/**
* Returns true if all elements of the collection match the predicate.
*
* Using streams gives an equivalent result but is less efficient:
*
*
{@code
* coll.stream().allMatch(predicate);
* }
*
* @param the type of elements
* @param coll a collection
* @param predicate a non-interfering, stateless predicate
* @return true if all elements of the collection match the predicate
*/
public static boolean allMatch(Iterable coll, Predicate super T> predicate) {
for (T elt : coll) {
if (!predicate.test(elt)) {
return false;
}
}
return true;
}
/**
* Returns true if no element of the collection matches the predicate.
*
* Using streams gives an equivalent result but is less efficient:
*
*
{@code
* coll.stream().noneMatch(predicate);
* }
*
* @param the type of elements
* @param coll a collection
* @param predicate a non-interfering, stateless predicate
* @return true if no element of the collection matches the predicate
*/
public static boolean noneMatch(Iterable coll, Predicate super T> predicate) {
for (T elt : coll) {
if (predicate.test(elt)) {
return false;
}
}
return true;
}
/**
* Returns the first index of the given value in the list, starting at the given index. Uses
* {@code Object.equals()} for comparison.
*
* @param list a list
* @param value the value to search for
* @param start the starting index
* @return the index of the value in the list, at or after the given index
*/
public static int indexOf(List> list, Object value, int start) {
int idx = list.subList(start, list.size()).indexOf(value);
return idx == -1 ? -1 : idx + start;
}
/**
* Represents a replacement of one range of a collection by another collection.
*
* @param the type of collection elements
*/
public static class Replacement {
/** The first line to replace, inclusive. */
public final int start;
/** The last line to replace, inclusive . May be equal to {@code start}-1. */
public final int end;
/** The new (replacement) elements. */
final Collection elements;
/**
* Creates a new Replacement.
*
* @param start the first line to replace, inclusive
* @param end the last line to replace, exclusive
* @param elements the new (replacement) elements
*/
private Replacement(int start, int end, Collection elements) {
this.start = start;
this.end = end;
this.elements = elements;
if (end < start - 1) {
throw new Error("Invalid pair: " + this);
}
}
/**
* Creates a new Replacement.
*
* @param the type of elements of the list
* @param start the first line to replace, inclusive
* @param end the last line to replace, exclusive
* @param elements the new (replacement) elements
* @return a new Replacement
*/
public static Replacement of(int start, int end, Collection elements) {
return new Replacement(start, end, elements);
}
@Override
public String toString(@GuardSatisfied Replacement this) {
return "Replacement{" + start + ", " + end + ", " + elements + "}";
}
}
/**
* Performs a set of replacements on the given collection, returning the transformed result (as a
* list).
*
* @param the type of collection elements
* @param c a collection
* @param replacements the replacements to perform on the collection, in order from the beginning
* of the collection to the end
* @return the transformed collection, as a new list (even if no changes were made)
*/
public static List replace(Iterable c, Iterable> replacements) {
List result = new ArrayList<>();
Iterator cItor = c.iterator();
int cIndex = -1; // the index into c
Iterator> replacementItor = replacements.iterator();
while (replacementItor.hasNext()) {
Replacement replacement = replacementItor.next();
while (cIndex < replacement.start - 1) {
result.add(cItor.next());
cIndex++;
}
result.addAll(replacement.elements);
while (cIndex < replacement.end) {
cItor.next();
cIndex++;
}
}
while (cItor.hasNext()) {
result.add(cItor.next());
}
return result;
}
/**
* Performs a set of replacements on the given array, returning the transformed result (as a
* list).
*
* @param the type of collection elements
* @param c an array
* @param replacements the replacements to perform on the arary, in order from the beginning of
* the list to the end
* @return the transformed collection, as a list
*/
public static List replace(T[] c, Collection> replacements) {
return replace(Arrays.asList(c), replacements);
}
/**
* Returns true if the second list is a subsequence (not necessarily contiguous) of the first.
*
* @param the type of elements of the list
* @param longer a list
* @param shorter a list
* @return true if the second list is a subsequence (not necessarily contiguous) of the first
*/
// TODO: This could take as input a RandomAccess.
@SuppressWarnings("signedness")
public static boolean isSubsequenceMaybeNonContiguous(
Iterable longer, Iterable shorter) {
Iterator itorLonger = longer.iterator();
Iterator itorShorter = shorter.iterator();
outerLoop:
while (itorShorter.hasNext()) {
T eltShorter = itorShorter.next();
while (itorLonger.hasNext()) {
T eltLonger = itorLonger.next();
if (Objects.equals(eltShorter, eltLonger)) {
continue outerLoop;
}
}
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////
/// SortedSet
///
/**
* Returns true if the two sets contain the same elements in the same order. This is faster than
* regular {@code equals()}, for sets with the same ordering operator, especially for sets that
* are not extremely small.
*
* @param the type of elements in the sets
* @param set1 the first set to compare
* @param set2 the first set to compare
* @return true if the two sets contain the same elements in the same order
*/
public static boolean sortedSetEquals(SortedSet set1, SortedSet set2) {
@SuppressWarnings("interning:not.interned")
boolean sameObject = set1 == set2;
if (sameObject) {
return true;
}
if (set1.size() != set2.size()) {
return false;
}
Comparator super T> comparator1 = set1.comparator();
Comparator super T> comparator2 = set2.comparator();
if (!Objects.equals(comparator1, comparator2)) {
// Fall back to regular `equals`.
return set1.equals(set2);
}
for (Iterator itor1 = set1.iterator(), itor2 = set2.iterator(); itor1.hasNext(); ) {
if (!Objects.equals(itor1.next(), itor2.next())) {
return false;
}
}
return true;
}
/**
* Returns true if the two sets contain the same elements in the same order. This is faster than
* regular {@code containsAll()}, for sets with the same ordering operator, especially for sets
* that are not extremely small.
*
* @param the type of elements in the sets
* @param set1 the first set to compare
* @param set2 the first set to compare
* @return true if the first set contains all the elements of the second set
*/
public static boolean sortedSetContainsAll(SortedSet set1, SortedSet set2) {
@SuppressWarnings("interning:not.interned")
boolean sameObject = set1 == set2;
if (sameObject) {
return true;
}
if (set1.size() < set2.size()) {
return false;
}
Comparator super T> comparator1 = set1.comparator();
Comparator super T> comparator2 = set2.comparator();
if (!Objects.equals(comparator1, comparator2)) {
// Fall back to regular `containsAll`.
return set1.containsAll(set2);
}
if (comparator1 == null) {
outerloopNaturalOrder:
for (Iterator itor1 = set1.iterator(), itor2 = set2.iterator(); itor2.hasNext(); ) {
T elt2 = itor2.next();
if (elt2 == null) {
throw new IllegalArgumentException("null element in set 2: " + set2);
}
while (itor1.hasNext()) {
T elt1 = itor1.next();
if (elt2 == null) {
throw new IllegalArgumentException("null element in set 2: " + set2);
}
@SuppressWarnings({
"unchecked", // Java warning about generic cast
"nullness:dereference", // next() has side effects, so elt1 isn't know to be non-null
"signedness:method.invocation" // generics problem; #979?
})
int comparison = ((Comparable) elt1).compareTo(elt2);
if (comparison == 0) {
continue outerloopNaturalOrder;
} else if (comparison < 0) {
return false;
}
}
return false;
}
} else {
outerloopComparator:
for (Iterator itor1 = set1.iterator(), itor2 = set2.iterator(); itor2.hasNext(); ) {
T elt2 = itor2.next();
while (itor1.hasNext()) {
T elt1 = itor1.next();
int comparison = comparator1.compare(elt1, elt2);
if (comparison == 0) {
continue outerloopComparator;
} else if (comparison < 0) {
return false;
}
}
return false;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////
/// ArrayList
///
/**
* Returns a vector containing the elements of the enumeration.
*
* @param type of the enumeration and vector elements
* @param e an enumeration to convert to a ArrayList
* @return a vector containing the elements of the enumeration
*/
@SuppressWarnings({"JdkObsolete", "NonApiType"})
public static ArrayList makeArrayList(Enumeration e) {
ArrayList result = new ArrayList<>();
while (e.hasMoreElements()) {
result.add(e.nextElement());
}
return result;
}
/**
* Creates an immutable list containing two elements. In Java 9+, use List.of().
*
* @param the List's element type
* @param e1 the first element
* @param e2 the second element
* @return a List containing the specified elements
*/
public static List listOf(E e1, E e2) {
ArrayList result = new ArrayList<>(2);
result.add(e1);
result.add(e2);
return Collections.unmodifiableList(result);
}
/**
* Concatenates a list and an element into a new list.
*
* @param the type of the list elements
* @param list the list; is not modified by this method
* @param lastElt the new last elemeent
* @return a new list containing the list elements and the last element, in that order
*/
@SuppressWarnings("unchecked")
public static List append(Collection list, T lastElt) {
List result = new ArrayList<>(list.size() + 1);
result.addAll(list);
result.add(lastElt);
return result;
}
/**
* Concatenates two lists into a new list.
*
* @param the type of the list elements
* @param list1 the first list
* @param list2 the second list
* @return a new list containing the contents of the given lists, in order
*/
@SuppressWarnings("unchecked")
public static List concatenate(Collection list1, Collection list2) {
List result = new ArrayList<>(list1.size() + list2.size());
result.addAll(list1);
result.addAll(list2);
return result;
}
// Rather than writing something like ArrayListToStringArray, use
// v.toArray(new String[0])
// Helper method
/**
* Compute (n choose k), which is (n! / (k!(n-k)!)).
*
* @param n number of elements from which to choose
* @param k number of elements to choose
* @return n choose k, or Long.MAX_VALUE if the value would overflow
*/
private static long choose(int n, int k) {
// From https://stackoverflow.com/questions/2201113/combinatoric-n-choose-r-in-java-math
if (n < k) {
return 0;
}
if (k == 0 || k == n) {
return 1;
}
long a = choose(n - 1, k - 1);
long b = choose(n - 1, k);
if (a < 0 || a == Long.MAX_VALUE || b < 0 || b == Long.MAX_VALUE || a + b < 0) {
return Long.MAX_VALUE;
} else {
return a + b;
}
}
/**
* Returns a list of lists of each combination (with repetition, but not permutations) of the
* specified objects starting at index {@code start} over {@code dims} dimensions, for {@code dims
* > 0}.
*
* For example, createCombinations(1, 0, {a, b, c}) returns a 3-element list of singleton
* lists:
*
*
* {a}, {b}, {c}
*
*
* And createCombinations(2, 0, {a, b, c}) returns a 6-element list of 2-element lists:
*
*
* {a, a}, {a, b}, {a, c}
* {b, b}, {b, c},
* {c, c}
*
*
* @param type of the input list elements, and type of the innermost output list elements
* @param dims number of dimensions: that is, size of each innermost list
* @param start initial index
* @param objs list of elements to create combinations of
* @return list of lists of length dims, each of which combines elements from objs
*/
public static List> createCombinations(
@Positive int dims, @NonNegative int start, List objs) {
if (dims < 1) {
throw new IllegalArgumentException();
}
long numResults = choose(objs.size() + dims - 1, dims);
if (numResults > 100000000) {
throw new Error("Do you really want to create more than 100 million lists?");
}
List> results = new ArrayList>();
for (int i = start; i < objs.size(); i++) {
if (dims == 1) {
List simple = new ArrayList<>();
simple.add(objs.get(i));
results.add(simple);
} else {
List> combos = createCombinations(dims - 1, i, objs);
for (List lt : combos) {
List simple = new ArrayList<>();
simple.add(objs.get(i));
simple.addAll(lt);
results.add(simple);
}
}
}
return results;
}
/**
* Returns a list of lists of each combination (with repetition, but not permutations) of integers
* from start to cnt (inclusive) over arity dimensions.
*
* For example, createCombinations(1, 0, 2) returns a 3-element list of singleton lists:
*
*
* {0}, {1}, {2}
*
*
* And createCombinations(2, 10, 2) returns a 6-element list of 2-element lists:
*
*
* {10, 10}, {10, 11}, {10, 12}, {11, 11}, {11, 12}, {12, 12}
*
*
* The length of the list is (cnt multichoose arity), which is ((cnt + arity - 1) choose arity).
*
* @param arity size of each innermost list
* @param start initial value
* @param cnt maximum element value
* @return list of lists of length arity, each of which combines integers from start to cnt
*/
@SuppressWarnings("NonApiType")
public static ArrayList> createCombinations(
int arity, @NonNegative int start, int cnt) {
long numResults = choose(cnt + arity - 1, arity);
if (numResults > 100000000) {
throw new Error("Do you really want to create more than 100 million lists?");
}
ArrayList> results = new ArrayList<>();
// Return a list with one zero length element if arity is zero
if (arity == 0) {
results.add(new ArrayList());
return results;
}
for (int i = start; i <= cnt; i++) {
ArrayList> combos = createCombinations(arity - 1, i, cnt);
for (ArrayList li : combos) {
ArrayList simple = new ArrayList<>();
simple.add(i);
simple.addAll(li);
results.add(simple);
}
}
return results;
}
///////////////////////////////////////////////////////////////////////////
/// Iterator
///
/**
* Converts an Iterator to an Iterable. The resulting Iterable can be used to produce a single,
* working Iterator (the one that was passed in). Subsequent calls to its iterator() method will
* fail, because otherwise they would return the same Iterator instance, which may have been
* exhausted, or otherwise be in some indeterminate state. Calling iteratorToIterable twice on the
* same argument can have similar problems, so don't do that.
*
* @param source the Iterator to be converted to Iterable
* @param the element type
* @return source, converted to Iterable
*/
public static Iterable iteratorToIterable(final Iterator source) {
if (source == null) {
throw new NullPointerException();
}
return new Iterable() {
/** True if this Iterable object has been used. */
private AtomicBoolean used = new AtomicBoolean();
@Override
public Iterator iterator() {
if (used.getAndSet(true)) {
throw new Error("Call iterator() just once");
}
return source;
}
};
}
// Making these classes into functions didn't work because I couldn't get
// their arguments into a scope that Java was happy with.
/**
* Converts an Enumeration into an Iterator.
*
* @param the type of elements of the enumeration and iterator
*/
public static final class EnumerationIterator implements Iterator {
/** The enumeration that this object wraps. */
Enumeration e;
/**
* Create an Iterator that yields the elements of the given Enumeration.
*
* @param e the Enumeration to make into an Iterator
*/
public EnumerationIterator(Enumeration e) {
this.e = e;
}
@SuppressWarnings("JdkObsolete")
@Override
public boolean hasNext(@GuardSatisfied EnumerationIterator this) {
return e.hasMoreElements();
}
@SuppressWarnings("JdkObsolete")
@Override
public T next(@GuardSatisfied EnumerationIterator this) {
return e.nextElement();
}
@Override
public void remove(@GuardSatisfied EnumerationIterator this) {
throw new UnsupportedOperationException();
}
}
/**
* Converts an Iterator into an Enumeration.
*
* @param the type of elements of the enumeration and iterator
*/
@SuppressWarnings("JdkObsolete")
public static final class IteratorEnumeration implements Enumeration {
/** The iterator that this object wraps. */
Iterator itor;
/**
* Create an Enumeration that contains the elements returned by the given Iterator.
*
* @param itor the Iterator to make an Enumeration from
*/
public IteratorEnumeration(Iterator itor) {
this.itor = itor;
}
@Override
public boolean hasMoreElements() {
return itor.hasNext();
}
@Override
public T nextElement() {
return itor.next();
}
}
// This must already be implemented someplace else. Right??
/**
* An Iterator that returns first the elements returned by its first argument, then the elements
* returned by its second argument. Like {@link MergedIterator}, but specialized for the case of
* two arguments.
*
* @param the type of elements of the iterator
*/
public static final class MergedIterator2 implements Iterator {
/** The first of the two iterators that this object merges. */
Iterator itor1;
/** The second of the two iterators that this object merges. */
Iterator itor2;
/**
* Create an iterator that returns the elements of {@code itor1} then those of {@code itor2}.
*
* @param itor1 an Iterator
* @param itor2 another Iterator
*/
public MergedIterator2(Iterator itor1, Iterator itor2) {
this.itor1 = itor1;
this.itor2 = itor2;
}
@Override
public boolean hasNext(@GuardSatisfied MergedIterator2 this) {
return itor1.hasNext() || itor2.hasNext();
}
@Override
public T next(@GuardSatisfied MergedIterator2 this) {
if (itor1.hasNext()) {
return itor1.next();
} else if (itor2.hasNext()) {
return itor2.next();
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove(@GuardSatisfied MergedIterator2 this) {
throw new UnsupportedOperationException();
}
}
// This must already be implemented someplace else. Right??
/**
* An Iterator that returns the elements in each of its argument Iterators, in turn. The argument
* is an Iterator of Iterators. Like {@link MergedIterator2}, but generalized to arbitrary number
* of iterators.
*
* @param the type of elements of the iterator
*/
public static final class MergedIterator implements Iterator {
/** The iterators that this object merges. */
Iterator> itorOfItors;
/**
* Create an iterator that returns the elements of the given iterators, in turn.
*
* @param itorOfItors an iterator whose elements are iterators; this MergedIterator will merge
* them all
*/
public MergedIterator(Iterator> itorOfItors) {
this.itorOfItors = itorOfItors;
}
/** The current iterator (from {@link #itorOfItors}) that is being iterated over. */
// Initialize to an empty iterator to prime the pump.
Iterator current = new ArrayList().iterator();
@SuppressWarnings({"allcheckers:purity", "lock:method.guarantee.violated"})
@Override
public boolean hasNext(@GuardSatisfied MergedIterator this) {
while (!current.hasNext() && itorOfItors.hasNext()) {
current = itorOfItors.next();
}
return current.hasNext();
}
@Override
public T next(@GuardSatisfied MergedIterator this) {
if (!hasNext()) {
throw new NoSuchElementException();
}
return current.next();
}
@Override
public void remove(@GuardSatisfied MergedIterator this) {
throw new UnsupportedOperationException();
}
}
/**
* An iterator that only returns elements that match the given predicate.
*
* @param the type of elements of the iterator
*/
public static final class FilteredIterator implements Iterator {
/** The iterator that this object is filtering. */
Iterator itor;
/** The predicate that determines which elements to retain. */
Predicate predicate;
/**
* Create an iterator that only returns elements of {@code itor} that match the given predicate.
*
* @param itor the Iterator to filter
* @param predicate the predicate that determines which elements to retain
*/
public FilteredIterator(Iterator itor, Predicate predicate) {
this.itor = itor;
this.predicate = predicate;
}
/** A marker object, distinct from any object that the iterator can return. */
@SuppressWarnings("unchecked")
T invalidT = (T) new Object();
/**
* The next object that this iterator will yield, or {@link #invalidT} if {@link #currentValid}
* is false.
*/
T current = invalidT;
/** True iff {@link #current} is an object from the wrapped iterator. */
boolean currentValid = false;
@SuppressWarnings({
"allcheckers:purity",
"lock:method.guarantee.violated"
}) // benevolent side effects
@Override
public boolean hasNext(@GuardSatisfied FilteredIterator this) {
while (!currentValid && itor.hasNext()) {
current = itor.next();
currentValid = predicate.test(current);
}
return currentValid;
}
@Override
public T next(@GuardSatisfied FilteredIterator this) {
if (hasNext()) {
currentValid = false;
@SuppressWarnings("interning")
boolean ok = (current != invalidT);
assert ok;
return current;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove(@GuardSatisfied FilteredIterator this) {
throw new UnsupportedOperationException();
}
}
/**
* Returns an iterator just like its argument, except that the first and last elements are
* removed. They can be accessed via the {@link #getFirst} and {@link #getLast} methods.
*
* @param the type of elements of the iterator
*/
public static final class RemoveFirstAndLastIterator implements Iterator {
/** The wrapped iterator. */
Iterator itor;
/** A marker object, distinct from any object that the iterator can return. */
@SuppressWarnings("unchecked")
T nothing = (T) new Object();
// I don't think this works, because the iterator might itself return null
// @Nullable T nothing = (@Nullable T) null;
/** The first object yielded by the wrapped iterator. */
T first = nothing;
/** The next object that this iterator will return. */
T current = nothing;
/**
* Create an iterator just like {@code itor}, except without its first and last elements.
*
* @param itor an itorator whose first and last elements to discard
*/
public RemoveFirstAndLastIterator(Iterator itor) {
this.itor = itor;
if (itor.hasNext()) {
first = itor.next();
}
if (itor.hasNext()) {
current = itor.next();
}
}
@Override
public boolean hasNext(@GuardSatisfied RemoveFirstAndLastIterator this) {
return itor.hasNext();
}
@Override
public T next(@GuardSatisfied RemoveFirstAndLastIterator this) {
if (!itor.hasNext()) {
throw new NoSuchElementException();
}
T tmp = current;
current = itor.next();
return tmp;
}
/**
* Returns the first element of the iterator that was used to construct this. This value is not
* part of this iterator (unless the original iterator would have returned it multiple times).
*
* @return the first element of the iterator that was used to construct this
*/
@SuppressWarnings("allcheckers:purity.not.sideeffectfree.call") // constructing an exception
@Pure
public T getFirst() {
@SuppressWarnings("interning") // check for equality to a special value
boolean invalid = (first == nothing);
if (invalid) {
throw new NoSuchElementException();
}
return first;
}
/**
* Returns the last element of the iterator that was used to construct this. This value is not
* part of this iterator (unless the original iterator would have returned it multiple times).
*
* Throws an error unless the RemoveFirstAndLastIterator has already been iterated all the
* way to its end (so the delegate is pointing to the last element).
*
* @return the last element of the iterator that was used to construct this
*/
// TODO: This is buggy when the delegate is empty.
@Pure
public T getLast() {
if (itor.hasNext()) {
throw new Error();
}
return current;
}
@Override
public void remove(@GuardSatisfied RemoveFirstAndLastIterator this) {
throw new UnsupportedOperationException();
}
}
/**
* Returns a List containing numElts randomly chosen elements from the iterator, or all the
* elements of the iterator if there are fewer. It examines every element of the iterator, but
* does not keep them all in memory.
*
* @param type of the iterator elements
* @param itor elements to be randomly selected from
* @param numElts number of elements to select
* @return list of numElts elements from itor
*/
public static List randomElements(Iterator itor, int numElts) {
return randomElements(itor, numElts, r);
}
/** The random generator. */
private static Random r = new Random();
/**
* Returns a List containing numElts randomly chosen elements from the iterator, or all the
* elements of the iterator if there are fewer. It examines every element of the iterator, but
* does not keep them all in memory.
*
* @param type of the iterator elements
* @param itor elements to be randomly selected from
* @param numElts number of elements to select
* @param random the Random instance to use to make selections
* @return list of numElts elements from itor
*/
public static List randomElements(Iterator itor, int numElts, Random random) {
// The elements are chosen with the following probabilities,
// where n == numElts:
// n n/2 n/3 n/4 n/5 ...
RandomSelector rs = new RandomSelector<>(numElts, random);
while (itor.hasNext()) {
rs.accept(itor.next());
}
return rs.getValues();
/*
ArrayList result = new ArrayList<>(numElts);
int i=1;
for (int n=0; n type of keys in the map
* @param m map from K to Integer
* @param key the key whose value will be incremented
* @return the old value, before it was incremented; this might be null
* @throws Error if the key is in the Map but maps to a non-Integer
*/
public static @Nullable Integer incrementMap(
Map m, K key) {
return incrementMap(m, key, 1);
}
/**
* Increment the Integer which is indexed by key in the Map. Set the value to {@code count} if not
* currently mapped.
*
* @param type of keys in the map
* @param m map from K to Integer
* @param key the key whose value will be incremented
* @param count how much to increment the value by
* @return the old value, before it was incremented; this might be null
* @throws Error if the key is in the Map but maps to a non-Integer
*/
public static