繁体   English   中英

使用 java 8 个流将列表转换为列表列表

[英]Convert a list to list of list using java 8 streams

我有一个列表和一个批量大小。 我需要将其拆分为列表列表,其中每个列表的最大批量大小最好使用 java8 Stream API。

一个重要条件是,如果我有一个空列表[] ,我需要得到[[]]

我正在这样做

final AtomicInteger counter = new AtomicInteger();
List<List<Integer>> result = listToSplit.stream()
    .collect(Collectors.groupingBy(it -> counter.getAndIncrement() / batchSize))
    .values();

但是对于空列表,它给出[[null]]

先说几点:

  1. 我很熟悉使用AtomicIntegerint[0]的 hack,因为几年前我开始使用 Streams 时已经问过 2 个问题:

  2. 随着我对 Stream API 的核心原理越来越熟悉,我意识到它们的处理方式应该与典型的 for-loops 有所不同,尽管您仍然使用可能接近程序for-loop构造的IntStream 不要违反使用 Stream API 的副作用原则。

  3. 在我们找到解决方案之前要提醒的另一件事是Collectors.groupingBy是一个收集器,导致MapMap.values()的后续调用返回Collection<List<Integer>>而不是List<List<Integer>> .

  4. 最后一点提醒一下,空输入列表的解决方案与可以完美分块的列表(15 个元素的列表按 5 个元素拆分为列表)的行为不兼容。 因此,如果一个空数组的结果是[[]] ,那么列表1,2,3,4,5,6的结果是[[1,2,3],[4,5,6],[]]使用batchSize=3是不可接受的,但可能使用简单的非分支方法生成。

关于上面所说的,您可能会得出这个解决方案的结论,但这并不优雅:

int max = (int) Math.ceil((double) listToSplit.size() / batchSize);   // number of chunks

List<List<Integer>> result;

if (listToSplit.isEmpty()) {
    result = Arrays.asList(Collections.emptyList());          // results in [[]] if empty
} else {
    result = IntStream.range(0, max)
        .map(i -> i * batchSize)                              // initial indices
        .mapToObj(i -> listToSplit.subList(                   // find a sublist
            i, Math.min(i + batchSize, listToSplit.size())))  // .. from i*b to (i+1)*b 
        .collect(Collectors.toList());                        // as List<List<Integer>>
}   

结论是在这种用例中坚持使用旧的但黄金for-loop :)

使用IntStreamlist.subList

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ListOfList {

    public static void main(String[] args) {
        List<Integer> listToSplit = new ArrayList<>();
//        listToSplit.add(1);
//        listToSplit.add(1);
//        listToSplit.add(1);
//        listToSplit.add(1);
        double batchSize = 3;

        int loops = Math.max(1,(int) Math.ceil(listToSplit.size()/batchSize));
        final List<List<Integer>> collect = IntStream.iterate(-1, i -> (int) (i + batchSize)).limit(loops)
                .mapToObj(id -> {
                    int actualCounter  = id+1;
                    int finalLength = (int) Math.min(listToSplit.size(),actualCounter + batchSize);
                    return listToSplit.subList(actualCounter, finalLength);
                }).collect(Collectors.toList());
        System.out.println(collect);
    }
}

If you're open to using a third-party library, you can use Collectors2.chunk() from Eclipse Collections with a Java Stream.

@Test
public void chunk()
{
    List<Integer> listToSplit = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    List<MutableList<Integer>> chunks = listToSplit.isEmpty() ?
            Lists.mutable.with(Lists.mutable.empty()) :
            listToSplit.stream().collect(Collectors2.chunk(3));

    List<List<Integer>> expected = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9),
            Arrays.asList(10));
    Assert.assertEquals(expected, chunks);
}

注意:我是 Eclipse Collections 的提交者。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM