package com.aggrepoint.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
*
* @author jiangmingyang
*/
public class CollectionUtils {
public static <T> T first(Collection<T> entities,
Function<T, Boolean> compare) {
if (entities == null)
return null;
for (T t : entities)
if (compare.apply(t))
return t;
return null;
}
public static <T> boolean firstThen(Collection<T> entities,
Function<T, Boolean> compare, Consumer<T> then) {
if (entities == null)
return false;
T t = first(entities, compare);
if (t == null)
return false;
then.accept(t);
return true;
}
public static <T, K> List<K> toList(Collection<T> entities,
Function<T, K> mapper) {
if (entities == null)
return null;
return entities.stream().map(mapper).filter(p -> p != null)
.collect(Collectors.toList());
}
public static <T, K> List<K> toList(Collection<T> entities,
Predicate<T> predicate, Function<T, K> mapper) {
if (entities == null)
return null;
return entities.stream().filter(predicate).map(mapper)
.filter(p -> p != null).collect(Collectors.toList());
}
public static <T, K> Set<K> toSet(Collection<T> entities,
Function<T, K> mapper) {
if (entities == null)
return null;
return entities.stream().map(mapper).filter(p -> p != null)
.collect(Collectors.toSet());
}
public static <T, K, M extends Map<K, T>> M toMap(Collection<T> entities,
Function<T, K> keyMapper, Supplier<M> supplier) {
if (entities == null)
return null;
return entities.stream().collect(
Collectors.toMap(keyMapper, Function.identity(), (a, b) -> b,
supplier));
}
public static <T, K, V, M extends Map<K, V>> M toMap(
Collection<T> entities, Function<T, K> keyMapper,
Function<T, V> valueMapper, Supplier<M> supplier) {
if (entities == null)
return null;
return entities.stream()
.collect(
Collectors.toMap(keyMapper, valueMapper, (a, b) -> b,
supplier));
}
/**
* Convert a collection to HashMap, The key is decided by keyMapper, value
* is the object in collection
*
* @param entities
* @param keyMapper
* @return
*/
public static <T, K> HashMap<K, T> toHashMap(Collection<T> entities,
Function<T, K> keyMapper) {
return toMap(entities, keyMapper, HashMap<K, T>::new);
}
public static <T, K> HashMap<K, T> toHashMap(T[] entities,
Function<T, K> keyMapper) {
return toHashMap(Arrays.asList(entities), keyMapper);
}
public static <T, K, V> HashMap<K, V> toHashMap(Collection<T> entities,
Function<T, K> keyMapper, Function<T, V> valueMapper) {
return toMap(entities, keyMapper, valueMapper, HashMap<K, V>::new);
}
public static <T, K, V> HashMap<K, V> toHashMap(T[] entities,
Function<T, K> keyMapper, Function<T, V> valueMapper) {
return toHashMap(Arrays.asList(entities), keyMapper, valueMapper);
}
/**
* Convert a collection to Hashtable. The key is decided by keyMapper, value
* is the object in collection
*
* @param entities
* @param keyMapper
* @return
*/
public static <T, K> Hashtable<K, T> toHashtable(Collection<T> entities,
Function<T, K> keyMapper) {
return toMap(entities, keyMapper, Hashtable<K, T>::new);
}
public static <T, K> Hashtable<K, T> toHashtable(T[] entities,
Function<T, K> keyMapper) {
return toHashtable(Arrays.asList(entities), keyMapper);
}
public static <T, K, V> Hashtable<K, V> toHashtable(Collection<T> entities,
Function<T, K> keyMapper, Function<T, V> valueMapper) {
return toMap(entities, keyMapper, valueMapper, Hashtable<K, V>::new);
}
public static <T, K, V> Hashtable<K, V> toHashtable(T[] entities,
Function<T, K> keyMapper, Function<T, V> valueMapper) {
return toHashtable(Arrays.asList(entities), keyMapper, valueMapper);
}
public static <T, K, V, C extends Collection<V>> HashMap<K, C> group(
Collection<T> entities, Function<T, K> keyMapper,
Function<T, V> valueMapper, Supplier<C> collectionFactory) {
if (entities == null)
return null;
return entities.stream().collect(
Collectors.groupingBy(
keyMapper,
HashMap::new,
Collectors.mapping(valueMapper,
Collectors.toCollection(collectionFactory))));
}
public static <T, K, V> HashMap<K, HashSet<V>> group(
Collection<T> entities, Function<T, K> keyMapper,
Function<T, V> valueMapper) {
return group(entities, keyMapper, valueMapper, HashSet::new);
}
public static <T, K> HashMap<K, List<T>> groupAsList(
Collection<T> entities, Function<T, K> keyMapper) {
return group(entities, keyMapper, Function.identity(), ArrayList::new);
}
public static <T, K, V> HashMap<K, List<V>> groupAsList(
Collection<T> entities, Function<T, K> keyMapper,
Function<T, V> valueMapper) {
return group(entities, keyMapper, valueMapper, ArrayList::new);
}
public static <T, K, V> HashMap<K, String> groupJoining(
Collection<T> entities, Function<T, K> keyMapper,
Function<T, String> valueMapper, String delimiter) {
if (entities == null)
return null;
return entities.stream().collect(
Collectors.groupingBy(
keyMapper,
HashMap::new,
Collectors.mapping(valueMapper,
Collectors.joining(delimiter))));
}
public static <T, K, V> HashMap<K, HashSet<V>> group(T[] entities,
Function<T, K> keyMapper, Function<T, V> valueMapper) {
return group(Arrays.asList(entities), keyMapper, valueMapper);
}
/**
* Grouping into 2 levels
*
* @param entities
* @param keyMapper1
* @param keyMapper2
* @param valueMapper
* @param collectionFactory
* @return
*/
public static <T, K, V, P, C extends Collection<V>> HashMap<K, HashMap<P, C>> groupTwice(
Collection<T> entities, Function<T, K> keyMapper1,
Function<T, P> keyMapper2, Function<T, V> valueMapper,
Supplier<C> collectionFactory) {
if (entities == null)
return null;
return entities.stream().collect(
Collectors.groupingBy(keyMapper1, HashMap::new, Collectors
.groupingBy(keyMapper2, HashMap::new, Collectors
.mapping(valueMapper, Collectors
.toCollection(collectionFactory)))));
}
/**
* Similar to grouping into 2 levels except each unit is not a Collection
* but a single object
*
* @param entities
* @param keyMapper1
* @param keyMapper2
* @param valueMapper
* @return
*/
public static <T, K, V, P> HashMap<K, HashMap<P, V>> matrix(
Collection<T> entities, Function<T, K> keyMapper1,
Function<T, P> keyMapper2, Function<T, V> valueMapper) {
if (entities == null)
return null;
return entities.stream().collect(
Collectors.groupingBy(keyMapper1, HashMap::new, Collectors
.toMap(keyMapper2, valueMapper, (a, b) -> a,
HashMap::new)));
}
/**
* Extract attribute from objects in collection and store them in array and
* return.
*
* @param entities
* @param keyMapper
* @param arr
* @return
*/
public static <T, K> K[] toArray(Collection<T> entities,
Function<T, K> keyMapper, K[] arr) {
if (entities == null)
return null;
return entities.stream().map(keyMapper).toArray(size -> arr);
}
/**
* Move an object in List up
*
* @param list
* @param key
* @param keyMapper
* @return
*/
public static <T, K> boolean moveUp(List<T> list, K key,
Function<T, K> keyMapper, int n) {
if (list == null)
return false;
ArrayList<T> newList = new ArrayList<T>();
boolean changed = false;
for (int i = 0; i < list.size(); i++) {
T item = list.get(i);
if (i > 0 && key.equals(keyMapper.apply(item))) {
int posi = i - n;
if (posi < 0)
posi = 0;
newList.add(posi, item);
changed = true;
} else
newList.add(item);
}
if (changed) {
list.clear();
list.addAll(newList);
return true;
}
return false;
}
public static <T, K> boolean moveUp(List<T> list, K key,
Function<T, K> keyMapper) {
return moveUp(list, key, keyMapper, 1);
}
/**
* Move an object in List down
*
* @param list
* @param key
* @param keyMapper
* @return
*/
public static <T, K> boolean moveDown(List<T> list, K key,
Function<T, K> keyMapper, int n) {
if (list == null)
return false;
ArrayList<T> newList = new ArrayList<T>();
boolean changed = false;
int start = list.size() - 1;
for (int i = start; i >= 0; i--) {
T item = list.get(i);
if (i != start && key.equals(keyMapper.apply(item))) {
int posi = n;
if (newList.size() < posi)
posi = newList.size();
newList.add(posi, item);
changed = true;
} else
newList.add(0, item);
}
if (changed) {
list.clear();
list.addAll(newList);
return true;
}
return false;
}
public static <T, K> boolean moveDown(List<T> list, K key,
Function<T, K> keyMapper) {
return moveDown(list, key, keyMapper, 1);
}
/**
* 将列表中id为key的项向前或向后移动n个位置。n为正表示向后移动,n为负表示向前移动。
*
* @param list
* @param key
* @param keyMapper
* @param n
* @return
*/
public static <T, K> boolean move(List<T> list, K key,
Function<T, K> keyMapper, int n) {
if (n == 0)
return false;
if (n > 0)
return moveDown(list, key, keyMapper, n);
return moveUp(list, key, keyMapper, -n);
}
/**
* 如果before为true则把key移动到ref前面,否则把key移动到ref后面
*
* @param list
* @param key
* @param ref
* @param keyMapper
* @param before
* @return 被移动的元素移动后在列表中的位置,-1表示没有被移动
*/
public static <T, K> List<T> move(List<T> list, K key, K ref,
Function<T, K> keyMapper, boolean before,
Function<T, Integer> orderGet, OrderSetter<T> orderSet, int orderGap) {
if (key == ref)
return null;
// { 找到要移动的项和参考项所在位置
int keyidx = -1;
int refidx = -1;
int idx = 0;
for (T t : list) {
K id = keyMapper.apply(t);
if (id.equals(key))
keyidx = idx;
if (id.equals(ref))
refidx = idx;
if (keyidx != -1 && refidx != -1)
break;
idx++;
}
if (keyidx == -1 || refidx == -1)
return null;
// }
// { 拿走要移动的项
T t = list.remove(keyidx);
if (refidx >= keyidx)
refidx--;
if (!before)
refidx++;
// }
// 放回要移动的项
list.add(refidx, t);
// { 设置排序位置
ArrayList<T> moved = new ArrayList<T>();
int startOrder = 0;
if (refidx > 0)
startOrder = orderGet.apply(list.get(refidx - 1));
for (int i = refidx + 1; i < list.size(); i++) {
int order = orderGet.apply(list.get(i));
if (order - startOrder >= i - refidx + 1) {
int gap = (order - startOrder) / (i - refidx + 1);
for (int j = refidx; j < i; j++) {
T move = list.get(j);
orderSet.apply(move, startOrder + (j - refidx + 1) * gap);
moved.add(move);
}
return moved;
}
}
for (int i = refidx; i < list.size(); i++) {
T move = list.get(i);
orderSet.apply(move, startOrder + (i - refidx + 1) * orderGap);
moved.add(move);
}
return moved;
// }
}
/**
* 建立树形结构
*
* @param list
* @param keyMapper
* @param parentKeyMapper
* @param childMapper
* @return
*/
public static <T, K> ArrayList<T> buildTree(Collection<T> list,
Function<T, K> keyMapper, Function<T, K> parentKeyMapper,
Function<T, Collection<T>> childMapper) {
HashMap<K, T> map = toHashMap(list, keyMapper);
ArrayList<T> root = new ArrayList<T>();
for (T t : list) {
K key = keyMapper.apply(t);
K parentKey = parentKeyMapper.apply(t);
if (parentKey == null || key.equals(parentKey))
root.add(t);
else
childMapper.apply(map.get(parentKey)).add(t);
}
return root;
}
public static <T, K> ArrayList<T> copyTrees(Collection<T> trees,
Function<T, K> keyMapper, Function<T, Collection<T>> childMapper,
Function<T, T> cloneMapper, Collection<K> toCopy) {
ArrayList<T> copy = new ArrayList<T>();
if (toCopy == null)
return copy;
for (T node : trees) {
T copyNode = copyTree(node, keyMapper, childMapper, cloneMapper,
toCopy);
if (copyNode != null)
copy.add(copyNode);
}
return copy;
}
public static <T, K> T copyTree(T node, Function<T, K> keyMapper,
Function<T, Collection<T>> childMapper, Function<T, T> cloneMapper,
Collection<K> toCopy) {
if (toCopy == null)
return null;
T copy = null;
Collection<T> children = childMapper.apply(node);
if (children != null && children.size() > 0) {
for (T child : children) {
T childCopy = copyTree(child, keyMapper, childMapper,
cloneMapper, toCopy);
if (childCopy != null) {
if (copy == null)
copy = cloneMapper.apply(node);
childMapper.apply(copy).add(childCopy);
}
}
}
if (copy == null && toCopy.contains(keyMapper.apply(node)))
copy = cloneMapper.apply(node);
return copy;
}
public static int[] toArray(Collection<Integer> col) {
if (col == null)
return null;
int[] arr = new int[col.size()];
int i = 0;
for (Integer v : col)
arr[i++] = v.intValue();
return arr;
}
public static <T> Collection<T> sort(Collection<T> list,
Function<T, Number> order, boolean asc) {
if (list == null || order == null)
return list;
ArrayList<T> ar = new ArrayList<T>();
ar.addAll(list);
ar.sort((p1, p2) -> {
Number order1 = order.apply(p1);
Number order2 = order.apply(p2);
if (order1.doubleValue() > order2.doubleValue())
return asc ? 1 : -1;
else if (order1.doubleValue() < order2.doubleValue())
return asc ? -1 : 1;
return 0;
});
list.clear();
list.addAll(ar);
return list;
}
public static <T> Collection<T> sort(Collection<T> list,
Function<T, Number> order) {
return sort(list, order, true);
}
/**
* 用于为Collection中的父对象加载子对象。子对象加载应以父对象的key的集合为条件,父对象中应该有一个集合用于存放子对象。
*
* 例如父对象Parent有属性id和集合childs,子对象Child有属性parentId。假设
* loader.load()可以根据parentId加载子对象集合,对于类型为List<Parent>的parents:
*
* <pre>
* loadChild(parents, Parent::getId, Parent::getChilds, ids -> loader.load(ids),
* Child::getParentId);
* </pre>
*/
public static <T, K, C> void loadChildren(Collection<T> list,
Function<T, K> keyMapper,
Function<T, Collection<C>> childCollectionMapper,
Function<Collection<K>, Collection<C>> loader,
Function<C, K> childKeyMapper) {
if (list == null || list.size() == 0)
return;
HashMap<K, T> map = toHashMap(list, keyMapper);
loader.apply(map.keySet()).forEach(
p -> childCollectionMapper.apply(
map.get(childKeyMapper.apply(p))).add(p));
}
/**
* list为装有类型为T的对象的集合,称为集合一。用T对象中通过keyGetter获得的key值组成集合,调用
* loader,可以获得类型为S的对象集合,称为集合二。T和S的关系是N对1。把集合一中通过
* keyGetter获得的关键值与集合二中通过loadedKeyGetter获得关键值相同的T和S对象匹配,调用match。
*/
public static <T, K, S, X extends Collection<T>> X loadNToOne(X list,
Function<T, K> keyGetter,
Function<Collection<K>, Collection<S>> loader,
Function<S, K> loadedKeyGetter, BiConsumer<T, S> match) {
if (list == null || list.size() == 0)
return list;
Set<K> keys = toSet(list, keyGetter);
if (keys == null || keys.size() == 0)
return list;
Collection<S> subs = loader.apply(keys);
if (subs == null || subs.size() == 0)
return list;
HashMap<K, S> map = toHashMap(subs, loadedKeyGetter);
for (T item : list) {
K key = keyGetter.apply(item);
if (key == null)
continue;
S sub = map.get(key);
if (sub == null)
continue;
match.accept(item, sub);
}
return list;
}
/**
* list为装有类型为T的对象的集合,称为集合一。用T对象中通过keyGetter获得的key值组成集合,调用
* loader,可以获得类型为S的对象集合,称为集合二。T和S的关系是1对N。把集合一中通过
* keyGetter获得的关键值与集合二中通过loadedKeyGetter获得关键值相同的T和S对象匹配,调用match。
*/
public static <T, K, S, X extends Collection<T>> X loadOneToN(X list,
Function<T, K> keyGetter,
Function<Collection<K>, Collection<S>> loader,
Function<S, K> loadedKeyGetter, BiConsumer<T, S> match) {
if (list == null || list.size() == 0)
return list;
Set<K> keys = toSet(list, keyGetter);
if (keys == null || keys.size() == 0)
return list;
Collection<S> subs = loader.apply(keys);
if (subs == null || subs.size() == 0)
return list;
HashMap<K, T> map = toHashMap(list, keyGetter);
for (S sub : subs) {
T item = map.get(loadedKeyGetter.apply(sub));
if (item == null)
continue;
match.accept(item, sub);
}
return list;
}
public static <T> List<T> asList(T obj) {
if (obj == null)
return null;
List<T> list = new ArrayList<T>();
list.add(obj);
return list;
}
/**
* 把obj包装为集合,调用function
*/
public static <T> T callAsCollection(T obj, Consumer<Collection<T>> function) {
if (obj == null)
return null;
List<T> list = new ArrayList<T>();
list.add(obj);
function.accept(list);
return obj;
}
public static <T, K> T findOrFirst(Collection<K> list, Function<K, T> mapper,
T toFind) {
if (list == null || list.size() == 0)
return null;
if (toFind != null)
for (K k : list)
if (mapper.apply(k).equals(toFind))
return toFind;
return mapper.apply(list.iterator().next());
}
}