如何使用流来查找两个列表或数组中的元素配对乘积

22

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

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

我知道可以使用for循环来做到这一点,但是否有更简洁的方法可以使用Java 8 streams来实现呢?

我尝试了以下方法,但似乎缺少了一些东西,因为我得到的是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] + "}");
    });
}

http://stackoverflow.com/a/34784924/1849366 - Rajind Ruparathna
使用flatMap()函数已经成功实现。 - Ankur Singh
3
你可能想查看关于笛卡尔积的内容。但在这种情况下,我建议使用两个for循环,因为它会比任何Java 8流魔法更易读。 - evgenii
@evgenii 是的,请参见https://dev59.com/5FwY5IYBdhLWcg3w2K7c(可能是重复的...) - Marco13
1
这个回答解决了你的问题吗?如何从两个数组中获取所有可能的组合? - user15613375
1
如何生成两个数组的组合? - user15613375
7个回答

17

1
接受这个答案,因为它还提供了链接以获取有关flatMap的更多信息。 - Ankur Singh
5
请将那些信息带到这里,而不是将它们隐藏在链接后面。 - Lightness Races in Orbit

14

你只需要用flatMap()代替第一个 map() 即可。


14

这里是一个使用两个 int 数组作为源而不是 List<Integer>IntStream 解决方案。我想知道是否有可能解决这个问题,而不必将每个 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

我使用了IntIntPairPrimitiveTuples类来自于Eclipse Collections,因为它们使输出列表作为字符串更简单。您可以像您在解决方案中所做的那样使用int[]。 代码如下。

List<int[]> list = new ArrayList<>();
IntStream.of(one).forEach(i ->
        IntStream.of(two).mapToObj(j -> new int[]{i, j}).forEach(list::add));
在即将于3月中旬发布的Eclipse Collections 8.1版本中,所有基本容器都新增了一个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在评论中指出的那样,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 的贡献者。


0
在这种特定情况下,使用flatMap甚至可以绕过数组创建,使您的代码更简单,如下所示:
list1.stream()
     .flatMap(i -> list2.stream().map(j -> "{" + i+ "," + j + "}"))
     .forEach(System.out::println);

0

你可以使用 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]]

另请参见:从多个列表生成所有组合


0
//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);

0
当然,你可以在每次使用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}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接