Поток декартовых произведений других потоков, каждый элемент в виде списка?
Как я могу реализовать функцию, использующую Java 8, чтобы взять некоторое количество потоков и создать поток, в котором каждый элемент является списком, состоящим из одного элемента декартового произведения потоков?
Я посмотрел на этот вопрос - этот вопрос использует агрегатор, который является 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 ответа
Возможное решение заключается в следующем:
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]
Вы можете реализовать что-то вроде этого:
List<Stream<String>> listStreams = List.of(
Stream.of("A", "B", "C", "D"),
Stream.of("I", "J", "K"),
Stream.of("Y", "Z"));
Stream<List<String>> streamLists = listStreams.stream()
// represent each list element as SingletonList<Object>
.map(stream -> stream.map(Collections::singletonList))
// summation of pairs of inner lists
.reduce((stream1, stream2) -> {
// list of lists from second stream
List<List<String>> list2 = stream2.collect(Collectors.toList());
// append to the first stream
return stream1.flatMap(inner1 -> list2.stream()
// combinations of inner lists
.map(inner2 -> {
List<String> list = new ArrayList<>();
list.addAll(inner1);
list.addAll(inner2);
return list;
}));
}).orElse(Stream.empty());
// output
streamLists.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]
[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]
См. Также: Найдите декартово произведение двух списков