无法创建数组与 java 流的组合

not able to create combinations of arrays with java streams

我正在尝试从多个大小的数组创建不同的组合,从每个数组中选择一个元素。 检查下面的代码,collect 变量给出了正确的输出,但是 collect1 给出了错误。

int[] pants= {3, 5, 7}, shirts = {4, 7, 8}, 
                skirts = {5, 8}, shoes = {3};

List<int[]> collect = Arrays.stream(pants)
            .mapToObj(i -> Arrays.stream(shirts)
            .mapToObj(j -> new int[] {i,j}))
            
            .flatMap(Function.identity())
            .collect(Collectors.toList());
    
    List<int[]> collect1 = Arrays.stream(pants)
            .mapToObj(i -> Arrays.stream(shirts)
            .mapToObj(j -> Arrays.stream(skirts)
            .mapToObj(k -> Arrays.stream(shoes)
            .mapToObj(l -> new int[] {i,j,k,l}))))
            
            .flatMap(Function.identity())
            .collect(Collectors.toList());

collect.forEach(ar->System.out.println(Arrays.toString(ar)));

collect1 的错误是:类型不匹配:无法从 List>> 转换为 List

根据 MapToObj 的文档:“Returns 一个对象值流,由将给定函数应用于此流的元素的结果组成。”

所以每次调用它时,它都会 returns 一个 Stream。因此,如果您只需要流中的值,则必须在每次调用 MapToObj:

时添加对 flatmap 的调用
List<int[]> collect1 = Arrays.stream(pants)
        .mapToObj(i -> Arrays.stream(shirts)
          .mapToObj(j -> Arrays.stream(skirts)
            .mapToObj(k -> Arrays.stream(shoes)
              .mapToObj(l -> new int[] {i,j,k,l}))))
        .flatMap(Function.identity())
        .flatMap(Function.identity())
        .flatMap(Function.identity())
        .collect(Collectors.toList());

将问题尽可能减少通常是个好主意。这是包含 2 个项目的 2 个列表。组合它们意味着获取第一个列表的第一项,然后与第二个列表中的每一个产生一个组合。然后将第一个列表中的第二个项目与第二个列表中的每个项目组合起来。等等……

伪代码:

combine({1, 2}, {3, 4}) == { { 1, 3 }, { 1, 4 }, { 2, 3 }, { 2, 4 } };

现在让我们尝试用流来表达:

int[] first = { 1, 2 }, second = { 3, 4 };

int[][] combinations = Arrays.stream(first)
        .flatMap(a -> Arrays.stream(second)
                .map(b -> new int[]{a, b}))
        .toArray();

棘手的是,这不起作用! Java 中的流(以及一般的泛型)只能使用引用类型,它们不支持简单的值类型,例如 int。这使得代码有点复杂。类型int[]扩展了Object,所以我们可以这样改造代码:

Object[] combinations = Arrays.stream(first)
        .mapToObj(a -> Arrays.stream(second)
                .mapToObj(b -> new int[]{a, b}))
        .flatMap(x -> x)
        .toArray();

添加第三个输入数组使得:

Object[] combinations = Arrays.stream(first)
        .mapToObj(a -> Arrays.stream(second)
                .mapToObj(b -> Arrays.stream(third)
                        .mapToObj(c -> new int[]{a, b, c}))
                .flatMap(x -> x))
        .flatMap(x -> x)
        .toArray();

或者,使用 int 数组列表:

List<int[]> combinations = Arrays.stream(first)
        .mapToObj(a -> Arrays.stream(second)
                .mapToObj(b -> Arrays.stream(third)
                        .mapToObj(c -> new int[]{a, b, c}))
                .flatMap(x -> x))
        .flatMap(x -> x)
        .collect(Collectors.toList());

冲洗并重复,直到您添加了所有输入:

List<int[]> combinations = Arrays.stream(first)
        .mapToObj(a -> Arrays.stream(second)
                .mapToObj(b -> Arrays.stream(third)
                        .mapToObj(c -> Arrays.stream(fourth).mapToObj(d -> new int[]{a, b, c, d}))
                        .flatMap(x -> x))
                .flatMap(x -> x))
        .flatMap(x -> x)
        .collect(Collectors.toList());

如果您从盒装整数开始,您的流会变得简单得多:

Integer[] first = { 1, 2 }, second = { 3, 4 }, third = { 5, 6 }, fourth = { 7, 8 };

List<int[]> combinations = Arrays.stream(first)
        .flatMap(a -> Arrays.stream(second)
                .flatMap(b -> Arrays.stream(third)
                        .flatMap(c -> Arrays.stream(fourth)
                                .map(d -> new int[]{a, b, c, d}))))
        .collect(Collectors.toList());

首先将每个 IntStream 转换为 Stream<Integer> 可以实现类似的效果。我发现这比 mapToObj.flatMap 风格更容易阅读和理解:

List<int[]> combinations = Arrays.stream(first)
        .boxed()
        .flatMap(a -> Arrays.stream(second)
                .boxed()
                .flatMap(b -> Arrays.stream(third)
                        .boxed()
                        .flatMap(c -> Arrays.stream(fourth)
                                .boxed()
                                .map(d -> new int[]{a, b, c, d}))))
        .collect(Collectors.toList());

注意创建大量嵌套和小型流以及 boxing/unboxing 原始值时的性能影响。