2017-01-08 4 views
7

Как я могу реализовать функцию с использованием Java 8 для принятия некоторого количества потоков и создать поток, в котором каждый элемент представляет собой список, состоящий из одного члена декартова произведения потоков?Поток декартова продукта других потоков, каждый элемент как список?

Я рассмотрел this question - этот вопрос использует агрегатор, который является BinaryOperator (беря два элемента подобного типа и производя предметы того же типа). Я бы хотел, чтобы в конечном итоге элементы были List s, а не типы элементов во входных потоках.

В частности, предположим, что мой искомую функцию называется product следующее:

Stream<List<String>> result = 
    product(
     Stream.of("A", "B", "C", "D"), 
     Stream.of("I", "J", "K), 
     Stream.of("Y", "Z") 
    ); 

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

должен напечатать:

[A, I, Y] 
[A, I, Z] 
[A, J, Y] 
[A, J, Z] 
[A, K, Y] 
[A, K, Z] 
[B, I, Y] 
... 
[D, K, Y] 
[D, K, Z] 

В идеале я хотел бы, чтобы эта операция будет как можно ленивее. Например, если входные потоки создаются Stream.generate(), было бы здорово, если бы поставщики этих потоков не выполнялись до тех пор, пока это не было абсолютно необходимо.

+2

ли эта помощь? http://stackoverflow.com/a/40202722/5629413 –

+5

Поток может быть использован только один раз, поэтому, если вы предоставляете 'product()' три одноразовых потока, тогда он должен будет хранить все значения двух из них, и в этом случае вам может быть лучше дать три объекта «Итерабельные», чтобы предотвратить дублирование входов. – Andreas

+0

Это довольно сложно. В JDK нет ничего, чтобы поддержать это. Вам нужно будет написать код ... – Bohemian

ответ

1

Возможное решение заключается в следующем:

private static <T> Stream<List<T>> product(Stream<T>... streams) { 
    if (streams.length == 0) { 
     return Stream.empty(); 
    } 
    List<List<T>> cartesian = streams[streams.length - 1].map(x -> Collections.singletonList(x)).collect(Collectors.toList()); 
    for (int i = streams.length - 2; i >= 0; i--) { 
     final List<List<T>> previous = cartesian; 
     cartesian = streams[i].flatMap(x -> previous.stream().map(p -> { 
      final List<T> list = new ArrayList<T>(p.size() + 1); 
      list.add(x); 
      list.addAll(p); 
      return list; 
     })).collect(Collectors.toList()); 
    } 
    return cartesian.stream(); 
} 

public static void main(String... args) { 
    final Stream<List<String>> result = 
      product(
        Stream.of("A", "B", "C", "D"), 
        Stream.of("I", "J", "K"), 
        Stream.of("Y", "Z") 
      ); 

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

Продукт-вызов возвращает Stream<List<String>> результат, который печатает, как

[A, I, Y] 
[A, I, Z] 
[A, J, Y] 
[A, J, Z] 
[A, K, Y] 
[A, K, Z] 
[B, I, Y] 
[B, I, Z] 
[B, J, Y] 
[B, J, Z] 
[B, K, Y] 
[B, K, Z] 
[C, I, Y] 
[C, I, Z] 
[C, J, Y] 
[C, J, Z] 
[C, K, Y] 
[C, K, Z] 
[D, I, Y] 
[D, I, Z] 
[D, J, Y] 
[D, J, Z] 
[D, K, Y] 
[D, K, Z] 
+0

В моем ответе было внесено изменение, предлагающее вернуть список напрямую, а не поток. Я вообще не против этой модификации, так как это, вероятно, лучше. Однако мне пришлось отклонить его по двум причинам: (i) мой ответ соответствует именно заданному вопросу, т. Е. Возвращает поток, и (ii) предлагаемый ответ не компилируется --- пожалуйста, поместите свой код в среду IDE и запустите это, прежде чем публиковать честно! – marco