com.google.gwt.emul.java.util.Collections Maven / Gradle / Ivy
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package java.util;
import static javaemul.internal.Coercions.ensureInt;
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkElementIndex;
import static javaemul.internal.InternalPreconditions.checkNotNull;
import java.io.Serializable;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* Utility methods that operate on collections.
* See
* the official Java API doc for details.
*/
public class Collections {
private static final class LifoQueue extends AbstractQueue implements
Serializable {
private final Deque deque;
LifoQueue(Deque deque) {
this.deque = deque;
}
@Override
public Iterator iterator() {
return deque.iterator();
}
@Override
public boolean offer(E e) {
return deque.offerFirst(e);
}
@Override
public E peek() {
return deque.peekFirst();
}
@Override
public E poll() {
return deque.pollFirst();
}
@Override
public int size() {
return deque.size();
}
}
private static final class EmptyList extends AbstractList implements
RandomAccess, Serializable {
@Override
public boolean contains(Object object) {
return false;
}
@Override
public Object get(int location) {
checkElementIndex(location, 0);
return null;
}
@Override
public Iterator iterator() {
return emptyIterator();
}
@Override
public ListIterator listIterator() {
return emptyListIterator();
}
@Override
public int size() {
return 0;
}
}
private static final class EmptyListIterator implements ListIterator {
static final EmptyListIterator INSTANCE = new EmptyListIterator();
@Override
public void add(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
return false;
}
@Override
public boolean hasPrevious() {
return false;
}
@Override
public Object next() {
throw new NoSuchElementException();
}
@Override
public int nextIndex() {
return 0;
}
@Override
public Object previous() {
throw new NoSuchElementException();
}
@Override
public int previousIndex() {
return -1;
}
@Override
public void remove() {
throw new IllegalStateException();
}
@Override
public void set(Object o) {
throw new IllegalStateException();
}
}
private static final class EmptySet extends AbstractSet implements
Serializable {
@Override
public boolean contains(Object object) {
return false;
}
@Override
public Iterator iterator() {
return emptyIterator();
}
@Override
public int size() {
return 0;
}
}
private static final class EmptyMap extends AbstractMap implements
Serializable {
@Override
public boolean containsKey(Object key) {
return false;
}
@Override
public boolean containsValue(Object value) {
return false;
}
@Override
public Set entrySet() {
return EMPTY_SET;
}
@Override
public Object get(Object key) {
return null;
}
@Override
public Set keySet() {
return EMPTY_SET;
}
@Override
public int size() {
return 0;
}
@Override
public Collection values() {
return EMPTY_LIST;
}
}
private static final class SetFromMap extends AbstractSet
implements Serializable {
private final Map backingMap;
private transient Set keySet;
SetFromMap(Map map) {
backingMap = map;
}
@Override
public boolean add(E e) {
return backingMap.put(e, Boolean.TRUE) == null;
}
@Override
public void clear() {
backingMap.clear();
}
@Override
public boolean contains(Object o) {
return backingMap.containsKey(o);
}
@Override
public boolean equals(Object o) {
return o == this || keySet().equals(o);
}
@Override
public int hashCode() {
return keySet().hashCode();
}
@Override
public Iterator iterator() {
return keySet().iterator();
}
@Override
public boolean remove(Object o) {
return backingMap.remove(o) != null;
}
@Override
public int size() {
return keySet().size();
}
@Override
public String toString() {
return keySet().toString();
}
/**
* Lazy initialize keySet to avoid NPE after deserialization.
*/
private Set keySet() {
if (keySet == null) {
keySet = backingMap.keySet();
}
return keySet;
}
}
private static final class SingletonList extends AbstractList implements Serializable {
private E element;
public SingletonList(E element) {
this.element = element;
}
@Override
public boolean contains(Object item) {
return Objects.equals(element, item);
}
@Override
public E get(int index) {
checkElementIndex(index, 1);
return element;
}
@Override
public int size() {
return 1;
}
}
/*
* TODO: make the unmodifiable collections serializable.
*/
static class UnmodifiableCollection implements Collection {
protected final Collection extends T> coll;
public UnmodifiableCollection(Collection extends T> coll) {
this.coll = coll;
}
@Override
public boolean add(T o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection extends T> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean contains(Object o) {
return coll.contains(o);
}
@Override
public boolean containsAll(Collection> c) {
return coll.containsAll(c);
}
@Override
public boolean isEmpty() {
return coll.isEmpty();
}
@Override
public Iterator iterator() {
return new UnmodifiableCollectionIterator(coll.iterator());
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeIf(Predicate super T> p) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return coll.size();
}
@Override
public Object[] toArray() {
return coll.toArray();
}
@Override
public E[] toArray(E[] a) {
return coll.toArray(a);
}
@Override
public String toString() {
return coll.toString();
}
}
static class UnmodifiableList extends UnmodifiableCollection implements
List {
private final List extends T> list;
public UnmodifiableList(List extends T> list) {
super(list);
this.list = list;
}
@Override
public void add(int index, T element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(int index, Collection extends T> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object o) {
return list.equals(o);
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
@Override
public ListIterator listIterator() {
return listIterator(0);
}
@Override
public ListIterator listIterator(int from) {
return new UnmodifiableListIterator(list.listIterator(from));
}
@Override
public void replaceAll(UnaryOperator operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator super T> c) {
throw new UnsupportedOperationException();
}
@Override
public T remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public T set(int index, T element) {
throw new UnsupportedOperationException();
}
@Override
public List subList(int fromIndex, int toIndex) {
return new UnmodifiableList(list.subList(fromIndex, toIndex));
}
}
static class UnmodifiableMap implements Map {
static class UnmodifiableEntrySet extends
UnmodifiableSet> {
private static class UnmodifiableEntry implements Map.Entry {
private Map.Entry extends K, ? extends V> entry;
public UnmodifiableEntry(Map.Entry extends K, ? extends V> entry) {
this.entry = entry;
}
@Override
public boolean equals(Object o) {
return entry.equals(o);
}
@Override
public K getKey() {
return entry.getKey();
}
@Override
public V getValue() {
return entry.getValue();
}
@Override
public int hashCode() {
return entry.hashCode();
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return entry.toString();
}
}
@SuppressWarnings("unchecked")
public UnmodifiableEntrySet(
Set extends Map.Entry extends K, ? extends V>> s) {
super((Set extends Entry>) s);
}
@Override
public boolean contains(Object o) {
return coll.contains(o);
}
@Override
public boolean containsAll(Collection> o) {
return coll.containsAll(o);
}
@Override
@SuppressWarnings("unchecked")
public Iterator> iterator() {
final Iterator> it = (Iterator>) coll.iterator();
return new Iterator>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Map.Entry next() {
return new UnmodifiableEntry(it.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Object[] toArray() {
Object[] array = super.toArray();
wrap(array, array.length);
return array;
}
@Override
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
Object[] result = super.toArray(a);
wrap(result, coll.size());
return (T[]) result;
}
/**
* Wrap an array of Map.Entries as UnmodifiableEntries.
*
* @param array array to wrap
* @param size number of entries to wrap
*/
@SuppressWarnings("unchecked")
private void wrap(Object[] array, int size) {
for (int i = 0; i < size; ++i) {
array[i] = new UnmodifiableEntry((Map.Entry) array[i]);
}
}
}
private transient UnmodifiableSet> entrySet;
private transient UnmodifiableSet keySet;
private final Map extends K, ? extends V> map;
private transient UnmodifiableCollection values;
public UnmodifiableMap(Map extends K, ? extends V> map) {
this.map = map;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object val) {
return map.containsValue(val);
}
@Override
public Set> entrySet() {
if (entrySet == null) {
entrySet = new UnmodifiableEntrySet(map.entrySet());
}
return entrySet;
}
@Override
public boolean equals(Object o) {
return map.equals(o);
}
@Override
public V get(Object key) {
return map.get(key);
}
@Override
public int hashCode() {
return map.hashCode();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set keySet() {
if (keySet == null) {
keySet = new UnmodifiableSet(map.keySet());
}
return keySet;
}
@Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map extends K, ? extends V> t) {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return map.size();
}
@Override
public String toString() {
return map.toString();
}
@Override
public Collection values() {
if (values == null) {
values = new UnmodifiableCollection(map.values());
}
return values;
}
}
static class UnmodifiableRandomAccessList extends UnmodifiableList
implements RandomAccess {
public UnmodifiableRandomAccessList(List extends T> list) {
super(list);
}
}
static class UnmodifiableSet extends UnmodifiableCollection implements
Set {
public UnmodifiableSet(Set extends T> set) {
super(set);
}
@Override
public boolean equals(Object o) {
return coll.equals(o);
}
@Override
public int hashCode() {
return coll.hashCode();
}
}
static class UnmodifiableSortedMap extends UnmodifiableMap
implements SortedMap {
private SortedMap sortedMap;
public UnmodifiableSortedMap(SortedMap sortedMap) {
super(sortedMap);
this.sortedMap = sortedMap;
}
@Override
public Comparator super K> comparator() {
return sortedMap.comparator();
}
@Override
public boolean equals(Object o) {
return sortedMap.equals(o);
}
@Override
public K firstKey() {
return sortedMap.firstKey();
}
@Override
public int hashCode() {
return sortedMap.hashCode();
}
@Override
public SortedMap headMap(K toKey) {
return new UnmodifiableSortedMap(sortedMap.headMap(toKey));
}
@Override
public K lastKey() {
return sortedMap.lastKey();
}
@Override
public SortedMap subMap(K fromKey, K toKey) {
return new UnmodifiableSortedMap(sortedMap.subMap(fromKey, toKey));
}
@Override
public SortedMap tailMap(K fromKey) {
return new UnmodifiableSortedMap(sortedMap.tailMap(fromKey));
}
}
static class UnmodifiableSortedSet extends UnmodifiableSet implements
SortedSet {
private SortedSet sortedSet;
@SuppressWarnings("unchecked")
public UnmodifiableSortedSet(SortedSet extends E> sortedSet) {
super(sortedSet);
this.sortedSet = (SortedSet) sortedSet;
}
@Override
public Comparator super E> comparator() {
return sortedSet.comparator();
}
@Override
public boolean equals(Object o) {
return sortedSet.equals(o);
}
@Override
public E first() {
return sortedSet.first();
}
@Override
public int hashCode() {
return sortedSet.hashCode();
}
@Override
public SortedSet headSet(E toElement) {
return new UnmodifiableSortedSet(sortedSet.headSet(toElement));
}
@Override
public E last() {
return sortedSet.last();
}
@Override
public SortedSet subSet(E fromElement, E toElement) {
return new UnmodifiableSortedSet(sortedSet.subSet(fromElement,
toElement));
}
@Override
public SortedSet tailSet(E fromElement) {
return new UnmodifiableSortedSet(sortedSet.tailSet(fromElement));
}
}
private static class UnmodifiableCollectionIterator implements Iterator {
private final Iterator extends T> it;
private UnmodifiableCollectionIterator(Iterator extends T> it) {
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public T next() {
return it.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private static class UnmodifiableListIterator extends
UnmodifiableCollectionIterator implements ListIterator {
private final ListIterator extends T> lit;
private UnmodifiableListIterator(ListIterator extends T> lit) {
super(lit);
this.lit = lit;
}
@Override
public void add(T o) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPrevious() {
return lit.hasPrevious();
}
@Override
public int nextIndex() {
return lit.nextIndex();
}
@Override
public T previous() {
return lit.previous();
}
@Override
public int previousIndex() {
return lit.previousIndex();
}
@Override
public void set(T o) {
throw new UnsupportedOperationException();
}
}
private static class RandomHolder {
private static final Random rnd = new Random();
}
@SuppressWarnings("unchecked")
public static final List EMPTY_LIST = new EmptyList();
@SuppressWarnings("unchecked")
public static final Map EMPTY_MAP = new EmptyMap();
@SuppressWarnings("unchecked")
public static final Set EMPTY_SET = new EmptySet();
public static boolean addAll(Collection super T> c, T... a) {
boolean result = false;
for (T e : a) {
result |= c.add(e);
}
return result;
}
public static Queue asLifoQueue(Deque deque) {
return new LifoQueue(deque);
}
/**
* Perform a binary search on a sorted List, using natural ordering.
*
*
* Note: The GWT implementation differs from the JDK implementation in that it
* does not do an iterator-based binary search for Lists that do not implement
* RandomAccess.
*
*
* @param sortedList object array to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number
* which is the index of the next larger value (or just past the end
* of the array if the searched value is larger than all elements in
* the array) minus 1 (to ensure error returns are negative)
* @throws ClassCastException if key
is not comparable to
* sortedList
's elements.
*/
public static int binarySearch(
final List extends Comparable super T>> sortedList, final T key) {
return binarySearch(sortedList, key, null);
}
/*
* These methods are commented out because they cannot currently be
* implemented in GWT. The signatures are included in case that changes.
*/
// public static Collection checkedCollection(Collection c, Class
// type) {
// // FUTURE: implement
// return null;
// }
//
// static List checkedList(List list, Class type) {
// // FUTURE: implement
// return null;
// }
//
// public static Map checkedMap(Map list, Class keyType,
// Class valueType) {
// // FUTURE: implement
// return null;
// }
//
// public static Set checkedSet(Set list, Class type) {
// // FUTURE: implement
// return null;
// }
//
// public static SortedMap checkedSortedMap(SortedMap m,
// Class keyType, Class valueType) {
// // FUTURE: implement
// return null;
// }
//
// public static SortedSet checkedSortedSet(SortedSet list, Class
// type) {
// // FUTURE: implement
// return null;
// }
/**
* Perform a binary search on a sorted List, using a user-specified comparison
* function.
*
*
* Note: The GWT implementation differs from the JDK implementation in that it
* does not do an iterator-based binary search for Lists that do not implement
* RandomAccess.
*
*
* @param sortedList List to search
* @param key value to search for
* @param comparator comparision function, null
indicates
* natural ordering should be used.
* @return the index of an element with a matching value, or a negative number
* which is the index of the next larger value (or just past the end
* of the array if the searched value is larger than all elements in
* the array) minus 1 (to ensure error returns are negative)
* @throws ClassCastException if key
and
* sortedList
's elements cannot be compared by
* comparator
.
*/
public static int binarySearch(final List extends T> sortedList,
final T key, Comparator super T> comparator) {
/*
* TODO: This doesn't implement the "iterator-based binary search" described
* in the JDK docs for non-RandomAccess Lists. Until GWT provides a
* LinkedList, this shouldn't be an issue.
*/
comparator = Comparators.nullToNaturalOrder(comparator);
int low = 0;
int high = sortedList.size() - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final T midVal = sortedList.get(mid);
final int compareResult = comparator.compare(midVal, key);
if (compareResult < 0) {
low = mid + 1;
} else if (compareResult > 0) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
public static void copy(List super T> dest, List extends T> src) {
if (src.size() > dest.size()) {
throw new IndexOutOfBoundsException("src does not fit in dest");
}
ListIterator super T> destIt = dest.listIterator();
for (T e : src) {
destIt.next();
destIt.set(e);
}
}
public static boolean disjoint(Collection> c1, Collection> c2) {
Collection> iterating = c1;
Collection> testing = c2;
// See if one of these objects possibly implements a fast contains.
if ((c1 instanceof Set) && !(c2 instanceof Set)) {
iterating = c2;
testing = c1;
}
for (Object o : iterating) {
if (testing.contains(o)) {
return false;
}
}
return true;
}
@SuppressWarnings(value = {"unchecked", "cast"})
public static Iterator emptyIterator() {
return (Iterator) EmptyListIterator.INSTANCE;
}
@SuppressWarnings(value = {"unchecked", "cast"})
public static List emptyList() {
return (List) EMPTY_LIST;
}
@SuppressWarnings(value = {"unchecked", "cast"})
public static ListIterator emptyListIterator() {
return (ListIterator) EmptyListIterator.INSTANCE;
}
@SuppressWarnings(value = {"unchecked", "cast"})
public static Map emptyMap() {
return (Map) EMPTY_MAP;
}
@SuppressWarnings(value = {"unchecked", "cast"})
public static Set emptySet() {
return (Set) EMPTY_SET;
}
public static Enumeration enumeration(Collection c) {
final Iterator it = c.iterator();
return new Enumeration() {
@Override
public boolean hasMoreElements() {
return it.hasNext();
}
@Override
public T nextElement() {
return it.next();
}
};
}
public static void fill(List super T> list, T obj) {
for (ListIterator super T> it = list.listIterator(); it.hasNext();) {
it.next();
it.set(obj);
}
}
public static int frequency(Collection> c, Object o) {
int count = 0;
for (Object e : c) {
if (Objects.equals(o, e)) {
++count;
}
}
return count;
}
public static ArrayList list(Enumeration e) {
ArrayList arrayList = new ArrayList();
while (e.hasMoreElements()) {
arrayList.add(e.nextElement());
}
return arrayList;
}
public static > T max(
Collection extends T> coll) {
return max(coll, null);
}
public static T max(Collection extends T> coll,
Comparator super T> comp) {
comp = Comparators.nullToNaturalOrder(comp);
Iterator extends T> it = coll.iterator();
// Will throw NoSuchElementException if coll is empty.
T max = it.next();
while (it.hasNext()) {
T t = it.next();
if (comp.compare(t, max) > 0) {
max = t;
}
}
return max;
}
public static > T min(
Collection extends T> coll) {
return min(coll, null);
}
public static T min(Collection extends T> coll,
Comparator super T> comp) {
return max(coll, reverseOrder(comp));
}
public static Set newSetFromMap(Map map) {
checkArgument(map.isEmpty(), "map is not empty");
return new SetFromMap(map);
}
public static List nCopies(int n, T o) {
ArrayList list = new ArrayList();
for (int i = 0; i < n; ++i) {
list.add(o);
}
return unmodifiableList(list);
}
public static boolean replaceAll(List list, T oldVal, T newVal) {
boolean modified = false;
for (ListIterator it = list.listIterator(); it.hasNext();) {
T t = it.next();
if (Objects.equals(t, oldVal)) {
it.set(newVal);
modified = true;
}
}
return modified;
}
@SuppressWarnings("unchecked")
public static void reverse(List> l) {
if (l instanceof RandomAccess) {
for (int iFront = 0, iBack = l.size() - 1; iFront < iBack; ++iFront, --iBack) {
Collections.swap(l, iFront, iBack);
}
} else {
ListIterator head = l.listIterator();
ListIterator tail = l.listIterator(l.size());
while (head.nextIndex() < tail.previousIndex()) {
Object headElem = head.next();
Object tailElem = tail.previous();
head.set(tailElem);
tail.set(headElem);
}
}
}
@SuppressWarnings("unchecked")
public static Comparator reverseOrder() {
return (Comparator) Comparator.reverseOrder();
}
public static Comparator reverseOrder(Comparator cmp) {
return cmp == null ? reverseOrder() : cmp.reversed();
}
/**
* Rotates the elements in {@code list} by the distance {@code dist}
*
* e.g. for a given list with elements [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], calling rotate(list, 3) or
* rotate(list, -7) would modify the list to look like this: [8, 9, 0, 1, 2, 3, 4, 5, 6, 7]
*
* @param lst the list whose elements are to be rotated.
* @param dist is the distance the list is rotated. This can be any valid integer. Negative values
* rotate the list backwards.
*/
@SuppressWarnings("unchecked")
public static void rotate(List> lst, int dist) {
checkNotNull(lst);
int size = lst.size();
// Rotating an empty collection results in the same empty collection
if (size == 0) {
return;
}
// Normalize the distance
int normdist = dist % size;
if (normdist == 0) {
return;
}
// Transform a rotation to the left into the equivalent rotation to the right.
if (normdist < 0) {
normdist += size;
}
if (lst instanceof RandomAccess) {
List