【问题标题】:How to use streams to find pairs of elements from two lists or array multiplication如何使用流从两个列表或数组乘法中查找元素对
【发布时间】:2017-07-02 09:10:48
【问题描述】:

我有两个数字列表,我想找到所有可能的数字对。例如,给定列表[1, 2, 3][3, 4],结果应该是:

[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]

我知道我可以使用 for 循环 做到这一点,但有没有更简洁的方法可以使用 Java 8 流 做到这一点?

我尝试了以下方法,但我得到了List<Stream<int[]>> 而不是List<int[]>

public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2, 3);
    List<Integer> list2 = Arrays.asList(3, 4);
    List<int[]> pairs = list1.stream()
                             .map(i -> list2.stream()
                                            .map(j -> new int[]{i, j}))
                             .collect(Collectors.toList());
    pairs.forEach(i -> {
        System.out.println("{" + i[0] + "," + i[1] + "}");
    });
}

【问题讨论】:

标签: java list java-8 java-stream cartesian-product


【解决方案1】:

使用flatMap() 方法代替ma​​p(),它将流合并为一个。 参考:Difference Between map() and flatMap()flatMap() example

【讨论】:

  • 接受此作为答案,因为它还提供了链接以获取有关 flatMap 的更多信息
  • 把这些信息带到这里而不是隐藏在链接后面
【解决方案2】:

您只需将您的第一个 map() 替换为 flatMap()

【讨论】:

    【解决方案3】:

    这是一个使用IntStream 和两个int 数组作为源而不是List&lt;Integer&gt; 的解决方案。我想看看是否可以在不将每个int 装箱为Integer 的情况下解决这个问题。

    int[] one = new int[]{1, 2, 3};
    int[] two = new int[]{3, 4};
    List<IntIntPair> list = new ArrayList<>();
    IntStream.of(one).forEach(i ->
            IntStream.of(two).mapToObj(j -> PrimitiveTuples.pair(i, j)).forEach(list::add));
    System.out.println(list);
    // [1:3, 1:4, 2:3, 2:4, 3:3, 3:4]
    

    不幸的是,我无法在IntStream 上使用flatMap,因为它返回IntStream。目前IntStream 上没有flatMapToObj,这是这里需要的。所以我改用forEach

    我从Eclipse Collections 使用的IntIntPairPrimitiveTuples 类,因为它们使将列表输出为字符串变得更简单。您可以像在解决方案中一样使用int[]。代码如下所示。

    List<int[]> list = new ArrayList<>();
    IntStream.of(one).forEach(i ->
            IntStream.of(two).mapToObj(j -> new int[]{i, j}).forEach(list::add));
    

    在 Eclipse Collections 的 8.1 版本中(将于 3 月中旬发布),现在库中所有原始容器上都有一个 flatCollect 方法,可用于解决此问题。这基本上完成了IntStream 上的flatMapToObj 方法应该做的事情。

    IntList a = IntLists.mutable.with(1, 2, 3);
    IntList b = IntLists.mutable.with(3, 4);
    List<IntIntPair> result =
            a.flatCollect(
                    i -> b.collect(j -> PrimitiveTuples.pair(i, j)),
                    Lists.mutable.empty());
    System.out.println(result);
    // [1:3, 1:4, 2:3, 2:4, 3:3, 3:4]
    

    更新:

    正如 Boris the Spider 在 cmets 中所指出的,forEach 解决方案不是线程安全的,如果 IntStreamparallel,则会中断。以下解决方案应以串行或并行方式工作。我很高兴有人指出了这一点,因为我没有想过在 IntStream 上执行 mapToObj,然后是 flatMap

    int[] one = new int[]{1, 2, 3};
    int[] two = new int[]{3, 4};
    List<int[]> list = IntStream.of(one).parallel()
            .mapToObj(i -> IntStream.of(two).mapToObj(j -> new int[]{i, j}))
            .flatMap(e -> e)
            .collect(Collectors.toList());
    list.stream().map(e -> "{" + e[0] + "," + e[1] + "}").forEach(System.out::println);
    

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

    【讨论】:

      【解决方案4】:

      在这种使用 flatMap 的特殊情况下,您甚至可以绕过数组创建并让您的代码更简单,如下所示:

      list1.stream()
           .flatMap(i -> list2.stream().map(j -> "{" + i+ "," + j + "}"))
           .forEach(System.out::println);
      

      【讨论】:

        【解决方案5】:
        //return pair of numbers
        List<List<Integer>> pairs=numbers.stream()
                .flatMap(i -> numbers2.stream()
                .map(j -> Arrays.asList(i,j)))
                .collect(Collectors.toList());
        
        pairs.stream().forEach(System.out::println);
        

        【讨论】:

          【解决方案6】:

          您可以使用mapreduce 方法生成可能组合的二维列表:

          List<Integer> list1 = Arrays.asList(1, 2, 3);
          List<Integer> list2 = Arrays.asList(3, 4);
          
          List<List<Integer>> combinations = Stream.of(list1, list2)
                  // represent each list element as a singleton list
                  .map(list -> list.stream().map(Collections::singletonList)
                          // Stream<List<List<Integer>>>
                          .collect(Collectors.toList()))
                  // intermediate output
                  //[[1], [2], [3]]
                  //[[3], [4]]
                  .peek(System.out::println)
                  // summation of pairs of inner lists
                  .reduce((listA, listB) -> listA.stream()
                          // combinations of inner lists
                          .flatMap(inner1 -> listB.stream()
                                  // merge two inner lists into one
                                  .map(inner2 -> Stream.of(inner1, inner2)
                                          .flatMap(List::stream)
                                          .collect(Collectors.toList())))
                          // list of combinations
                          .collect(Collectors.toList()))
                  // otherwise an empty list
                  .orElse(Collections.emptyList());
          
          // output
          System.out.println(combinations);
          // [[1, 3], [1, 4], [2, 3], [2, 4], [3, 3], [3, 4]]
          

          另见:Generate all combinations from multiple lists

          【讨论】:

            【解决方案7】:

            当然,你可以每次在Stream#flatMap中创建一个Stream,但是就性能而言,它很差。

            相反,我建议您以更具声明性的方式选择 Stream#mapMulti 和运算符

            List<int[]> pairs = list1.stream()
                                     .mapMulti((Integer left, Consumer<int[]> consumer) -> 
                                         list2.forEach(right -> consumer.accept(new int[]{left, right})))
                                     .toList();
            

            产生相同的结果,没有 Stream 创建的开销

            {1,3}
            {1,4}
            {2,3}
            {2,4}
            {3,3}
            {3,4}
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-05-03
              • 2018-10-11
              • 1970-01-01
              • 2022-11-28
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多