blob: 252236b62aa7ccebba1c965abc4ef595906359f8 [file] [log] [blame]
/*
* 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;
/**
* Utility methods that operate on collections. <a
* href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html">[Sun
* docs]</a>
*/
public class Collections {
/**
* Used to implement iterators on unmodifiable lists.
*
* @param <E> element type.
*/
private static class UnmodifiableListIterator<E> implements ListIterator<E> {
final ListIterator<? extends E> it;
public UnmodifiableListIterator(ListIterator<? extends E> it) {
this.it = it;
}
public void add(E o) {
throw new UnsupportedOperationException(
"UnmodifiableListIterator: add not permitted");
}
public boolean hasNext() {
return it.hasNext();
}
public boolean hasPrevious() {
return it.hasPrevious();
}
public E next() {
return it.next();
}
public int nextIndex() {
return it.nextIndex();
}
public E previous() {
return it.previous();
}
public int previousIndex() {
return it.previousIndex();
}
public void remove() {
throw new UnsupportedOperationException(
"UnmodifiableListIterator: remove not permitted");
}
public void set(E o) {
throw new UnsupportedOperationException(
"UnmodifiableListIterator: set not permitted");
}
}
public static final List<?> EMPTY_LIST = unmodifiableList(new ArrayList<Object>());
public static final Map<?, ?> EMPTY_MAP = unmodifiableMap(new HashMap<Object, Object>());
public static final Set<?> EMPTY_SET = unmodifiableSet(new HashSet<Object>());
private static Comparator<Comparable<Object>> reverseComparator = new Comparator<Comparable<Object>>() {
public int compare(Comparable<Object> o1, Comparable<Object> o2) {
return o2.compareTo(o1);
}
};
public static <T> boolean addAll(Collection<? super T> c, T... a) {
boolean result = false;
for (T e : a) {
result |= c.add(e);
}
return result;
}
/**
* Perform a binary search on a sorted List, using natural ordering.
*
* <p>
* 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.
* </p>
*
* @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 <code>key</code> is not comparable to
* <code>sortedList</code>'s elements.
*/
public static <T> 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 <E> Collection<E> checkedCollection(Collection<E> c, Class<E>
// type) {
// // FUTURE: implement
// return null;
// }
//
// static <E> List<E> checkedList(List<E> list, Class<E> type) {
// // FUTURE: implement
// return null;
// }
//
// public static <K,V> Map<K,V> checkedMap(Map<K,V> list, Class<K> keyType,
// Class<V> valueType) {
// // FUTURE: implement
// return null;
// }
//
// public static <E> Set<E> checkedSet(Set<E> list, Class<E> type) {
// // FUTURE: implement
// return null;
// }
//
// public static <K,V> SortedMap<K,V> checkedSortedMap(SortedMap<K,V> m,
// Class<K> keyType, Class<V> valueType) {
// // FUTURE: implement
// return null;
// }
//
// public static <E> SortedSet<E> checkedSortedSet(SortedSet<E> list, Class<E>
// type) {
// // FUTURE: implement
// return null;
// }
/**
* Perform a binary search on a sorted List, using a user-specified comparison
* function.
*
* <p>
* 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.
* </p>
*
* @param sortedList List to search
* @param key value to search for
* @param comparator comparision function, <code>null</code> indicates
* <i>natural ordering</i> 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 <code>key</code> and
* <code>sortedList</code>'s elements cannot be compared by
* <code>comparator</code>.
*/
public static <T> 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.
*/
if (comparator == null) {
comparator = Comparators.natural();
}
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 <T> void copy(List<? super T> dest, List<? extends T> src) {
// TODO(jat): optimize
dest.addAll(src);
}
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("unchecked")
public static <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> emptyMap() {
return (Map<K, V>) EMPTY_MAP;
}
@SuppressWarnings("unchecked")
public static <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
public static <T> Enumeration<T> enumeration(Collection<T> c) {
final Iterator<T> it = c.iterator();
return new Enumeration<T>() {
public boolean hasMoreElements() {
return it.hasNext();
}
public T nextElement() {
return it.next();
}
};
}
public static <T> 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 (o == null ? e == null : o.equals(e)) {
++count;
}
}
return count;
}
public static <T extends Object & Comparable<? super T>> T max(
Collection<? extends T> coll) {
return max(coll, null);
}
public static <T> T max(Collection<? extends T> coll,
Comparator<? super T> comp) {
if (comp == null) {
comp = Comparators.natural();
}
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 extends Object & Comparable<? super T>> T min(
Collection<? extends T> coll) {
return min(coll, null);
}
public static <T> T min(Collection<? extends T> coll,
Comparator<? super T> comp) {
return max(coll, reverseOrder(comp));
}
public static <T> List<T> nCopies(int n, T o) {
ArrayList<T> list = new ArrayList<T>();
for (int i = 0; i < n; ++i) {
list.add(o);
}
return unmodifiableList(list);
}
public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) {
boolean modified = false;
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
T t = it.next();
if (t == null ? oldVal == null : t.equals(oldVal)) {
it.set(newVal);
modified = true;
}
}
return modified;
}
public static <T> void reverse(List<T> l) {
if (l instanceof RandomAccess) {
for (int iFront = 0, iBack = l.size() - 1; iFront < iBack; ++iFront, --iBack) {
Collections.swap(l, iFront, iBack);
}
} else {
ListIterator<T> head = l.listIterator();
ListIterator<T> tail = l.listIterator(l.size());
while (head.nextIndex() < tail.previousIndex()) {
T headElem = head.next();
T tailElem = tail.previous();
head.set(tailElem);
tail.set(headElem);
}
}
}
@SuppressWarnings("unchecked")
public static <T> Comparator<T> reverseOrder() {
return (Comparator<T>) reverseComparator;
}
public static <T> Comparator<T> reverseOrder(final Comparator<T> cmp) {
if (cmp == null) {
return reverseOrder();
}
return new Comparator<T>() {
public int compare(T t1, T t2) {
return cmp.compare(t2, t1);
}
};
}
// TODO(tobyr) Is it worth creating custom singleton sets, lists, and maps?
// More efficient at runtime, but more code bloat to download
public static <T> Set<T> singleton(T o) {
HashSet<T> set = new HashSet<T>(1);
set.add(o);
return unmodifiableSet(set);
}
public static <T> List<T> singletonList(T o) {
List<T> list = new ArrayList<T>(1);
list.add(o);
return unmodifiableList(list);
}
public static <K, V> Map<K, V> singletonMap(K key, V value) {
Map<K, V> map = new HashMap<K, V>(1);
map.put(key, value);
return unmodifiableMap(map);
}
public static <T> void sort(List<T> target) {
Object[] x = target.toArray();
Arrays.sort(x);
replaceContents(target, x);
}
public static <T> void sort(List<T> target, Comparator<? super T> c) {
Object[] x = target.toArray();
Arrays.sort(x, (Comparator<Object>) c);
replaceContents(target, x);
}
public static void swap(List<?> list, int i, int j) {
swapImpl(list, i, j);
}
public static <T> Collection<T> unmodifiableCollection(
final Collection<? extends T> coll) {
return new Collection<T>() {
public boolean add(T o) {
throw new UnsupportedOperationException(
"unmodifiableCollection: add not permitted");
}
public boolean addAll(Collection<? extends T> c) {
throw new UnsupportedOperationException(
"unmodifiableCollection: addAll not permitted");
}
public void clear() {
throw new UnsupportedOperationException(
"unmodifiableCollection: clear not permitted");
}
public boolean contains(Object o) {
return coll.contains(o);
}
public boolean containsAll(Collection<?> c) {
return coll.containsAll(c);
}
public boolean isEmpty() {
return coll.isEmpty();
}
public Iterator<T> iterator() {
final Iterator<? extends T> it = coll.iterator();
return new Iterator<T>() {
public boolean hasNext() {
return it.hasNext();
}
public T next() {
return it.next();
}
public void remove() {
throw new UnsupportedOperationException(
"unmodifiableCollection.iterator: remove not permitted");
}
};
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"unmodifiableCollection: remove not permitted");
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableCollection: removeAll not permitted");
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableCollection: retainAll not permitted");
}
public int size() {
return coll.size();
}
public Object[] toArray() {
return coll.toArray();
}
public <OT> OT[] toArray(OT[] a) {
return coll.toArray(a);
}
};
}
public static <T> List<T> unmodifiableList(final List<? extends T> list) {
return new List<T>() {
public void add(int index, T element) {
throw new UnsupportedOperationException(
"unmodifiableList: add not permitted");
}
public boolean add(T o) {
throw new UnsupportedOperationException(
"unmodifiableList: add not permitted");
}
public boolean addAll(Collection<? extends T> c) {
throw new UnsupportedOperationException(
"unmodifiableList: addAll not permitted");
}
public boolean addAll(int index, Collection<? extends T> c) {
throw new UnsupportedOperationException(
"unmodifiableList: addAll not permitted");
}
public void clear() {
throw new UnsupportedOperationException(
"unmodifiableList: clear not permitted");
}
public boolean contains(Object o) {
return list.contains(o);
}
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
public T get(int index) {
return list.get(index);
}
public int indexOf(Object o) {
return list.indexOf(o);
}
public boolean isEmpty() {
return list.isEmpty();
}
public Iterator<T> iterator() {
return listIterator();
}
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
public ListIterator<T> listIterator() {
return new UnmodifiableListIterator<T>(list.listIterator());
}
public ListIterator<T> listIterator(int from) {
return new UnmodifiableListIterator<T>(list.listIterator(from));
}
public T remove(int index) {
throw new UnsupportedOperationException(
"unmodifiableList: remove not permitted");
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"unmodifiableList: remove not permitted");
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableList: removeAll not permitted");
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableList: retainAll not permitted");
}
public T set(int index, T element) {
throw new UnsupportedOperationException(
"unmodifiableList: set not permitted");
}
public int size() {
return list.size();
}
public List<T> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException(
"unmodifiableList: subList not permitted");
}
public Object[] toArray() {
return list.toArray();
}
public <OT> OT[] toArray(OT[] array) {
return list.toArray(array);
}
};
};
public static <K, V> Map<K, V> unmodifiableMap(
final Map<? extends K, ? extends V> map) {
return new Map<K, V>() {
public void clear() {
throw new UnsupportedOperationException(
"unmodifiableMap: clear not permitted");
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<K, V>> entrySet() {
Set<? extends Map.Entry<? extends K, ? extends V>> entrySet = map.entrySet();
return (Set<Map.Entry<K, V>>) entrySet;
}
public V get(Object key) {
return map.get(key);
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<K> keySet() {
return (Set<K>) map.keySet();
}
public V put(K key, V value) {
throw new UnsupportedOperationException(
"unmodifiableMap: put not permitted");
}
public void putAll(Map<? extends K, ? extends V> t) {
throw new UnsupportedOperationException(
"unmodifiableMap: putAll not permitted");
}
public V remove(Object key) {
throw new UnsupportedOperationException(
"unmodifiableMap: remove not permitted");
}
public int size() {
return map.size();
}
public Collection<V> values() {
return (Collection<V>) map.values();
}
};
}
public static <T> Set<T> unmodifiableSet(final Set<? extends T> set) {
return new Set<T>() {
public boolean add(T o) {
throw new UnsupportedOperationException(
"unmodifiableSet: add not permitted");
}
public boolean addAll(Collection<? extends T> c) {
throw new UnsupportedOperationException(
"unmodifiableSet: addAll not permitted");
}
public void clear() {
throw new UnsupportedOperationException(
"unmodifiableSet: clear not permitted");
}
public boolean contains(Object o) {
return set.contains(o);
}
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public boolean isEmpty() {
return set.isEmpty();
}
public Iterator<T> iterator() {
final Iterator<? extends T> it = set.iterator();
return new Iterator<T>() {
public boolean hasNext() {
return it.hasNext();
}
public T next() {
return it.next();
}
public void remove() {
throw new UnsupportedOperationException(
"unmodifiableCollection.iterator: remove not permitted");
}
};
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"unmodifiableSet: remove not permitted");
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableSet: removeAll not permitted");
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException(
"unmodifiableSet: retainAll not permitted");
}
public int size() {
return set.size();
}
public Object[] toArray() {
return set.toArray();
}
public <OT> OT[] toArray(OT[] a) {
return set.toArray(a);
}
};
}
public static <K, V> SortedMap<K, V> unmodifiableSortedMap(
SortedMap<? extends K, ? extends V> map) {
throw new UnsupportedOperationException(
"unmodifiableSortedMap not implemented");
}
public static <T> SortedSet<T> unmodifiableSortedSet(
SortedSet<? extends T> set) {
throw new UnsupportedOperationException(
"unmodifiableSortedSet not implemented");
}
/**
* Replace contents of a list from an array.
*
* @param <T> element type
* @param target list to replace contents from an array
* @param x an Object array which can contain only T instances
*/
@SuppressWarnings("unchecked")
private static <T> void replaceContents(List<T> target, Object[] x) {
int size = target.size();
assert (x.length == size);
for (int i = 0; i < size; i++) {
target.set(i, (T) x[i]);
}
}
private static <T> void swapImpl(List<T> list, int i, int j) {
T t = list.get(i);
list.set(i, list.get(j));
list.set(j, t);
}
}