Java 8中流的笛卡尔积作为流(仅使用流) [英] Cartesian product of streams in Java 8 as stream (using streams only)

查看:669
本文介绍了Java 8中流的笛卡尔积作为流(仅使用流)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个方法来创建一个元素流,这些元素是多个给定流的笛卡尔积(最后由二元运算符聚合到同一类型)。请注意,参数和结果都是流,不是集合。

I would like to create a method which creates a stream of elements which are cartesian products of multiple given streams (aggregated to the same type at the end by a binary operator). Please note that both arguments and results are streams, not collections.

例如,对于 {A,B}的两个流 {X,Y} 我希望它生成值 {AX,AY,BX,BY} 的流(简单串联用于聚合字符串) )。到目前为止,我已经提出了这个代码:

For example, for two streams of {A, B} and {X, Y} I would like it produce stream of values {AX, AY, BX, BY} (simple concatenation is used for aggregating the strings). So far, I have came up with this code:

private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, Stream<T>... streams) {
    Stream<T> result = null;

    for (Stream<T> stream : streams) {
        if (result == null) {
            result = stream;
        } else {
            result = result.flatMap(m -> stream.map(n -> aggregator.apply(m, n)));
        }
    }

    return result;
}

这是我想要的用例:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Stream.of("A", "B"), 
  Stream.of("X", "Y")
);

System.out.println(result.collect(Collectors.toList()));

预期结果: AX,AY,BX,BY

另一个例子:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Stream.of("A", "B"), 
  Stream.of("K", "L"), 
  Stream.of("X", "Y")
);

预期结果: AKX,AKY,ALX,ALY,BKX,BKY, BLX,BLY

但是,如果我运行代码,我会收到此错误:

However, if I run the code, I get this error:

IllegalStateException:流已经被操作或关闭

流消耗在哪里?通过 flatMap ?是否可以轻松修复?

Where is the stream consumed? By flatMap? Can it be easily fixed?

推荐答案

在示例中传递流程绝不比传递列表更好:

Passing the streams in your example is never better than passing Lists:

private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, List<T>... lists) {
    ...
}

并像这样使用它:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Arrays.asList("A", "B"), 
  Arrays.asList("K", "L"), 
  Arrays.asList("X", "Y")
);

在这两种情况下,你都会从varargs创建一个隐式数组并将其用作数据源,因此懒惰是假想。您的数据实际存储在数组中。

In both cases you create an implicit array from varargs and use it as data source, thus the laziness is imaginary. Your data is actually stored in the arrays.

在大多数情况下,产生的笛卡尔积产品流比输入长得多,因此实际上没有理由进行输入懒。例如,有五个元素的列表(总共25个),您将得到3125个元素的结果流。因此在存储器中存储25个元素不是很大的问题。实际上在大多数实际情况中它们已经存储在内存中。

In most of the cases the resulting Cartesian product stream is much longer than the inputs, thus there's practically no reason to make the inputs lazy. For example, having five lists of five elements (25 in total), you will have the resulting stream of 3125 elements. So storing 25 elements in the memory is not very big problem. Actually in most of the practical cases they are already stored in the memory.

为了生成笛卡尔积的流,你需要不断地回放所有流(除了第一个)。要回放,流应该能够一次又一次地检索原始数据,或者以某种方式缓冲它们(你不喜欢它们)或者从源中再次抓取它们(colleciton,数组,文件,网络,随机数等)。并且一次又一次地执行所有中间操作。如果您的源操作和中间操作很慢,那么延迟解决方案可能比缓冲解决方案慢得多。如果您的来源无法再次生成数据(例如,随机数生成器无法生成之前生成的相同数字),则您的解决方案将不正确。

In order to generate the stream of Cartesian products you need to constantly "rewind" all the streams (except the first one). To rewind, the streams should be able to retrieve the original data again and again, either buffering them somehow (which you don't like) or grabbing them again from the source (colleciton, array, file, network, random numbers, etc.) and perform again and again all the intermediate operations. If your source and intermediate operations are slow, then lazy solution may be much slower than buffering solution. If your source is unable to produce the data again (for example, random numbers generator which cannot produce the same numbers it produced before), your solution will be incorrect.

尽管如此完全懒惰的解决方案是可能的。只使用不是流,而是流供应商:

Nevertheless totally lazy solution is possbile. Just use not streams, but stream suppliers:

private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator,
                                       Supplier<Stream<T>>... streams) {
    return Arrays.stream(streams)
        .reduce((s1, s2) -> 
            () -> s1.get().flatMap(t1 -> s2.get().map(t2 -> aggregator.apply(t1, t2))))
        .orElse(Stream::empty).get();
}

解决方案很有意思,因为我们创建并减少供应商流以获得由此产生的供应商并最终称之为用法:

The solution is interesting as we create and reduce the stream of suppliers to get the resulting supplier and finally call it. Usage:

Stream<String> result = cartesian(
          (a, b) -> a + b, 
          () -> Stream.of("A", "B"), 
          () -> Stream.of("K", "L"), 
          () -> Stream.of("X", "Y")
        );
result.forEach(System.out::println);

这篇关于Java 8中流的笛卡尔积作为流(仅使用流)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆