package com.cooleshow.base.utils; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** *
 *     author: blankj
 *     blog  : http://blankj.com
 *     time  : 2019/07/26
 *     desc  : utils about collection
 * 
*/ public final class CollectionUtils { private CollectionUtils() { throw new UnsupportedOperationException("u can't instantiate me..."); } /////////////////////////////////////////////////////////////////////////// // listOf /////////////////////////////////////////////////////////////////////////// /** * Returns a new read-only list of given elements. * * @param array The array. * @return a new read-only list of given elements */ @SafeVarargs public static List newUnmodifiableList(E... array) { return Collections.unmodifiableList(newArrayList(array)); } /** * Returns a new read-only list only of those given elements, that are not null. * * @param array The array. * @return a new read-only list only of those given elements, that are not null */ @SafeVarargs public static List newUnmodifiableListNotNull(E... array) { return Collections.unmodifiableList(newArrayListNotNull(array)); } @SafeVarargs public static ArrayList newArrayList(E... array) { ArrayList list = new ArrayList<>(); if (array == null || array.length == 0) return list; for (E e : array) { list.add(e); } return list; } @SafeVarargs public static ArrayList newArrayListNotNull(E... array) { ArrayList list = new ArrayList<>(); if (array == null || array.length == 0) return list; for (E e : array) { if (e == null) continue; list.add(e); } return list; } @SafeVarargs public static LinkedList newLinkedList(E... array) { LinkedList list = new LinkedList<>(); if (array == null || array.length == 0) return list; for (E e : array) { list.add(e); } return list; } @SafeVarargs public static LinkedList newLinkedListNotNull(E... array) { LinkedList list = new LinkedList<>(); if (array == null || array.length == 0) return list; for (E e : array) { if (e == null) continue; list.add(e); } return list; } @SafeVarargs public static HashSet newHashSet(E... array) { HashSet set = new HashSet<>(); if (array == null || array.length == 0) return set; for (E e : array) { set.add(e); } return set; } @SafeVarargs public static HashSet newHashSetNotNull(E... array) { HashSet set = new HashSet<>(); if (array == null || array.length == 0) return set; for (E e : array) { if (e == null) continue; set.add(e); } return set; } @SafeVarargs public static TreeSet newTreeSet(Comparator comparator, E... array) { TreeSet set = new TreeSet<>(comparator); if (array == null || array.length == 0) return set; for (E e : array) { set.add(e); } return set; } @SafeVarargs public static TreeSet newTreeSetNotNull(Comparator comparator, E... array) { TreeSet set = new TreeSet<>(comparator); if (array == null || array.length == 0) return set; for (E e : array) { if (e == null) continue; set.add(e); } return set; } public static Collection newSynchronizedCollection(Collection collection) { return Collections.synchronizedCollection(collection); } public static Collection newUnmodifiableCollection(Collection collection) { return Collections.unmodifiableCollection(collection); } /** * Returns a {@link Collection} containing the union * of the given {@link Collection}s. *

* The cardinality of each element in the returned {@link Collection} * will be equal to the maximum of the cardinality of that element * in the two given {@link Collection}s. * * @param a the first collection * @param b the second collection * @return the union of the two collections * @see Collection#addAll */ public static Collection union(final Collection a, final Collection b) { if (a == null && b == null) return new ArrayList(); if (a == null) return new ArrayList(b); if (b == null) return new ArrayList(a); ArrayList list = new ArrayList<>(); Map mapA = getCardinalityMap(a); Map mapB = getCardinalityMap(b); Set elts = new HashSet(a); elts.addAll(b); for (Object obj : elts) { for (int i = 0, m = Math.max(getFreq(obj, mapA), getFreq(obj, mapB)); i < m; i++) { list.add(obj); } } return list; } /** * Returns a {@link Collection} containing the intersection * of the given {@link Collection}s. *

* The cardinality of each element in the returned {@link Collection} * will be equal to the minimum of the cardinality of that element * in the two given {@link Collection}s. * * @param a the first collection * @param b the second collection * @return the intersection of the two collections * @see Collection#retainAll */ public static Collection intersection(final Collection a, final Collection b) { if (a == null || b == null) return new ArrayList(); ArrayList list = new ArrayList<>(); Map mapA = getCardinalityMap(a); Map mapB = getCardinalityMap(b); Set elts = new HashSet(a); elts.addAll(b); for (Object obj : elts) { for (int i = 0, m = Math.min(getFreq(obj, mapA), getFreq(obj, mapB)); i < m; i++) { list.add(obj); } } return list; } private static int getFreq(final Object obj, final Map freqMap) { Integer count = (Integer) freqMap.get(obj); if (count != null) { return count; } return 0; } /** * Returns a {@link Collection} containing the exclusive disjunction * (symmetric difference) of the given {@link Collection}s. *

* The cardinality of each element e in the returned {@link Collection} * will be equal to * max(cardinality(e,a),cardinality(e,b)) - min(cardinality(e,a),cardinality(e,b)). *

* This is equivalent to * {@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)}) * or * {@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)}). * * @param a the first collection * @param b the second collection * @return the symmetric difference of the two collections */ public static Collection disjunction(final Collection a, final Collection b) { if (a == null && b == null) return new ArrayList(); if (a == null) return new ArrayList(b); if (b == null) return new ArrayList(a); ArrayList list = new ArrayList<>(); Map mapA = getCardinalityMap(a); Map mapB = getCardinalityMap(b); Set elts = new HashSet(a); elts.addAll(b); for (Object obj : elts) { for (int i = 0, m = ((Math.max(getFreq(obj, mapA), getFreq(obj, mapB))) - (Math.min(getFreq(obj, mapA), getFreq(obj, mapB)))); i < m; i++) { list.add(obj); } } return list; } /** * Returns a new {@link Collection} containing a - b. * The cardinality of each element e in the returned {@link Collection} * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * * @param a the collection to subtract from * @param b the collection to subtract * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(final Collection a, final Collection b) { if (a == null) return new ArrayList(); if (b == null) return new ArrayList(a); ArrayList list = new ArrayList(a); for (Object o : b) { list.remove(o); } return list; } /** * Returns true iff at least one element is in both collections. *

* In other words, this method returns true iff the * {@link #intersection} of coll1 and coll2 is not empty. * * @param coll1 the first collection * @param coll2 the first collection * @return true iff the intersection of the collections is non-empty * @see #intersection */ public static boolean containsAny(final Collection coll1, final Collection coll2) { if (coll1 == null || coll2 == null) return false; if (coll1.size() < coll2.size()) { for (Object o : coll1) { if (coll2.contains(o)) { return true; } } } else { for (Object o : coll2) { if (coll1.contains(o)) { return true; } } } return false; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Collection coll) { Map count = new HashMap<>(); if (coll == null) return count; for (Object obj : coll) { Integer c = count.get(obj); if (c == null) { count.put(obj, 1); } else { count.put(obj, c + 1); } } return count; } /** * Returns true iff a is a sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a. * * @param a the first (sub?) collection * @param b the second (super?) collection * @return true iff a is a sub-collection of b * @see #isProperSubCollection * @see Collection#containsAll */ public static boolean isSubCollection(final Collection a, final Collection b) { if (a == null || b == null) return false; Map mapA = getCardinalityMap(a); Map mapB = getCardinalityMap(b); for (Object obj : a) { if (getFreq(obj, mapA) > getFreq(obj, mapB)) { return false; } } return true; } /** * Returns true iff a is a proper sub-collection of b, * that is, iff the cardinality of e in a is less * than or equal to the cardinality of e in b, * for each element e in a, and there is at least one * element f such that the cardinality of f in b * is strictly greater than the cardinality of f in a. *

* The implementation assumes *

    *
  • a.size() and b.size() represent the * total cardinality of a and b, resp.
  • *
  • a.size() < Integer.MAXVALUE
  • *
* * @param a the first (sub?) collection * @param b the second (super?) collection * @return true iff a is a proper sub-collection of b * @see #isSubCollection * @see Collection#containsAll */ public static boolean isProperSubCollection(final Collection a, final Collection b) { if (a == null || b == null) return false; return a.size() < b.size() && isSubCollection(a, b); } /** * Returns true iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection * @param b the second collection * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if (a == null || b == null) return false; if (a.size() != b.size()) { return false; } else { Map mapA = getCardinalityMap(a); Map mapB = getCardinalityMap(b); if (mapA.size() != mapB.size()) { return false; } else { for (Object obj : mapA.keySet()) { if (getFreq(obj, mapA) != getFreq(obj, mapB)) { return false; } } return true; } } } /** * Returns the number of occurrences of obj in coll. * * @param obj the object to find the cardinality of * @param coll the collection to search * @return the the number of occurrences of obj in coll */ public static int cardinality(E obj, final Collection coll) { if (coll == null) return 0; if (coll instanceof Set) { return (coll.contains(obj) ? 1 : 0); } int count = 0; if (obj == null) { for (E e : coll) { if (e == null) { count++; } } } else { for (E e : coll) { if (obj.equals(e)) { count++; } } } return count; } /** * Finds the first element in the given collection which matches the given predicate. *

* If the input collection or predicate is null, or no element of the collection * matches the predicate, null is returned. * * @param collection the collection to search, may be null * @param predicate the predicate to use, may be null * @return the first element of the collection which matches the predicate or null if none could be found */ public static E find(Collection collection, Predicate predicate) { if (collection == null || predicate == null) return null; for (E item : collection) { if (predicate.evaluate(item)) { return item; } } return null; } /** * Executes the given closure on each element in the collection. *

* If the input collection or closure is null, there is no change made. * * @param collection the collection to get the input from, may be null * @param closure the closure to perform, may be null */ public static void forAllDo(Collection collection, Closure closure) { if (collection == null || closure == null) return; int index = 0; for (E e : collection) { closure.execute(index++, e); } } /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. *

* If the input collection or predicate is null, there is no change made. * * @param collection the collection to get the input from, may be null * @param predicate the predicate to use as a filter, may be null */ public static void filter(Collection collection, Predicate predicate) { if (collection == null || predicate == null) return; for (Iterator it = collection.iterator(); it.hasNext(); ) { if (!predicate.evaluate((E) it.next())) { it.remove(); } } } /** * Selects all elements from input collection which match the given predicate * into an output collection. *

* A null predicate matches no elements. * * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection select(Collection inputCollection, Predicate predicate) { if (inputCollection == null || predicate == null) return new ArrayList<>(); ArrayList answer = new ArrayList<>(inputCollection.size()); for (E o : inputCollection) { if (predicate.evaluate(o)) { answer.add(o); } } return answer; } /** * Selects all elements from inputCollection which don't match the given predicate * into an output collection. *

* If the input predicate is null, the result is an empty list. * * @param inputCollection the collection to get the input from, may not be null * @param predicate the predicate to use, may be null * @return the elements not matching the predicate (new list) * @throws NullPointerException if the input collection is null */ public static Collection selectRejected(Collection inputCollection, Predicate predicate) { if (inputCollection == null || predicate == null) return new ArrayList<>(); ArrayList answer = new ArrayList<>(inputCollection.size()); for (E o : inputCollection) { if (!predicate.evaluate(o)) { answer.add(o); } } return answer; } /** * Transform the collection by applying a Transformer to each element. *

* If the input collection or transformer is null, there is no change made. *

* This routine is best for Lists, for which set() is used to do the * transformations "in place." For other Collections, clear() and addAll() * are used to replace elements. *

* If the input collection controls its input, such as a Set, and the * Transformer creates duplicates (or are otherwise invalid), the * collection may reduce in size due to calling this method. * * @param collection the collection to get the input from, may be null * @param transformer the transformer to perform, may be null */ public static void transform(Collection collection, Transformer transformer) { if (collection == null || transformer == null) return; if (collection instanceof List) { List list = (List) collection; for (ListIterator it = list.listIterator(); it.hasNext(); ) { it.set(transformer.transform((E1) it.next())); } } else { Collection resultCollection = collect(collection, transformer); collection.clear(); collection.addAll(resultCollection); } } /** * Returns a new Collection consisting of the elements of inputCollection transformed * by the given transformer. *

* If the input transformer is null, the result is an empty list. * * @param inputCollection the collection to get the input from, may be null * @param transformer the transformer to use, may be null * @return the transformed result (new list) */ public static Collection collect(final Collection inputCollection, final Transformer transformer) { List answer = new ArrayList<>(); if (inputCollection == null || transformer == null) return answer; for (E1 e1 : inputCollection) { answer.add(transformer.transform(e1)); } return answer; } /** * Counts the number of elements in the input collection that match the predicate. *

* A null collection or predicate matches no elements. * * @param collection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @return the number of matches for the predicate in the collection */ public static int countMatches(Collection collection, Predicate predicate) { if (collection == null || predicate == null) return 0; int count = 0; for (E o : collection) { if (predicate.evaluate(o)) { count++; } } return count; } /** * Answers true if a predicate is true for at least one element of a collection. *

* A null collection or predicate returns false. * * @param collection the collection to get the input from, may be null * @param predicate the predicate to use, may be null * @return true if at least one element of the collection matches the predicate */ public static boolean exists(Collection collection, Predicate predicate) { if (collection == null || predicate == null) return false; for (E o : collection) { if (predicate.evaluate(o)) { return true; } } return false; } /** * Adds an element to the collection unless the element is null. * * @param collection the collection to add to, may be null * @param object the object to add, if null it will not be added * @return true if the collection changed */ public static boolean addIgnoreNull(Collection collection, E object) { if (collection == null) return false; return (object != null && collection.add(object)); } /** * Adds all elements in the iteration to the given collection. * * @param collection the collection to add to, may be null * @param iterator the iterator of elements to add, may be null */ public static void addAll(Collection collection, Iterator iterator) { if (collection == null || iterator == null) return; while (iterator.hasNext()) { collection.add(iterator.next()); } } /** * Adds all elements in the enumeration to the given collection. * * @param collection the collection to add to, may be null * @param enumeration the enumeration of elements to add, may be null */ public static void addAll(Collection collection, Enumeration enumeration) { if (collection == null || enumeration == null) return; while (enumeration.hasMoreElements()) { collection.add(enumeration.nextElement()); } } /** * Adds all elements in the array to the given collection. * * @param collection the collection to add to, may be null * @param elements the array of elements to add, may be null */ public static void addAll(Collection collection, E[] elements) { if (collection == null || elements == null || elements.length == 0) return; collection.addAll(Arrays.asList(elements)); } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. *

* The supported types, and associated semantics are: *

    *
  • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
  • *
  • List -- this method is equivalent to the list's get method.
  • *
  • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
  • *
  • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
  • *
  • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
  • *
* * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(Object object, int index) { if (object == null) return null; if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } if (object instanceof Map) { Map map = (Map) object; Iterator iterator = map.entrySet().iterator(); return get(iterator, index); } else if (object instanceof List) { return ((List) object).get(index); } else if (object instanceof Object[]) { return ((Object[]) object)[index]; } else if (object instanceof Iterator) { Iterator it = (Iterator) object; while (it.hasNext()) { index--; if (index == -1) { return it.next(); } else { it.next(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + index); } else if (object instanceof Collection) { Iterator iterator = ((Collection) object).iterator(); return get(iterator, index); } else if (object instanceof Enumeration) { Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { index--; if (index == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + index); } else { try { return Array.get(object, index); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Gets the size of the collection/iterator specified. *

* This method can handles objects as follows *

    *
  • Collection - the collection size *
  • Map - the map size *
  • Array - the array size *
  • Iterator - the number of elements remaining in the iterator *
  • Enumeration - the number of elements remaining in the enumeration *
* * @param object the object to get the size of * @return the size of the specified collection * @throws IllegalArgumentException thrown if object is not recognised or null */ public static int size(Object object) { if (object == null) return 0; int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else { try { total = Array.getLength(object); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Checks if the specified collection/array/iterator is empty. *

* This method can handles objects as follows *

    *
  • Collection - via collection isEmpty *
  • Map - via map isEmpty *
  • Array - using array size *
  • Iterator - via hasNext *
  • Enumeration - via hasMoreElements *
*

* Note: This method is named to avoid clashing with * {@link #isEmpty(Collection)}. * * @param object the object to get the size of, not null * @return true if empty * @throws IllegalArgumentException thrown if object is not recognised or null */ public static boolean sizeIsEmpty(Object object) { if (object == null) return true; if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return !((Iterator) object).hasNext(); } else if (object instanceof Enumeration) { return !((Enumeration) object).hasMoreElements(); } else { try { return Array.getLength(object) == 0; } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Null-safe check if the specified collection is empty. *

* Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null */ public static boolean isEmpty(Collection coll) { return coll == null || coll.size() == 0; } /** * Null-safe check if the specified collection is not empty. *

* Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty */ public static boolean isNotEmpty(Collection coll) { return !isEmpty(coll); } /** * Returns a collection containing all the elements in collection * that are also in retain. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless retain does not contain e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call c.retainAll(retain);. * * @param collection the collection whose contents are the target of the #retailAll operation * @param retain the collection containing the elements to be retained in the returned collection * @return a Collection containing all the elements of collection * that occur at least once in retain. */ public static Collection retainAll(Collection collection, Collection retain) { if (collection == null || retain == null) return new ArrayList<>(); List list = new ArrayList<>(); for (E item : collection) { if (retain.contains(item)) { list.add(item); } } return list; } /** * Removes the elements in remove from collection. That is, this * method returns a collection containing all the elements in c * that are not in remove. The cardinality of an element e * in the returned collection is the same as the cardinality of e * in collection unless remove contains e, in which * case the cardinality is zero. This method is useful if you do not wish to modify * the collection c and thus cannot call collection.removeAll(remove);. * * @param collection the collection from which items are removed (in the returned collection) * @param remove the items to be removed from the returned collection * @return a Collection containing all the elements of collection except * any elements that also occur in remove. */ public static Collection removeAll(Collection collection, Collection remove) { if (collection == null) return new ArrayList<>(); if (remove == null) return new ArrayList<>(collection); List list = new ArrayList<>(); for (E obj : collection) { if (!remove.contains(obj)) { list.add(obj); } } return list; } /** * Randomly permutes the specified list using a default source of randomness. * * @param list the list to be shuffled. * @throws UnsupportedOperationException if the specified list or * its list-iterator does not support the set operation. */ public static void shuffle(List list) { Collections.shuffle(list); } /** * Return the string of collection. * * @param collection The collection. * @return the string of collection */ public static String toString(Collection collection) { if (collection == null) return "null"; return collection.toString(); } public interface Closure { void execute(int index, E item); } public interface Transformer { E2 transform(E1 input); } public interface Predicate { boolean evaluate(E item); } }