Составление потоков с помощью flatmap в Java 8
Давайте рассмотрим, у меня есть следующий класс:
class A {
int i, j, k;
public A(int i, int j, int k) {
this.i = i; this.j = j; this.k = k;
}
}
где i
, j
, k
иметь известный диапазон: r_i
, r_j
, r_k
, Теперь я хочу сгенерировать все возможные экземпляры A
в этом диапазоне. Я мог бы придумать что-то вроде:
Stream.iterate(0, n -> ++n).limit(r_i)
.flatMap(i -> Stream.iterate(0, n -> ++n).limit(r_j)
.flatMap(j -> Stream.iterate(0, n -> ++n).limit(r_k)
.map(k -> new A(i, j, k)))).collect(Collectors.toList())
Во-первых, это слишком многословно. Есть ли способ сократить его? В частности, я не мог найти range
на Stream
, Во-вторых, компилятор не может определить тип возвращаемого типа. Считает этоList<Object>
вместо ожидаемого List<A>
, Как я могу это исправить?
2 ответа
Один из способов использования range
это выполнить конвертацию бокса сразу после этого:
List<A> list=IntStream.range(0, r_i).boxed()
.flatMap(i -> IntStream.range(0, r_j).boxed()
.flatMap(j -> IntStream.range(0, r_k)
.mapToObj(k -> new A(i, j, k)))).collect(Collectors.toList());
Это не самый красивый код, но IntStream.range(0, max).boxed()
все еще лучше чем Stream.iterate(0, n -> n+1).limit(max)
...
Одна альтернатива состоит в том, чтобы использовать реальную операцию выравнивания, а не вложенные операции:
List<A> list=IntStream.range(0, r_i).boxed()
.flatMap(i -> IntStream.range(0, r_j).mapToObj(j -> new int[]{i,j}))
.flatMap(ij -> IntStream.range(0, r_k).mapToObj(k -> new A(ij[0], ij[1], k)))
.collect(Collectors.toList());
Главный недостаток, который я вижу, состоит в том, что он страдает от отсутствия IntPair
или же Tuple<int,int>
тип. Таким образом, он использует массив в качестве обходного пути.
Если нормально иметь изменяемую переменную, вы можете получить список класса A следующим образом...
List<A> newCollect = new ArrayList<>();
IntStream.range(0, r_i).forEach(
i -> IntStream.range(0, r_j).forEach(
j -> IntStream.range(0, r_k).forEach(
k -> newCollect.add(new A(i, j, k))
)
)
);
или вы можете сделать список из списка A, затем flatMap дважды, как это...
List<A> newCollect2 = IntStream.range(0, r_i).mapToObj(
i -> IntStream.range(0, r_j).mapToObj(
j -> IntStream.range(0, r_k).mapToObj(
k -> new A(i, j, k)
).collect(Collectors.toList())
).collect(Collectors.toList())
)
.flatMap(a -> a.stream())
.flatMap(a -> a.stream())
.collect(Collectors.toList());