如何使用流从两个列表或数组乘法中查找元素对

Posted

技术标签:

【中文标题】如何使用流从两个列表或数组乘法中查找元素对【英文标题】: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] + "");
    );

【问题讨论】:

***.com/a/34784924/1849366 这适用于使用 flatMap() 您可能想了解有关笛卡尔积的信息。但在这种情况下,我建议使用两个 for 循环,因为它比任何 java 8 流魔术都更具可读性。 这能回答你的问题吗? How to get all possible combinations from two arrays? 复制:How do I generate combinations of two arrays? 【参考方案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,但是就性能而言,它很差。

相反,我建议您以更具声明性的方式选择 java-16 的 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

【讨论】:

以上是关于如何使用流从两个列表或数组乘法中查找元素对的主要内容,如果未能解决你的问题,请参考以下文章

如何在python列表中查找某个元素的索引

如何使用流从对象列表中获取地图? [复制]

如何在 Pyspark 中使用 groupby 和数组元素?

如何使用java流从列表中获取前5个最大值

如何使用numpy查找数组中元素的索引? [复制]

数组,或只是一个列表......不确定如何编码[关闭]