Составление потоков с помощью 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());
Другие вопросы по тегам