/******************************************************************************* * Copyright (c) 2007 DSource.org and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial implementation *******************************************************************************/ package melnorme.utilbox.misc; import static melnorme.utilbox.core.CoreUtil.areEqual; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.collections.HashSet2; import melnorme.utilbox.collections.Indexable; import melnorme.utilbox.core.fntypes.FunctionX; /** * Utils for creation, query, and modification of Collection classes. */ public class CollectionUtil { @SuppressWarnings("rawtypes") public static final Indexable EMPTY_INDEXABLE = new ArrayList2<>(); public static <T> Indexable<T> nullToEmpty(Indexable<T> indexable) { return Indexable.nullToEmpty(indexable); } /** @return a new {@link ArrayList} from given collection * (a null collection is considered as if it's an empty one). */ public static <E> ArrayList2<E> createArrayList(Iterable<? extends E> iterable) { return new ArrayList2<>(nullToEmpty(iterable)); } /** @return a new {@link LinkedList} from given collection * (a null collection is considered as if it's an empty one). */ public static <E> LinkedList<E> createLinkedList(Iterable<? extends E> iterable) { return addAll2(new LinkedList<E>(), nullToEmpty(iterable)); } /** @return a new {@link HashSet} from given collection * (a null collection is considered as if it's an empty one). */ public static <E> HashSet2<E> createHashSet(Iterable<? extends E> iterable) { return new HashSet2<>(nullToEmpty(iterable)); } /** @return a new {@link ArrayList2} from given array (a null array is considered like it's an empty one). */ @SafeVarargs public static <E> ArrayList2<E> createArrayList(E... array) { ArrayList2<E> newCollection = new ArrayList2<E>(); if(array != null) { Collections.addAll(newCollection, array); } return newCollection; } /** @return a new {@link LinkedList} from given array (a null array is considered like it's an empty one). */ @SafeVarargs public static <E> LinkedList<E> createLinkedList(E... array) { LinkedList<E> newCollection = new LinkedList<E>(); if(array != null) { Collections.addAll(newCollection, array); } return newCollection; } /** @return a new {@link HashSet} from given array (a null array is considered like it's an empty one). */ @SafeVarargs public static <E> HashSet2<E> createHashSet(E... array) { HashSet2<E> newCollection = new HashSet2<E>(); if(array != null) { Collections.addAll(newCollection, array); } return newCollection; } /** @return given iterable if it's non-null, an empty immutable {@link Iterable} otherwise. */ public static <E> Iterable<E> nullToEmpty(Iterable<E> iterable) { return iterable == null ? Collections.EMPTY_LIST : iterable; } /** @return given coll if it's non-null, an empty immutable {@link Collection} otherwise. */ public static <E> Collection<E> nullToEmpty(Collection<E> coll) { return coll == null ? Collections.EMPTY_LIST : coll; } /** @return given list if it's non-null, an empty immutable {@link List} otherwise. */ public static <E> List<E> nullToEmpty(List<E> list) { return list == null ? Collections.EMPTY_LIST : list; } /* ----------------- query ----------------- */ public static <T> int indexOfSame(Iterable<? extends T> iterable, T obj) { return indexOfSame(iterable.iterator(), obj); } public static <T> int indexOfSame(Iterator<? extends T> iterator, T obj) { int ix = 0; while(iterator.hasNext()) { T element = iterator.next(); if(element == obj) return ix; ix++; } return -1; } public static <T> int indexOf(Iterable<? extends T> iterable, T obj) { return indexOf(iterable.iterator(), obj); } public static <T> int indexOf(Iterator<? extends T> iterator, T obj) { int ix = 0; while(iterator.hasNext()) { T element = iterator.next(); if(areEqual(element, obj)) { return ix; } ix++; } return -1; } /** * @return the index in the iteration order of the first element that matches given predicate, or -1 otherwise. */ public static <T> int indexUntil(Iterator<? extends T> iterator, Predicate<T> predicate) { int ix = 0; while(iterator.hasNext()) { T element = iterator.next(); if(predicate.test(element)) { return ix; } ix++; } return -1; } // BM: is there no function in the JVM standard lib that does this?? /** * @return the first element to match given predicate, null otherwise. */ public static <T> T findElement(Iterator<? extends T> iterator, Predicate<? super T> predicate) { while(iterator.hasNext()) { T element = iterator.next(); if(predicate.test(element)) { return element; } } return null; } /** @return whether given coll contains given obj * (obj must be the same as the one contained, not just equal). */ public static boolean containsSame(Iterable<?> coll, Object obj) { for(Iterator<?> iterator = coll.iterator(); iterator.hasNext();) { Object element = iterator.next(); if(element == obj) return true; } return false; } /** @return whether given coll contains all elements of other, using a naive/basic algorithm. */ public static boolean containsAll(Collection<?> coll, Collection<?> other) { for(Object otherElement : other) { if(!coll.contains(otherElement)) return false; } return true; } /** * @return the first element of given coll, if collection is non-null and non-empty. Otherwise null. */ public static <T> T getSingleElementOrNull(Iterable<T> coll) { if(coll == null) return null; Iterator<T> iterator = coll.iterator(); T firstElement = iterator.next(); return iterator.hasNext() ? null : firstElement; } /* ----------------- modifications ----------------- */ /** Creates a List copy of orig, with all elements except elements equal to excludedElem. */ public static <E> ArrayList2<E> copyExcept(E[] orig, E excludedElem) { ArrayList2<E> rejectedElements = new ArrayList2<E>(orig.length); for (int i= 0; i < orig.length; i++) { if (!orig[i].equals(excludedElem)) { rejectedElements.add(orig[i]); } } return rejectedElements; } public static <ELEM, COLL extends Collection<? super ELEM>> COLL addAll2(COLL dest, Iterable<? extends ELEM> source) { return addAllFromIterator(dest, source.iterator()); } public static <ELEM, COLL extends Collection<? super ELEM>> COLL addAllFromIterator(COLL dest, Iterator<? extends ELEM> source) { while(source.hasNext()) { dest.add(source.next()); } return dest; } public static <ELEM, COLL extends Collection<? super ELEM>, SourceELEM> COLL addAll(COLL dest, Iterable<? extends SourceELEM> source, Function<SourceELEM, ELEM> mapper) { for(SourceELEM elem : source) { dest.add(mapper.apply(elem)); } return dest; } /** Remove from given list all elements that match given predicate. */ public static <E, L extends Iterable<E>> L filter(L list, Predicate<E> predicate) { for (Iterator<? extends E> iter = list.iterator(); iter.hasNext(); ) { E obj = iter.next(); if(predicate.test(obj)) { iter.remove(); } } return list; } public static <ELEM, INTO extends Collection<ELEM>> INTO filter2( INTO into, Iterable<? extends ELEM> from, Predicate<ELEM> predicate ) { for (ELEM elem : from) { if(predicate.test(elem)) { into.add(elem); } } return into; } public static <ELEM, INTO extends Collection<ELEM>, EXC extends Exception> INTO filter2x( INTO into, Iterable<? extends ELEM> from, FunctionX<ELEM, Boolean, EXC> predicate ) throws EXC { for (ELEM elem : from) { if(predicate.apply(elem) == Boolean.TRUE) { into.add(elem); } } return into; } /** Removes from given list the first element that matches given predicate. * @return true if an element was removed, false otherwise. */ public static <E> boolean removeElement(List<? extends E> list, Predicate<E> predicate) { for (Iterator<? extends E> iter = list.iterator(); iter.hasNext(); ) { E obj = iter.next(); if(predicate.test(obj)) { iter.remove(); return true; } } return false; } /** Sorts given list and returns it. */ public static <E extends Comparable<? super E>, L extends List<E>> L sort(L list) { Collections.sort(list); return list; } @SuppressWarnings("unused") private static void sort__genericsTestCompile() { List<? extends Integer> list1 = null; ArrayList<? extends Integer> list2 = null; list1 = sort(list1); list2 = sort(list2); } /* ----------------- functional API ----------------- */ // We might want to modify this to use Java 8 Streams public static <E, R> ArrayList2<R> map( Iterable<E> coll, Function<? super E, ? extends R> evalFunction) { return mapTo(coll, evalFunction, new ArrayList2<R>()); } public static <E, R, DEST extends Collection<R>> DEST mapTo( Iterable<E> coll, Function<? super E, ? extends R> evalFunction, DEST destCollection) { for(E collElement : coll) { R mappeElem = evalFunction.apply(collElement); destCollection.add(mappeElem); } return destCollection; } public static <E, R, EXC extends Exception> ArrayList2<R> mapx( Iterable<E> coll, FunctionX<? super E, ? extends R, EXC> evalFunction) throws EXC { return mapxTo(coll, evalFunction, new ArrayList2<R>()); } public static <E, R, EXC extends Exception, DEST extends Collection<R>> DEST mapxTo( Iterable<E> coll, FunctionX<? super E, ? extends R, EXC> evalFunction, DEST destCollection) throws EXC { for(E collElement : coll) { R mappeElem = evalFunction.apply(collElement); destCollection.add(mappeElem); } return destCollection; } public static boolean listEquals(Indexable<?> coll1, Indexable<?> coll2) { return Indexable.indexableEquals(coll1, coll2); } public static boolean listEquals(List<?> coll1, List<?> coll2) { if(coll1.size() != coll2.size()) { return false; } return iterationEquals(coll1.iterator(), coll2.iterator()); } public static boolean iterationEquals(Iterator<?> iter1, Iterator<?> iter2) { while(iter1.hasNext() && iter2.hasNext()) { Object o1 = iter1.next(); Object o2 = iter2.next(); if(!areEqual(o1, o2)) { return false; } } return !(iter1.hasNext() || iter2.hasNext()); } }