add CollectionOperation

This commit is contained in:
Looly 2022-08-19 12:28:27 +08:00
parent 136cc3b08a
commit 0842ff0f50
3 changed files with 231 additions and 159 deletions

View File

@ -6,8 +6,8 @@ import cn.hutool.core.collection.iter.IteratorEnumeration;
import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.comparator.PinyinComparator; import cn.hutool.core.comparator.PinyinComparator;
import cn.hutool.core.comparator.PropertyComparator; import cn.hutool.core.comparator.PropertyComparator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.CompositeConverter; import cn.hutool.core.convert.CompositeConverter;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.hash.Hash32; import cn.hutool.core.lang.hash.Hash32;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
@ -297,42 +297,6 @@ public class CollUtil {
return new ArrayList<>(set); return new ArrayList<>(set);
} }
/**
* 两个集合的并集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最多的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c, c]此结果中只保留了三个c
*
* @param <T> 集合元素类型
* @param coll1 集合1
* @param coll2 集合2
* @return 并集的集合返回 {@link ArrayList}
*/
public static <T> Collection<T> union(final Collection<T> coll1, final Collection<T> coll2) {
if (isEmpty(coll1)) {
return ListUtil.of(coll2);
} else if (isEmpty(coll2)) {
return ListUtil.of(coll1);
}
// 给每个元素计数
final Map<T, Integer> map1 = countMap(coll1);
final Map<T, Integer> map2 = countMap(coll2);
// 两个集合的全部元素
final Set<T> elements = SetUtil.of(map1.keySet());
elements.addAll(map2.keySet());
// 并集, 每个元素至少会有一个
final List<T> list = new ArrayList<>(elements.size());
for (final T t : elements) {
// 每个元素 保留最多的个数
int amount = Math.max(map1.getOrDefault(t, 0), map2.getOrDefault(t, 0));
for (int i = 0; i < amount; i++) {
list.add(t);
}
}
return list;
}
/** /**
* 多个集合的并集<br> * 多个集合的并集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最多的个数<br> * 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最多的个数<br>
@ -340,20 +304,12 @@ public class CollUtil {
* 结果[a, b, c, c, c]此结果中只保留了三个c * 结果[a, b, c, c, c]此结果中只保留了三个c
* *
* @param <T> 集合元素类型 * @param <T> 集合元素类型
* @param coll1 集合1 * @param colls 集合数组
* @param coll2 集合2
* @param otherColls 其它集合
* @return 并集的集合返回 {@link ArrayList} * @return 并集的集合返回 {@link ArrayList}
*/ */
@SafeVarargs @SafeVarargs
public static <T> Collection<T> union(final Collection<T> coll1, final Collection<T> coll2, final Collection<T>... otherColls) { public static <T> Collection<T> union(final Collection<T>... colls) {
Collection<T> union = union(coll1, coll2); return CollectionOperation.of(colls).union();
for (final Collection<T> coll : otherColls) {
if (isNotEmpty(coll)) {
union = union(union, coll);
}
}
return union;
} }
/** /**
@ -363,33 +319,12 @@ public class CollUtil {
* 结果[a, b, c]此结果中只保留了一个c * 结果[a, b, c]此结果中只保留了一个c
* *
* @param <T> 集合元素类型 * @param <T> 集合元素类型
* @param coll1 集合1 * @param colls 列表集合
* @param coll2 集合2
* @param otherColls 其它集合
* @return 并集的集合返回 {@link LinkedHashSet} * @return 并集的集合返回 {@link LinkedHashSet}
*/ */
@SafeVarargs @SafeVarargs
public static <T> Set<T> unionDistinct(final Collection<T> coll1, final Collection<T> coll2, final Collection<T>... otherColls) { public static <T> Set<T> unionDistinct(final Collection<T>... colls) {
final Set<T> result; return CollectionOperation.of(colls).unionDistinct();
if (isEmpty(coll1)) {
result = new LinkedHashSet<>();
} else {
result = new LinkedHashSet<>(coll1);
}
if (isNotEmpty(coll2)) {
result.addAll(coll2);
}
if (ArrayUtil.isNotEmpty(otherColls)) {
for (final Collection<T> otherColl : otherColls) {
if (isNotEmpty(otherColl)) {
result.addAll(otherColl);
}
}
}
return result;
} }
/** /**
@ -399,72 +334,12 @@ public class CollUtil {
* 结果[a, b, c, c, c, a, b, c, c] * 结果[a, b, c, c, c, a, b, c, c]
* *
* @param <T> 集合元素类型 * @param <T> 集合元素类型
* @param coll1 集合1 * @param colls 集合数组
* @param coll2 集合2
* @param otherColls 其它集合
* @return 并集的集合返回 {@link ArrayList} * @return 并集的集合返回 {@link ArrayList}
*/ */
@SafeVarargs @SafeVarargs
public static <T> List<T> unionAll(final Collection<T> coll1, final Collection<T> coll2, final Collection<T>... otherColls) { public static <T> List<T> unionAll(final Collection<T>... colls) {
// 先统计所有集合的元素数量, 避免扩容 return CollectionOperation.of(colls).unionAll();
int totalSize = size(coll1) + size(coll2);
for (Collection<T> coll : otherColls) {
totalSize += size(coll);
}
if (totalSize == 0) {
return ListUtil.zero();
}
final List<T> result = new ArrayList<>(totalSize);
if (isNotEmpty(coll1)) {
result.addAll(coll1);
}
if (isNotEmpty(coll2)) {
result.addAll(coll2);
}
if (ArrayUtil.isNotEmpty(otherColls)) {
for (final Collection<T> otherColl : otherColls) {
if (isNotEmpty(otherColl)) {
result.addAll(otherColl);
}
}
}
return result;
}
/**
* 两个集合的交集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最少的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c]此结果中只保留了两个c
*
* @param <T> 集合元素类型
* @param coll1 集合1
* @param coll2 集合2
* @return 交集的集合返回 {@link ArrayList}
*/
public static <T> Collection<T> intersection(final Collection<T> coll1, final Collection<T> coll2) {
if (isNotEmpty(coll1) && isNotEmpty(coll2)) {
final Map<T, Integer> map1 = countMap(coll1);
final Map<T, Integer> map2 = countMap(coll2);
boolean isFirstSmaller = map1.keySet().size() <= map2.keySet().size();
// 只需要遍历数量较少的集合的元素
final Set<T> elements = SetUtil.of(isFirstSmaller ? map1.keySet() : map2.keySet());
// 交集的元素个数 最多为 较少集合的元素个数
final List<T> list = new ArrayList<>(isFirstSmaller ? coll1.size() : coll2.size());
for (final T t : elements) {
int amount = Math.min(map1.getOrDefault(t, 0), map2.getOrDefault(t, 0));
for (int i = 0; i < amount; i++) {
list.add(t);
}
}
return list;
}
return ListUtil.zero();
} }
/** /**
@ -474,25 +349,12 @@ public class CollUtil {
* 结果[a, b, c, c]此结果中只保留了两个c * 结果[a, b, c, c]此结果中只保留了两个c
* *
* @param <T> 集合元素类型 * @param <T> 集合元素类型
* @param coll1 集合1 * @param colls 集合列表
* @param coll2 集合2
* @param otherColls 其它集合
* @return 交集的集合返回 {@link ArrayList} * @return 交集的集合返回 {@link ArrayList}
*/ */
@SafeVarargs @SafeVarargs
public static <T> Collection<T> intersection(final Collection<T> coll1, final Collection<T> coll2, final Collection<T>... otherColls) { public static <T> Collection<T> intersection(final Collection<T>... colls) {
// 任意容器为空, 则返回空集 return CollectionOperation.of(colls).intersection();
if (isEmpty(coll1) || isEmpty(coll2) || ArrayUtil.hasEmpty((Object[]) otherColls)) {
return ListUtil.zero();
}
Collection<T> intersection = intersection(coll1, coll2);
if (ArrayUtil.isEmpty(otherColls)) {
return intersection;
}
for (final Collection<T> coll : otherColls) {
intersection = intersection(intersection, coll);
}
return intersection;
} }
/** /**
@ -563,7 +425,7 @@ public class CollUtil {
elements.addAll(map2.keySet()); elements.addAll(map2.keySet());
// 元素的个数为 该元素在两个集合中的个数的差 // 元素的个数为 该元素在两个集合中的个数的差
for (final T t : elements) { for (final T t : elements) {
int amount = Math.abs(map1.getOrDefault(t, 0) - map2.getOrDefault(t, 0)); final int amount = Math.abs(map1.getOrDefault(t, 0) - map2.getOrDefault(t, 0));
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
result.add(t); result.add(t);
} }
@ -691,7 +553,7 @@ public class CollUtil {
if (isEmpty(coll1) || isEmpty(coll2)) { if (isEmpty(coll1) || isEmpty(coll2)) {
return false; return false;
} }
boolean isFirstSmaller = coll1.size() <= coll2.size(); final boolean isFirstSmaller = coll1.size() <= coll2.size();
// 用元素较少的集合来遍历 // 用元素较少的集合来遍历
final Collection<?> smallerColl = isFirstSmaller ? coll1 : coll2; final Collection<?> smallerColl = isFirstSmaller ? coll1 : coll2;
// 用元素较多的集合构造Set, 用于快速判断是否有相同元素 // 用元素较多的集合构造Set, 用于快速判断是否有相同元素
@ -2333,7 +2195,7 @@ public class CollUtil {
} }
// 统计每个map的values的大小总和 // 统计每个map的values的大小总和
int size = 0; int size = 0;
for (Map<?, V> map : mapCollection) { for (final Map<?, V> map : mapCollection) {
size += size(map.values()); size += size(map.values());
} }
if (size == 0) { if (size == 0) {
@ -2611,7 +2473,7 @@ public class CollUtil {
//noinspection unchecked //noinspection unchecked
final Map<Object, Integer> countMap1 = countMap((Iterable<Object>) coll1); final Map<Object, Integer> countMap1 = countMap((Iterable<Object>) coll1);
for (Object e : coll2) { for (final Object e : coll2) {
// 该元素若存在, 则个数减去1, 若不存在则个数为-1 // 该元素若存在, 则个数减去1, 若不存在则个数为-1
final Integer amount = countMap1.merge(e, -1, Integer::sum); final Integer amount = countMap1.merge(e, -1, Integer::sum);
if (amount < 0) { if (amount < 0) {

View File

@ -0,0 +1,209 @@
package cn.hutool.core.collection;
import cn.hutool.core.util.ArrayUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 集合运算
*
* @param <E> 集合元素类型
*/
public class CollectionOperation<E> {
@SafeVarargs
public static <E> CollectionOperation<E> of(final Collection<E>... colls) {
return new CollectionOperation<>(colls);
}
private final Collection<E>[] colls;
/**
* 构造
*
* @param colls 集合数组
*/
public CollectionOperation(final Collection<E>[] colls) {
this.colls = colls;
}
/**
* 多个集合的并集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最多的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c, c]此结果中只保留了三个c
*
* @return 并集的集合返回 {@link ArrayList}
*/
public Collection<E> union() {
final Collection<E>[] colls = this.colls;
if (ArrayUtil.isEmpty(colls)) {
return ListUtil.zero();
}
Collection<E> result = colls[0];
for (int i = 1; i < colls.length; i++) {
result = _union(result, colls[i]);
}
return result;
}
/**
* 多个集合的非重复并集类似于SQL中的UNION DISTINCT<br>
* 针对一个集合中存在多个相同元素的情况只保留一个<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c]此结果中只保留了一个c
*
* @return 并集的集合返回 {@link LinkedHashSet}
*/
public Set<E> unionDistinct() {
final Collection<E>[] colls = this.colls;
int totalLength = 0;
for (final Collection<E> set : colls) {
if (CollUtil.isNotEmpty(set)) {
totalLength += set.size();
}
}
final Set<E> result = new HashSet<>(totalLength, 1);
for (final Collection<E> set : colls) {
if (CollUtil.isNotEmpty(set)) {
result.addAll(set);
}
}
return result;
}
/**
* 多个集合的完全并集类似于SQL中的UNION ALL<br>
* 针对一个集合中存在多个相同元素的情况保留全部元素<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c, c, a, b, c, c]
*
* @return 并集的集合返回 {@link ArrayList}
*/
public List<E> unionAll() {
final Collection<E>[] colls = this.colls;
if (ArrayUtil.isEmpty(colls)) {
return ListUtil.zero();
}
// 先统计所有集合的元素数量, 避免扩容
int totalSize = 0;
for (final Collection<E> coll : colls) {
if (CollUtil.isNotEmpty(coll)) {
totalSize += CollUtil.size(coll);
}
}
if (totalSize == 0) {
return ListUtil.zero();
}
// 遍历并全部加入集合
final List<E> result = new ArrayList<>(totalSize);
for (final Collection<E> coll : colls) {
if (CollUtil.isNotEmpty(coll)) {
result.addAll(coll);
}
}
return result;
}
/**
* 多个集合的交集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最少的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c]此结果中只保留了两个c
*
* @return 交集的集合返回 {@link ArrayList}
*/
public Collection<E> intersection() {
final Collection<E>[] colls = this.colls;
if (ArrayUtil.isEmpty(colls)) {
return ListUtil.zero();
}
Collection<E> result = colls[0];
for (int i = 1; i < colls.length; i++) {
result = _intersection(result, colls[i]);
}
return result;
}
/**
* 两个集合的并集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最多的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c, c]此结果中只保留了三个c<br>
* 结果集合与原集合顺序无关
*
* @param <T> 集合元素类型
* @param coll1 集合1
* @param coll2 集合2
* @return 并集的集合返回 {@link ArrayList}
*/
private static <T> Collection<T> _union(final Collection<T> coll1, final Collection<T> coll2) {
if (CollUtil.isEmpty(coll1)) {
return ListUtil.of(coll2);
} else if (CollUtil.isEmpty(coll2)) {
return ListUtil.of(coll1);
}
// 给每个元素计数
final Map<T, Integer> map1 = CollUtil.countMap(coll1);
final Map<T, Integer> map2 = CollUtil.countMap(coll2);
// 两个集合的全部元素
final Set<T> elements = CollectionOperation.of(map1.keySet(), map2.keySet()).unionDistinct();
// 并集, 每个元素至少会有一个
final List<T> list = new ArrayList<>(coll1.size() + coll2.size());
for (final T t : elements) {
// 每个元素 保留最多的个数
final int amount = Math.max(map1.getOrDefault(t, 0), map2.getOrDefault(t, 0));
for (int i = 0; i < amount; i++) {
list.add(t);
}
}
return list;
}
/**
* 两个集合的交集<br>
* 针对一个集合中存在多个相同元素的情况计算两个集合中此元素的个数保留最少的个数<br>
* 例如集合1[a, b, c, c, c]集合2[a, b, c, c]<br>
* 结果[a, b, c, c]此结果中只保留了两个c
*
* @param <E> 集合元素类型
* @param coll1 集合1
* @param coll2 集合2
* @return 交集的集合返回 {@link ArrayList}
*/
private static <E> Collection<E> _intersection(final Collection<E> coll1, final Collection<E> coll2) {
if (CollUtil.isEmpty(coll1) || CollUtil.isEmpty(coll2)) {
return ListUtil.zero();
}
final Map<E, Integer> map1 = CollUtil.countMap(coll1);
final Map<E, Integer> map2 = CollUtil.countMap(coll2);
final boolean isFirstSmaller = map1.size() <= map2.size();
// 只需要遍历数量较少的集合的元素
final Set<E> elements = SetUtil.of(isFirstSmaller ? map1.keySet() : map2.keySet());
// 交集的元素个数 最多为 较少集合的元素个数
final List<E> list = new ArrayList<>(isFirstSmaller ? coll1.size() : coll2.size());
for (final E t : elements) {
final int amount = Math.min(map1.getOrDefault(t, 0), map2.getOrDefault(t, 0));
for (int i = 0; i < amount; i++) {
list.add(t);
}
}
return list;
}
}

View File

@ -192,6 +192,7 @@ public class SetUtil {
* @param <T> 元素类型 * @param <T> 元素类型
* @param c 集合 * @param c 集合
* @return 只读集合 * @return 只读集合
* @see Collections#unmodifiableSet(Set)
* @since 6.0.0 * @since 6.0.0
*/ */
public static <T> Set<T> unmodifiable(final Set<? extends T> c) { public static <T> Set<T> unmodifiable(final Set<? extends T> c) {