/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.collect; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.RandomAccess; import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; import org.andork.func.Mapper; import org.andork.func.Predicate; import org.andork.util.Java7; public class CollectionUtils { public static <T> void addAll(Collection<T> c, Iterable<? extends T> i) { for (T t : i) { c.add(t); } } public static <T> ArrayList<T> asArrayList(T... elements) { ArrayList<T> result = new ArrayList<T>(); for (T elem : elements) { result.add(elem); } return result; } public static <T> HashSet<T> asHashSet(T... elements) { HashSet<T> result = new HashSet<T>(); for (T elem : elements) { result.add(elem); } return result; } public static <T> Set<T> asImmutableHashSet(T... elements) { return Collections.unmodifiableSet(asHashSet(elements)); } public static <T, O extends Comparable<O>> int binarySearch(List<? extends T> l, int fromIndex, int toIndex, Mapper<T, ? extends O> mapper, O key) { if (l instanceof RandomAccess) { return indexedBinarySearch(l, fromIndex, toIndex - 1, mapper, key); } else { throw new UnsupportedOperationException("This method currently only supports RandomAccess lists"); } } public static <T, O extends Comparable<O>> int binarySearch(List<? extends T> l, Mapper<T, ? extends O> mapper, O key) { if (l instanceof RandomAccess) { return indexedBinarySearch(l, 0, l.size(), mapper, key); } else { throw new UnsupportedOperationException("This method currently only supports RandomAccess lists"); } } public static <T, O> int binarySearch(List<? extends T> l, Mapper<T, ? extends O> mapper, O key, Comparator<? super O> c) { if (l instanceof RandomAccess) { return indexedBinarySearch(l, mapper, key, c); } else { throw new UnsupportedOperationException("This method currently only supports RandomAccess lists"); } } /** * Finds the closest element to a given value in a list. * * @param list * the list to search through, sorted in ascending order. * @param value * the target value. * @return the element of {@code list} closest to {@code value}, or * {@code null} if {@code list} is empty. */ public static int findClosestIndex(List<Double> list, Double value) { if (list.isEmpty()) { return -1; } int index = Collections.binarySearch(list, value); if (index >= 0) { return index; } else { index = -(index + 1); if (index == list.size() || index > 0 && list.get(index) - value > value - list.get(index - 1)) { index--; } return index; } } /** * Gets the index of the first element in the given list of the given type. * * @param list * the list to search through. * @param type * the type to find. * @return the first element in {@code list} that is an instance of * {@code type}, or {@code null} if no such element is in * {@code list}. */ @SuppressWarnings("unchecked") public static <T, S extends T> S findInstance(List<T> list, Class<S> type) { int index = indexOfInstance(list, type); return index < 0 ? null : (S) list.get(index); } private static <T, O extends Comparable<O>> int indexedBinarySearch(List<? extends T> l, int low, int high, Mapper<T, ? extends O> mapper, O key) { while (low <= high) { int mid = low + high >>> 1; T midVal = l.get(mid); int cmp = mapper.map(midVal).compareTo(key); if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } private static <T, O> int indexedBinarySearch(List<? extends T> l, Mapper<T, ? extends O> mapper, O key, Comparator<? super O> c) { int low = 0; int high = l.size() - 1; while (low <= high) { int mid = low + high >>> 1; T midVal = l.get(mid); int cmp = c.compare(mapper.map(midVal), key); if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } public static <T, E extends T> int indexOf(List<T> list, E element, int startIndex) { while (startIndex < list.size()) { if (Java7.Objects.equals(list.get(startIndex), element)) { return startIndex; } startIndex++; } return -1; } public static <T> int indexOf(List<T> list, Predicate<? super T> p) { int i = 0; for (T t : list) { if (p.eval(t)) { return i; } i++; } return -1; } /** * Finds the index of the first element in the given list of the given type. * * @param list * the list to search through. * @param type * the type to find. * @return the index of the first element in {@code list} that is an * instance of {@code type}, or -1 if no such element is in * {@code list}. */ public static <T> int indexOfInstance(List<T> list, Class<? extends T> type) { int k = 0; for (T t : list) { if (type.isInstance(t)) { return k; } k++; } return -1; } public static <K, V> HashMap<V, K> invert(Map<K, V> in) { HashMap<V, K> result = new HashMap<V, K>(); for (Map.Entry<K, V> e : in.entrySet()) { result.put(e.getValue(), e.getKey()); } return result; } public static <K, V> void invert(Map<K, V> in, Map<V, K> out) { for (Map.Entry<K, V> e : in.entrySet()) { out.put(e.getValue(), e.getKey()); } } public static <K, V> Map<K, V> keyify(Stream<? extends V> stream, Function<? super V, K> keyAssigner) { Map<K, V> result = new LinkedHashMap<>(); stream.forEach(v -> result.put(keyAssigner.apply(v), v)); return result; } public static <T, E extends T> int lastIndexOf(List<T> list, E element, int startIndex) { while (startIndex >= 0) { if (Java7.Objects.equals(list.get(startIndex), element)) { return startIndex; } startIndex--; } return -1; } public static <T> int lastIndexOf(List<T> list, Predicate<? super T> p) { ListIterator<T> i = list.listIterator(list.size()); while (i.hasPrevious()) { if (p.eval(i.previous())) { return i.nextIndex(); } } return -1; } public static <I, O> O[] map(Mapper<I, O> mapper, I[] in) { O[] out = (O[]) new Object[in.length]; for (int i = 0; i < in.length; i++) { out[i] = mapper.map(in[i]); } return out; } public static <I, O> Iterable<O> map(final Mapper<I, O> mapper, final Iterable<? extends I> in) { return new Iterable<O>() { @Override public Iterator<O> iterator() { return new Iterator<O>() { Iterator<? extends I> inIter = in.iterator(); @Override public boolean hasNext() { return inIter.hasNext(); } @Override public O next() { return mapper.map(inIter.next()); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } /** * Moves elements matching the given predicate to the front of the list, and * returns the number of elements that matched the predicate. * * @param list * @param p * @return the number of elements that matched the predicate. */ public static <T> int moveToFront(List<T> list, Predicate<? super T> p) { int count = 0; ListIterator<T> frontIter = list.listIterator(); ListIterator<T> allIter = list.listIterator(); while (allIter.hasNext()) { T next = allIter.next(); if (p.eval(next)) { count++; T front = frontIter.next(); frontIter.set(next); allIter.set(front); } } return count; } public static <K, V> HashMap<K, V> mutableSingletonHashMap(K key, V value) { HashMap<K, V> result = new HashMap<K, V>(); result.put(key, value); return result; } public static <T> ArrayList<T> newArrayList() { return new ArrayList<T>(); } public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>(); } public static <K, V> LinkedHashMap<K, V> newLinkedHashMap() { return new LinkedHashMap<K, V>(); } public static <T> List<T> nullTolerantUnmodifiableList(List<? extends T> l) { return l == null ? Collections.<T> emptyList() : Collections.unmodifiableList(l); } public static <E> void removeAll(Iterable<E> c, java.util.function.Predicate<E> p) { Iterator<E> i = c.iterator(); while (i.hasNext()) { if (p.test(i.next())) { i.remove(); } } } public static <E> void removeAll(Iterable<E> c, Predicate<E> p) { Iterator<E> i = c.iterator(); while (i.hasNext()) { if (p.eval(i.next())) { i.remove(); } } } /** * Adds the first {@code count} elements of an {@link Iterable} to a * {@link Collection}, or all of the elements if there are fewer than * {@code count}. * * @param count * the maximum number of elements to take. * @param i * the {@link Iterable} to take elements from. * @param collection * the {@link Collection} to add the taken elements to. */ public static <T> void take(int count, Iterable<? extends T> i, Collection<T> collection) { for (T t : i) { if (count-- > 0) { collection.add(t); } else { break; } } } public static <T> void testComparator(List<? extends T> list, Comparator<T> comparator) { for (int i = 0; i < list.size(); i++) { for (int j = i; j < list.size(); j++) { T oi = list.get(i); T oj = list.get(j); int ci = comparator.compare(oi, oj); int cj = comparator.compare(oj, oi); if (Math.signum(ci) != -Math.signum(cj)) { throw new RuntimeException("Bad comparator: " + comparator + "\n\tlist.get(" + i + "): " + oi + "\n\tlist.get(" + j + "): " + oj + "\ncompare(list.get(" + i + "), list.get(" + j + ")): " + ci + "\ncompare(list.get(" + j + "), list.get(" + i + ")): " + cj); } } } } public static <T> ArrayList<T> toArrayList(Iterable<? extends T> i) { ArrayList<T> result = new ArrayList<T>(); addAll(result, i); return result; } public static <T> ArrayList<T> toArrayList(Stream<T> stream) { ArrayList<T> result = new ArrayList<T>(); stream.forEach(t -> result.add(t)); return result; } public static List<Double> toDoubles(Collection<? extends Number> numbers) { List<Double> result = new ArrayList<Double>(); for (Number number : numbers) { result.add(number.doubleValue()); } return result; } public static <T> HashSet<T> toHashSet(Stream<T> stream) { HashSet<T> result = new HashSet<T>(); stream.forEach(t -> result.add(t)); return result; } public static <T extends Comparable<T>> ArrayList<T> toSortedArrayList(Iterable<? extends T> i) { ArrayList<T> result = toArrayList(i); Collections.sort(result); return result; } public static <T> ArrayList<T> toSortedArrayList(Iterable<? extends T> i, Comparator<? super T> comparator) { ArrayList<T> result = toArrayList(i); Collections.sort(result, comparator); return result; } public static <T> List<T> toUnmodifiableList(Iterable<? extends T> i) { ArrayList<T> result = toArrayList(i); result.trimToSize(); return Collections.unmodifiableList(result); } }