Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 8中流的笛卡尔乘积作为流(仅使用流)_Java_Set_Java 8_Java Stream_Cartesian Product - Fatal编程技术网

Java 8中流的笛卡尔乘积作为流(仅使用流)

Java 8中流的笛卡尔乘积作为流(仅使用流),java,set,java-8,java-stream,cartesian-product,Java,Set,Java 8,Java Stream,Cartesian Product,我想创建一个方法,该方法创建一个元素流,这些元素是多个给定流的笛卡尔积(最后通过二进制运算符聚合为相同类型)。请注意,参数和结果都是流,而不是集合 例如,对于{A,B}和{X,Y}的两个流,我希望它生成值流{AX,AY,BX,BY}(简单的连接用于聚合字符串)。到目前为止,我已经提出了以下代码: private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, Stream<T>

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

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

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;
}
预期结果:
AKX、AKY、ALX、ALY、BKX、BKY、BLX、BLY

但是,如果我运行代码,会出现以下错误:

非法状态异常:流已被操作或关闭


溪流在哪里被消耗?按平面图?是否可以轻松修复?

在第二次迭代的
flatMap
操作中消耗。因此,每次映射结果时,都必须创建一个新流。因此,您必须提前收集
,以便在每次迭代中获得新流

private static <T> Stream<T> cartesian(BiFunction<T, T, T> aggregator, Stream<T>... streams) {
    Stream<T> result = null;
    for (Stream<T> stream : streams) {
        if (result == null) {
            result = stream;
        } else {
            Collection<T> s = stream.collect(Collectors.toList());
            result = result.flatMap(m -> s.stream().map(n -> aggregator.apply(m, n)));
        }
    }
    return result;
}
专用静态流笛卡尔(双功能聚合器,流…流){
流结果=空;
用于(流:流){
如果(结果==null){
结果=流;
}否则{
Collection s=stream.collect(Collectors.toList());
result=result.flatMap(m->s.stream().map(n->aggregator.apply(m,n));
}
}
返回结果;
}
甚至更短:

private static <T> Stream<T> cartesian(BiFunction<T, T, T> aggregator, Stream<T>... streams) {
    return Arrays.stream(streams).reduce((r, s) -> {
        List<T> collect = s.collect(Collectors.toList());
        return r.flatMap(m -> collect.stream().map(n -> aggregator.apply(m, n)));
    }).orElse(Stream.empty());
}
专用静态流笛卡尔(双功能聚合器,流…流){
返回数组.streams.reduce((r,s)->{
List collect=s.collect(Collectors.toList());
返回r.flatMap(m->collect.stream().map(n->aggregator.apply(m,n));
}).orElse(Stream.empty());
}

在示例中传递流永远比传递列表要好:

private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, List<T>... lists) {
    ...
}
私有静态流笛卡尔(BinaryOperator聚合器,列表…列表){
...
}
然后像这样使用它:

Stream<String> result = cartesian(
  (a, b) -> a + b, 
  Arrays.asList("A", "B"), 
  Arrays.asList("K", "L"), 
  Arrays.asList("X", "Y")
);
流结果=笛卡尔(
(a,b)->a+b,
数组。asList(“A”、“B”),
数组。asList(“K”、“L”),
数组。asList(“X”、“Y”)
);
在这两种情况下,您都从varargs创建一个隐式数组并将其用作数据源,因此惰性是虚构的。您的数据实际上存储在数组中

在大多数情况下,生成的笛卡尔乘积流比输入长得多,因此实际上没有理由让输入变懒。例如,有五个包含五个元素的列表(总共25个),您将得到3125个元素的结果流。所以在内存中存储25个元素不是什么大问题。实际上,在大多数实际情况下,它们已经存储在内存中

为了生成笛卡尔产品流,您需要不断“倒带”所有流(第一个流除外)。要倒带,流应该能够一次又一次地检索原始数据,或者以某种方式对它们进行缓冲(您不喜欢),或者从源(collection、array、file、network、random number等)中再次获取它们,并一次又一次地执行所有中间操作。若源操作和中间操作很慢,那个么惰性解决方案可能比缓冲解决方案慢得多。如果您的源无法再次生成数据(例如,随机数生成器无法生成与以前相同的数字),则您的解决方案将不正确

然而,完全懒惰的解决方案是可能的。不要使用流,而是使用流供应商:

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();
}
专用静态流笛卡尔(BinaryOperator)聚合器,
供应商(数据流){
返回数组.stream(流)
.减少((s1,s2)->
()->s1.get().flatMap(t1->s2.get().map(t2->aggregator.apply(t1,t2)))
.orElse(Stream::empty).get();
}
这个解决方案很有趣,因为我们创建并减少了供应商流,以获得最终的供应商并最终调用它。用法:

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);
流结果=笛卡尔(
(a,b)->a+b,
()->(“A”、“B”)流,
()->“K”、“L”流,
()->“X”、“Y”流
);
result.forEach(System.out::println);

您可以创建一个方法,该方法返回对象的
列表流,而不聚合它们。算法是相同的:在每个步骤中,将第二个流的元素收集到一个列表中,然后将它们附加到第一个流的元素中

聚合器在方法之外

@SuppressWarnings(“未选中”)
公共静态流卡特尔产品(流…流){
//传入数据不正确
if(streams==null)返回Stream.empty();
返回数组.stream(流)
//非空流
.filter(对象::非空)
//将每个列表元素表示为SingletonList
.map(stream->stream.map(集合::singletonList))
//内部列表对的求和
.减少((流程1、流程2)->{
//来自第二个流的列表列表
List list2=stream2.collect(Collectors.toList());
//附加到第一个流
返回stream1.flatMap(inner1->list2.stream()
//内部列表的组合
.map(inner2->{
列表=新的ArrayList();
list.addAll(inner1);
list.addAll(inner2);
退货清单;
}));
}).orElse(Stream.empty());
}
publicstaticvoidmain(字符串[]args){
溪流1=溪流(“A”、“B”);
溪流2=溪流(“K”、“L”);
溪流3=溪流(“X”,“Y”);
@抑制警告
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();
}
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);
AKX
AKY
ALX
ALY
BKX
BKY
BLX
BLY