如何用 N 个分区而不是大小为 N 的分区对列表进行分区?

How to partition a list with N partitions rather than partitions of size N?

Guava 的方法,Lists#partition,将 List<?> 划分为 List<List<?>>,其中每个分区包含 N 个元素(由函数的第二个参数指定,并且不包括最后一个分区)。

是否可以使用此方法但改为创建 N 个分区?

如果没有,有什么方法可以解决?

我试图用以下内容创建 31 个分区(keysList<String> 大小 57),但它只创建 29:

List<String> keys = ...;

var paritions = Lists.partition(keys, (int) Math.ceil(keys.size() / 31D));

要创建 N 个分区,您必须至少有 2N 个元素。在您的情况下,您的分区要求为 31,这意味着您需要 62 个元素。

因为你有 57 个元素,所以你有五个元素 - 或者两个半分区 - 低于所需的最小值,这就是为什么你得到 29 个分区,最后一个分区只有一个元素。

番石榴正在做它的工作。您没有足够的元素来正确细分成您想要的分区。

问题在于您的自定义分区分配 "empty space"(即由于缺少完全填充分区的元素而留下的间隙)与 Guava 的方法不同。该方法将在创建下一个分区之前完全填充每个分区,而您希望均匀分布元素。这是因为 partition() 定义了组的 size,而你想指定组的 number

看看这个自定义实现:

private static <T> List<List<T>> distribute(List<T> elements, int nrOfGroups)
{
    int elementsPerGroup = elements.size() / nrOfGroups;
    int leftoverElements = elements.size() % nrOfGroups;

    List<List<T>> groups = new ArrayList<>();
    for (int i = 0; i < nrOfGroups; i++)
    {
        groups.add(elements.subList(i * elementsPerGroup + Math.min(i, leftoverElements),
                                    (i + 1) * elementsPerGroup + Math.min(i + 1, leftoverElements)));
    }
    return groups;
}

它将计算组的最小大小(floor of count/#groups),然后在有剩余元素的情况下更正前几个组的大小。

例子

List<Integer> elements = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
int nrOfGroups = 6;
// [[1, 2], [3, 4], [5], [6], [7], [8]]