
本文介绍一种健壮、无副作用的算法,用于将任意长度的列表按需划分为指定数量的近似等长子列表,准确计算每个子列表的起始与结束索引,避免整除误差导致的数据遗漏或越界。
本文介绍一种健壮、无副作用的算法,用于将任意长度的列表按需划分为指定数量的近似等长子列表,准确计算每个子列表的起始与结束索引,避免整除误差导致的数据遗漏或越界。
在分布式处理、分页计算或并行任务调度中,常需将一个大型列表(如用户数据、日志条目)切分为若干“块”(chunks),由不同线程或服务实例分别处理。关键挑战在于:当列表长度 N 无法被目标块数 k 整除时,如何保证所有元素被覆盖、无重复、无越界,且各块长度差异不超过 1?错误的索引计算(如简单使用整数除法截断)极易导致首尾块偏小、中间块错位,甚至 IndexOutOfBoundsException。
✅ 正确解法:基于「向上取整块大小」+「动态边界计算」
核心思想是:
- 每块基础大小为
baseSize = N / k(整除向下取整); - 前
remainder = N % k个块额外多分配 1 个元素(即长度为baseSize + 1); - 后
k - remainder个块保持baseSize长度; - 这样总长度恒为
remainder × (baseSize + 1) + (k − remainder) × baseSize = N,完美覆盖。
? Java 实现(推荐,无副作用,支持任意 List<t></t>)
public static <T> List<T> getChunk(List<T> list, int chunkIndex, int numOfChunks) {
if (list == null || numOfChunks <= 0 || chunkIndex < 1 || chunkIndex > numOfChunks) {
throw new IllegalArgumentException("Invalid parameters: list=" + list + ", chunkIndex=" + chunkIndex + ", numOfChunks=" + numOfChunks);
}
int size = list.size();
if (size == 0) return List.of(); // 空列表返回空子列表
int baseSize = size / numOfChunks; // 每块基础长度(向下取整)
int remainder = size % numOfChunks; // 需要“加一”的块数(前 remainder 块)
// 计算当前块的起始索引
int startIndex;
int endIndex;
if (chunkIndex <= remainder) {
// 前 remainder 块:每块长度为 baseSize + 1
startIndex = (chunkIndex - 1) * (baseSize + 1);
endIndex = startIndex + baseSize + 1;
} else {
// 后续块:每块长度为 baseSize
startIndex = remainder * (baseSize + 1) + (chunkIndex - remainder - 1) * baseSize;
endIndex = startIndex + baseSize;
}
return list.subList(startIndex, endIndex);
}? 使用示例
List<Map<String, String>> names = List.of(
Map.of("firstName", "Todd", "lastName", "Jones"),
Map.of("firstName", "Mike", "lastName", "Johnson"),
Map.of("firstName", "Tina", "lastName", "Jones"),
Map.of("firstName", "Tommy", "lastName", "Wiseau"),
Map.of("firstName", "Anna", "lastName", "Lee")
);
// 将 5 个元素分为 2 块 → 第1块含3个,第2块含2个
System.out.println(getChunk(names, 1, 2)); // indices [0, 3)
System.out.println(getChunk(names, 2, 2)); // indices [3, 5)⚠️ 注意事项与最佳实践
-
索引从 1 开始:
chunkIndex语义为“第几块”,符合自然语言习惯(非数组下标),避免混淆; -
边界安全:
subList(start, end)是左闭右开区间,无需手动+1或-1,天然防越界; -
不可变视图:
subList()返回的是原列表的视图,修改会影响原列表;如需独立副本,请用new ArrayList(subList(...)); - 性能友好:全程仅计算索引,不复制数据、不修改原列表,时间复杂度 O(1),空间 O(1);
-
拒绝魔数:避免硬编码
chunkIndex == 1或== numOfChunks的分支逻辑——它在numOfChunks=1或N 时极易出错;上述实现对所有合法输入鲁棒。
✅ 总结
均分子列表的本质是「余数再分配」问题。与其凭直觉拼凑边界公式,不如采用标准数学模型:先算 baseSize 和 remainder,再按块序号线性定位。该方案简洁、可验证、易测试,且已广泛应用于 Apache Commons Collections(ListUtils.partition)、Guava(Lists.partition)等主流库底层逻辑中。掌握此模式,即可从容应对任何规模的分块调度需求。

















