java 笛卡尔积,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.

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 Stream cartesian(BinaryOperator aggregator, Stream... streams) {

Stream result = null;

for (Stream stream : streams) {

if (result == null) {

result = stream;

} else {

result = result.flatMap(m -> stream.map(n -> aggregator.apply(m, n)));

}

}

return result;

}

This is my desired use case:

Stream result = cartesian(

(a, b) -> a + b,

Stream.of("A", "B"),

Stream.of("X", "Y")

);

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

Expected result: AX, AY, BX, BY.

Another example:

Stream result = cartesian(

(a, b) -> a + b,

Stream.of("A", "B"),

Stream.of("K", "L"),

Stream.of("X", "Y")

);

Expected result: AKX, AKY, ALX, ALY, BKX, BKY, BLX, BLY.

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

IllegalStateException: stream has already been operated upon or closed

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 Stream cartesian(BinaryOperator aggregator, List... lists) {

...

}

And use it like this:

Stream result = cartesian(

(a, b) -> a + b,

Arrays.asList("A", "B"),

Arrays.asList("K", "L"),

Arrays.asList("X", "Y")

);

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.

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.

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 Stream cartesian(BinaryOperator aggregator,

Supplier>... 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 result = cartesian(

(a, b) -> a + b,

() -> Stream.of("A", "B"),

() -> Stream.of("K", "L"),

() -> Stream.of("X", "Y")

);

result.forEach(System.out::println);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值