diff --git a/CHANGELOG.md b/CHANGELOG.md index c1586f8cd..962fef4f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,10 @@ * 【core 】 XmlUtil增加append重载(issue#I466Q0@Gitee) * 【poi 】 增加EscapeStrCellSetter(issue#I466ZZ@Gitee) * 【poi 】 ExcelBase增加renameSheet、cloneSheet(issue#I466ZZ@Gitee) -* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee) +* 【core 】 ListUtil增加splitAvg方法(pr#397@Gitee) ### 🐞Bug修复 +* 【core 】 修复MapUtil.sort比较器不一致返回原map的问题(issue#I46AQJ@Gitee) * 【core 】 修复JSONSupport默认循环引用导致的问题(issue#1779@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/AvgPartition.java b/hutool-core/src/main/java/cn/hutool/core/collection/AvgPartition.java new file mode 100644 index 000000000..48811f4af --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/collection/AvgPartition.java @@ -0,0 +1,57 @@ +package cn.hutool.core.collection; + +import cn.hutool.core.lang.Assert; + +import java.util.List; + +/** + * 列表分区或分段
+ * 通过传入分区个数,将指定列表分区为不同的块,每块区域的长度均匀分布(个数差不超过1)
+ *
+ *     [1,2,3,4] -》 [1,2], [3, 4]
+ *     [1,2,3,4] -》 [1,2], [3], [4]
+ *     [1,2,3,4] -》 [1], [2], [3], [4]
+ *     [1,2,3,4] -》 [1], [2], [3], [4], []
+ * 
+ * 分区是在原List的基础上进行的,返回的分区是不可变的抽象列表,原列表元素变更,分区中元素也会变更。 + * + * @param 元素类型 + * @author looly + * @since 5.7.10 + */ +public class AvgPartition extends Partition { + + final int limit; + // 平均分完后剩余的个数,平均放在前remainder个分区中 + final int remainder; + + /** + * 列表分区 + * + * @param list 被分区的列表 + * @param limit 分区个数 + */ + public AvgPartition(List list, int limit) { + super(list, list.size() / (limit <= 0 ? 1 : limit)); + Assert.isTrue(limit > 0, "Partition limit must be > 0"); + this.limit = limit; + this.remainder = list.size() % limit; + } + + @Override + public List get(int index) { + // 当limit个数超过list的size时,size为0,此时每个分区分1个元素,直到remainder个分配完,剩余分区为[] + int start = index * size + Math.min(index, remainder); + int end = start + size; + if (index + 1 <= remainder) { + // 将remainder个元素平均分布在前面,每个分区分1个 + end += 1; + } + return list.subList(start, end); + } + + @Override + public int size() { + return limit; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java index de6e45454..df516b931 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java @@ -8,7 +8,6 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.PageUtil; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -364,7 +363,7 @@ public class ListUtil { */ public static List reverseNew(List list) { List list2 = ObjectUtil.clone(list); - if(null == list2){ + if (null == list2) { // 不支持clone list2 = new ArrayList<>(list); } @@ -470,8 +469,8 @@ public class ListUtil { public static int lastIndexOf(List list, Matcher matcher) { if (null != list) { final int size = list.size(); - if(size > 0){ - for(int i = size -1; i >= 0; i--){ + if (size > 0) { + for (int i = size - 1; i >= 0; i--) { if (null == matcher || matcher.match(list.get(i))) { return i; } @@ -538,7 +537,7 @@ public class ListUtil { */ public static List> partition(List list, int size) { if (CollUtil.isEmpty(list)) { - return Collections.emptyList(); + return empty(); } return (list instanceof RandomAccess) @@ -558,8 +557,8 @@ public class ListUtil { * @param list 列表 * @param size 每个段的长度 * @return 分段列表 - * @since 5.4.5 * @see #partition(List, int) + * @since 5.4.5 */ public static List> split(List list, int size) { return partition(list, size); @@ -568,40 +567,27 @@ public class ListUtil { /** * 将集合平均分成多个list,返回这个集合的列表 *

例:

- *
-	 *     ListUtil.splitAvg(null, 3);	// [[], [], []]
+	 * 
+	 *     ListUtil.splitAvg(null, 3);	// []
 	 *     ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 2);	// [[1, 2], [3, 4]]
 	 *     ListUtil.splitAvg(Arrays.asList(1, 2, 3), 5);	// [[1], [2], [3], [], []]
 	 *     ListUtil.splitAvg(Arrays.asList(1, 2, 3), 2);	// [[1, 2], [3]]
-	 * 
+ * * - * @param 集合元素类型 - * @param list 集合 - * @param limit 要均分成几个list + * @param 集合元素类型 + * @param list 集合 + * @param limit 要均分成几个list * @return 分段列表 + * @author lileming + * @since 5.7.10 */ public static List> splitAvg(List list, int limit) { - final List> result = new ArrayList<>(); if (CollUtil.isEmpty(list)) { - for (int i = 0; i < limit; i++) { - result.add(new ArrayList<>()); - } - return result; + return empty(); } - int remainder = list.size() % limit; - int number = list.size() / limit; - int offset = 0; - for (int i = 0; i < limit; i++) { - List value; - if (remainder > 0) { - value = list.subList(i * number + offset, (i + 1) * number + offset + 1); - remainder--; - offset++; - } else { - value = list.subList(i * number + offset, (i + 1) * number + offset); - } - result.add(value); - } - return result; + + return (list instanceof RandomAccess) + ? new RandomAccessAvgPartition<>(list, limit) + : new AvgPartition<>(list, limit); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/Partition.java b/hutool-core/src/main/java/cn/hutool/core/collection/Partition.java index 6ac22812f..861e7e0a6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/collection/Partition.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/Partition.java @@ -14,6 +14,7 @@ import java.util.List; * @since 5.7.10 */ public class Partition extends AbstractList> { + final List list; final int size; diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/RandomAccessAvgPartition.java b/hutool-core/src/main/java/cn/hutool/core/collection/RandomAccessAvgPartition.java new file mode 100644 index 000000000..fce4c8b17 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/collection/RandomAccessAvgPartition.java @@ -0,0 +1,32 @@ +package cn.hutool.core.collection; + +import java.util.List; +import java.util.RandomAccess; + +/** + * 列表分区或分段(可随机访问列表)
+ * 通过传入分区个数,将指定列表分区为不同的块,每块区域的长度均匀分布(个数差不超过1)
+ *
+ *     [1,2,3,4] -》 [1,2], [3, 4]
+ *     [1,2,3,4] -》 [1,2], [3], [4]
+ *     [1,2,3,4] -》 [1], [2], [3], [4]
+ *     [1,2,3,4] -》 [1], [2], [3], [4], []
+ * 
+ * 分区是在原List的基础上进行的,返回的分区是不可变的抽象列表,原列表元素变更,分区中元素也会变更。 + * + * @param 元素类型 + * @author looly + * @since 5.7.10 + */ +public class RandomAccessAvgPartition extends AvgPartition implements RandomAccess { + + /** + * 列表分区 + * + * @param list 被分区的列表 + * @param limit 分区个数 + */ + public RandomAccessAvgPartition(List list, int limit) { + super(list, limit); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java index a49d8bba5..25455b1ad 100644 --- a/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/collection/ListUtilTest.java @@ -9,13 +9,14 @@ import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class ListUtilTest { @Test @Ignore - public void split() { + public void splitBenchTest() { List list = new ArrayList<>(); CollUtil.padRight(list, RandomUtil.randomInt(1000_0000, 1_0000_0000), "test"); @@ -37,6 +38,32 @@ public class ListUtilTest { Console.log(stopWatch.prettyPrint()); } + @Test + public void splitAvgTest(){ + List> lists = ListUtil.splitAvg(null, 3); + Assert.assertEquals(ListUtil.empty(), lists); + + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 1); + Assert.assertEquals("[[1, 2, 3, 4]]", lists.toString()); + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 2); + Assert.assertEquals("[[1, 2], [3, 4]]", lists.toString()); + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 3); + Assert.assertEquals("[[1, 2], [3], [4]]", lists.toString()); + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 4); + Assert.assertEquals("[[1], [2], [3], [4]]", lists.toString()); + + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3), 5); + Assert.assertEquals("[[1], [2], [3], [], []]", lists.toString()); + lists = ListUtil.splitAvg(Arrays.asList(1, 2, 3), 2); + Assert.assertEquals("[[1, 2], [3]]", lists.toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void splitAvgNotZero(){ + // limit不能小于等于0 + ListUtil.splitAvg(Arrays.asList(1, 2, 3, 4), 0); + } + @Test public void editTest(){ List a = ListUtil.toLinkedList("1", "2", "3");