diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java index 57e2b7ca3..522d56e03 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollUtil.java @@ -6,8 +6,8 @@ import cn.hutool.core.collection.iter.IteratorEnumeration; import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.comparator.PinyinComparator; import cn.hutool.core.comparator.PropertyComparator; -import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.CompositeConverter; +import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.hash.Hash32; import cn.hutool.core.map.MapUtil; @@ -297,63 +297,19 @@ public class CollUtil { return new ArrayList<>(set); } - /** - * 两个集合的并集
- * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最多的个数
- * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
- * 结果:[a, b, c, c, c],此结果中只保留了三个c - * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @return 并集的集合,返回 {@link ArrayList} - */ - public static Collection union(final Collection coll1, final Collection coll2) { - if (isEmpty(coll1)) { - return ListUtil.of(coll2); - } else if (isEmpty(coll2)) { - return ListUtil.of(coll1); - } - - // 给每个元素计数 - final Map map1 = countMap(coll1); - final Map map2 = countMap(coll2); - // 两个集合的全部元素 - final Set elements = SetUtil.of(map1.keySet()); - elements.addAll(map2.keySet()); - // 并集, 每个元素至少会有一个 - final List 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; - } - /** * 多个集合的并集
* 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最多的个数
* 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c, c, c],此结果中只保留了三个c * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @param otherColls 其它集合 + * @param 集合元素类型 + * @param colls 集合数组 * @return 并集的集合,返回 {@link ArrayList} */ @SafeVarargs - public static Collection union(final Collection coll1, final Collection coll2, final Collection... otherColls) { - Collection union = union(coll1, coll2); - for (final Collection coll : otherColls) { - if (isNotEmpty(coll)) { - union = union(union, coll); - } - } - return union; + public static Collection union(final Collection... colls) { + return CollectionOperation.of(colls).union(); } /** @@ -362,34 +318,13 @@ public class CollUtil { * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c],此结果中只保留了一个c * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @param otherColls 其它集合 + * @param 集合元素类型 + * @param colls 列表集合 * @return 并集的集合,返回 {@link LinkedHashSet} */ @SafeVarargs - public static Set unionDistinct(final Collection coll1, final Collection coll2, final Collection... otherColls) { - final Set result; - if (isEmpty(coll1)) { - result = new LinkedHashSet<>(); - } else { - result = new LinkedHashSet<>(coll1); - } - - if (isNotEmpty(coll2)) { - result.addAll(coll2); - } - - if (ArrayUtil.isNotEmpty(otherColls)) { - for (final Collection otherColl : otherColls) { - if (isNotEmpty(otherColl)) { - result.addAll(otherColl); - } - } - } - - return result; + public static Set unionDistinct(final Collection... colls) { + return CollectionOperation.of(colls).unionDistinct(); } /** @@ -398,73 +333,13 @@ public class CollUtil { * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c, c, c, a, b, c, c] * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @param otherColls 其它集合 + * @param 集合元素类型 + * @param colls 集合数组 * @return 并集的集合,返回 {@link ArrayList} */ @SafeVarargs - public static List unionAll(final Collection coll1, final Collection coll2, final Collection... otherColls) { - // 先统计所有集合的元素数量, 避免扩容 - int totalSize = size(coll1) + size(coll2); - for (Collection coll : otherColls) { - totalSize += size(coll); - } - if (totalSize == 0) { - return ListUtil.zero(); - } - final List result = new ArrayList<>(totalSize); - - if (isNotEmpty(coll1)) { - result.addAll(coll1); - } - if (isNotEmpty(coll2)) { - result.addAll(coll2); - } - - if (ArrayUtil.isNotEmpty(otherColls)) { - for (final Collection otherColl : otherColls) { - if (isNotEmpty(otherColl)) { - result.addAll(otherColl); - } - } - } - - return result; - } - - /** - * 两个集合的交集
- * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最少的个数
- * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
- * 结果:[a, b, c, c],此结果中只保留了两个c - * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @return 交集的集合,返回 {@link ArrayList} - */ - public static Collection intersection(final Collection coll1, final Collection coll2) { - if (isNotEmpty(coll1) && isNotEmpty(coll2)) { - final Map map1 = countMap(coll1); - final Map map2 = countMap(coll2); - - boolean isFirstSmaller = map1.keySet().size() <= map2.keySet().size(); - // 只需要遍历数量较少的集合的元素 - final Set elements = SetUtil.of(isFirstSmaller ? map1.keySet() : map2.keySet()); - // 交集的元素个数 最多为 较少集合的元素个数 - final List 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(); + public static List unionAll(final Collection... colls) { + return CollectionOperation.of(colls).unionAll(); } /** @@ -473,26 +348,13 @@ public class CollUtil { * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
* 结果:[a, b, c, c],此结果中只保留了两个c * - * @param 集合元素类型 - * @param coll1 集合1 - * @param coll2 集合2 - * @param otherColls 其它集合 + * @param 集合元素类型 + * @param colls 集合列表 * @return 交集的集合,返回 {@link ArrayList} */ @SafeVarargs - public static Collection intersection(final Collection coll1, final Collection coll2, final Collection... otherColls) { - // 任意容器为空, 则返回空集 - if (isEmpty(coll1) || isEmpty(coll2) || ArrayUtil.hasEmpty((Object[]) otherColls)) { - return ListUtil.zero(); - } - Collection intersection = intersection(coll1, coll2); - if (ArrayUtil.isEmpty(otherColls)) { - return intersection; - } - for (final Collection coll : otherColls) { - intersection = intersection(intersection, coll); - } - return intersection; + public static Collection intersection(final Collection... colls) { + return CollectionOperation.of(colls).intersection(); } /** @@ -563,7 +425,7 @@ public class CollUtil { elements.addAll(map2.keySet()); // 元素的个数为 该元素在两个集合中的个数的差 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++) { result.add(t); } @@ -691,7 +553,7 @@ public class CollUtil { if (isEmpty(coll1) || isEmpty(coll2)) { return false; } - boolean isFirstSmaller = coll1.size() <= coll2.size(); + final boolean isFirstSmaller = coll1.size() <= coll2.size(); // 用元素较少的集合来遍历 final Collection smallerColl = isFirstSmaller ? coll1 : coll2; // 用元素较多的集合构造Set, 用于快速判断是否有相同元素 @@ -2333,7 +2195,7 @@ public class CollUtil { } // 统计每个map的values的大小总和 int size = 0; - for (Map map : mapCollection) { + for (final Map map : mapCollection) { size += size(map.values()); } if (size == 0) { @@ -2611,7 +2473,7 @@ public class CollUtil { //noinspection unchecked final Map countMap1 = countMap((Iterable) coll1); - for (Object e : coll2) { + for (final Object e : coll2) { // 该元素若存在, 则个数减去1, 若不存在则个数为-1 final Integer amount = countMap1.merge(e, -1, Integer::sum); if (amount < 0) { diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollectionOperation.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollectionOperation.java new file mode 100755 index 000000000..581823f1e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollectionOperation.java @@ -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 集合元素类型 + */ +public class CollectionOperation { + + @SafeVarargs + public static CollectionOperation of(final Collection... colls) { + return new CollectionOperation<>(colls); + } + + private final Collection[] colls; + + /** + * 构造 + * + * @param colls 集合数组 + */ + public CollectionOperation(final Collection[] colls) { + this.colls = colls; + } + + /** + * 多个集合的并集
+ * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最多的个数
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c, c, c],此结果中只保留了三个c + * + * @return 并集的集合,返回 {@link ArrayList} + */ + public Collection union() { + final Collection[] colls = this.colls; + if (ArrayUtil.isEmpty(colls)) { + return ListUtil.zero(); + } + + Collection result = colls[0]; + for (int i = 1; i < colls.length; i++) { + result = _union(result, colls[i]); + } + + return result; + } + + /** + * 多个集合的非重复并集,类似于SQL中的“UNION DISTINCT”
+ * 针对一个集合中存在多个相同元素的情况,只保留一个
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c],此结果中只保留了一个c + * + * @return 并集的集合,返回 {@link LinkedHashSet} + */ + public Set unionDistinct() { + final Collection[] colls = this.colls; + int totalLength = 0; + for (final Collection set : colls) { + if (CollUtil.isNotEmpty(set)) { + totalLength += set.size(); + } + } + final Set result = new HashSet<>(totalLength, 1); + for (final Collection set : colls) { + if (CollUtil.isNotEmpty(set)) { + result.addAll(set); + } + } + return result; + } + + /** + * 多个集合的完全并集,类似于SQL中的“UNION ALL”
+ * 针对一个集合中存在多个相同元素的情况,保留全部元素
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c, c, c, a, b, c, c] + * + * @return 并集的集合,返回 {@link ArrayList} + */ + public List unionAll() { + final Collection[] colls = this.colls; + if (ArrayUtil.isEmpty(colls)) { + return ListUtil.zero(); + } + // 先统计所有集合的元素数量, 避免扩容 + int totalSize = 0; + for (final Collection coll : colls) { + if (CollUtil.isNotEmpty(coll)) { + totalSize += CollUtil.size(coll); + } + } + if (totalSize == 0) { + return ListUtil.zero(); + } + + // 遍历并全部加入集合 + final List result = new ArrayList<>(totalSize); + for (final Collection coll : colls) { + if (CollUtil.isNotEmpty(coll)) { + result.addAll(coll); + } + } + + return result; + } + + /** + * 多个集合的交集
+ * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最少的个数
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c, c],此结果中只保留了两个c + * + * @return 交集的集合,返回 {@link ArrayList} + */ + public Collection intersection() { + final Collection[] colls = this.colls; + if (ArrayUtil.isEmpty(colls)) { + return ListUtil.zero(); + } + + Collection result = colls[0]; + for (int i = 1; i < colls.length; i++) { + result = _intersection(result, colls[i]); + } + + return result; + } + + /** + * 两个集合的并集
+ * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最多的个数
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c, c, c],此结果中只保留了三个c
+ * 结果集合与原集合顺序无关 + * + * @param 集合元素类型 + * @param coll1 集合1 + * @param coll2 集合2 + * @return 并集的集合,返回 {@link ArrayList} + */ + private static Collection _union(final Collection coll1, final Collection coll2) { + if (CollUtil.isEmpty(coll1)) { + return ListUtil.of(coll2); + } else if (CollUtil.isEmpty(coll2)) { + return ListUtil.of(coll1); + } + + // 给每个元素计数 + final Map map1 = CollUtil.countMap(coll1); + final Map map2 = CollUtil.countMap(coll2); + + // 两个集合的全部元素 + final Set elements = CollectionOperation.of(map1.keySet(), map2.keySet()).unionDistinct(); + + // 并集, 每个元素至少会有一个 + final List 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; + } + + /** + * 两个集合的交集
+ * 针对一个集合中存在多个相同元素的情况,计算两个集合中此元素的个数,保留最少的个数
+ * 例如:集合1:[a, b, c, c, c],集合2:[a, b, c, c]
+ * 结果:[a, b, c, c],此结果中只保留了两个c + * + * @param 集合元素类型 + * @param coll1 集合1 + * @param coll2 集合2 + * @return 交集的集合,返回 {@link ArrayList} + */ + private static Collection _intersection(final Collection coll1, final Collection coll2) { + if (CollUtil.isEmpty(coll1) || CollUtil.isEmpty(coll2)) { + return ListUtil.zero(); + } + final Map map1 = CollUtil.countMap(coll1); + final Map map2 = CollUtil.countMap(coll2); + + final boolean isFirstSmaller = map1.size() <= map2.size(); + // 只需要遍历数量较少的集合的元素 + final Set elements = SetUtil.of(isFirstSmaller ? map1.keySet() : map2.keySet()); + // 交集的元素个数 最多为 较少集合的元素个数 + final List 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; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/SetUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/SetUtil.java index 2b62725f6..8a99dc73f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/SetUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/SetUtil.java @@ -192,6 +192,7 @@ public class SetUtil { * @param 元素类型 * @param c 集合 * @return 只读集合 + * @see Collections#unmodifiableSet(Set) * @since 6.0.0 */ public static Set unmodifiable(final Set c) {