将列表均匀地分发到Java

时间:2016-12-22 16:34:05

标签: java arrays algorithm

我想将列表均匀分配到给定数量的子列表中。 例如,我有一个包含元素1到10的列表,我想要3个列表。这些看起来应该是:

SL1 -> {1, 2, 3, 4}
SL2 -> {5, 6, 7}
SL3 -> {8, 9, 10}

重要:每个列表包含的内容不相关,即SL1可能具有{1,5,7,10}。最重要的是有2个大小为3的列表和1个大小为4的列表。

我尝试了几件事,包括Iterables.partition,但这无济于事。

我唯一能想到的是:

public Iterable<List<Integer>> distributeEvenlyQueryListIntoLists(final LinkedList<Integer> bigList, final Integer numberOfSublists) {
    List<List<Integer>> result = new ArrayList<>();

    // Creates as many lists as needed
    for (int i = 0; i < numberOfSublists; i++) {
        result.add(new ArrayList<>());
    }

    while (bigList.iterator().hasNext()) {
        for (int i = 0; i < numberOfSublists; i++) {
            if (!bigList.iterator().hasNext()) {
                break;
            }
            result.get(i).add(bigList.poll());
        }
    }
    return result;
}

传递的bigList不一定是LinkedList,也可以是Iterable

我特别讨厌创建子列表的第一个循环。

谢谢!

2 个答案:

答案 0 :(得分:5)

只需以循环模式分发它们:

public <T> List<List<T>> partition(Iterable<T> iterable, int partitions){
    List<List<T>> result = new ArrayList<>(partitions);
    for(int i = 0; i < partitions; i++)
        result.add(new ArrayList<>());

    Iterator<T> iterator = iterable.iterator()
    for(int i = 0; iterator.hasNext(); i++)
        result.get(i % partitions).add(iterator.next());

    return result;
}

使用此代码运行的示例:

List<String> l = Stream.iterate(0, i->i + 1).limit(25).map(i->Integer.toString(i)).collect(Collectors.toList());
System.out.println(partition(l, 4).toString());

可生产

  

[[0,4,8,12,16,20,24],[1,5,9,13,17,21],[2,6,10,14,18,22],[3] ,7,11,15,19,23]]

基本思想是向结果集中的每个列表添加一个元素。这样就可以保证两个列表之间元素数量的差异不会超过1。

作为替代方案,您可以使用Iterables.partition的guavas实现,这需要稍微不同的方法。

答案 1 :(得分:2)

如果您讨厌创建子列表,这意味着您正在寻找快速解决方案。 如果您拥有原始List,并且计划不改变原始List ,请考虑List.subList()

int subSize = bigList.length() / numSubs;
int numBigSubs = 0; // # of subs that need to be one bigger
if (bigList.length() % numSubs > 0) {
     subSize++;
     numBigSubs = bigList.length() % numSubs;
}
int from = 0;
int to  = subSize;
List<List<Integer>> subList = new ArrayList<List<Integer>>(numSubs);
for (int i = 0; i < numSubs; i++) {
    List<Integer> newSub = bigList.subList(from, to);
    subList.add (newSub);
    from = to;
    to += subSize;
    if (i >= numBigSubs && numBigSubs > 0) to--; 
}

注意:我在没有测试的情况下写了这个 - 如果它失败了,我道歉,并希望有人会编辑它来工作。

同样,这方面的最大好处是它应该是快速的 - 所有的子列表只是对较大的一个的观点。缺点是,如果您更改列表,则所有投注均已关闭。