5

I am trying to generate pairs of integers - I have a class Pair with a constructor taking 2 ints. The following code works but seems rather clunky - in particular the conversion from an intStream to an object stream using mapToObj(Integer::new).

private static List<Pair> success() {
    return IntStream.range(0, 10).
            mapToObj(Integer::new).flatMap(i -> IntStream.range(12, 15).
                mapToObj(j -> new Pair(i, j))).
            collect(Collectors.toList());
}

Firstly does anyone have a more elegant way to do this ?

Secondly when I refactored to extract some streams as variables, I get an error: IllegalStateException: stream has already been operated upon or closed. Here is the refactored method - does anyone know if this a problem with the code ?

static List<Pair> fail() {
    Stream<Integer> outer = IntStream.range(0, 10).mapToObj(Integer::new);
    IntStream inner = IntStream.range(12, 15);
    Stream<Pair> pairStream = outer.flatMap(i -> 
            inner.mapToObj(j -> new Pair(i, j)));
    return pairStream.collect(Collectors.toList());
}
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
Barry Fortune
  • 71
  • 1
  • 4
  • 1
    answer to your second question - http://stackoverflow.com/questions/27990451/java-8-stream-illegalstateexception-stream-has-already-been-operated-on-or-clos – ka4eli Aug 04 '15 at 16:55
  • If you know a little maths you can do `IntStream.range(0, 30).mapToObj(i -> new Pair(i/3, 12 + i%3)).collect(Collectors.toList());`, but I agree with @Jeffrey. Nothing is going to be clearer than nested `for` loops. – Paul Boddington Aug 04 '15 at 17:44
  • static imports for IntStream.range and Collectors.toList will help reduce the information-sparse bulk. – nclark Jul 05 '17 at 17:51

3 Answers3

4

It is possible to make it a bit more concise by replacing mapToObj(Integer::new) with boxed- but apart from that, Java is not that concise:

IntStream.range(0, 10)
        .boxed()
        .flatMap(i -> IntStream.range(12, 15)
                               .mapToObj(j -> new Pair(i, j)))
        .collect(Collectors.toList());

As for the second question: There are other answers which link to the problem. The concrete problem is that inner is not used once, but each time of the outer flatMap().

This way it works:

        final IntStream range = IntStream.range(0, 10);
        List<Pair> ps =  range
               .boxed().flatMap(i -> {
                   final IntStream range1 = IntStream.range(12, 15);
                   return range1.
                           mapToObj(j -> new Pair<>(i, j));
               }).
            collect(Collectors.toList());
user140547
  • 7,750
  • 3
  • 28
  • 80
1

Why not use plain for-loops? Plain for-loops will:

  1. Look nicer
  2. Make your intent clear

static List<Pair> fail() {
    List<Pair> pairs = new ArrayList<>(30);

    for (int i = 0; i < 10; i++) {
        for (int j = 12; j < 15; j++) {
            pairs.add(new Pair(i, j));
        }
    }

    return pairs;
}
Jeffrey
  • 44,417
  • 8
  • 90
  • 141
0

If your Pair class accepts primitive ints you can eliminate the unnecessary boxing this way:

private static List<Pair> success() {
    return IntStream.range(0, 10).
            mapToObj(i -> IntStream.range(12, 15).
                mapToObj(j -> new Pair(i, j))).
            flatMap(Function.identity()).
            collect(Collectors.toList());
}

As for extracting streams into variables, you may create a supplier instead:

private static List<Pair> success() {
    Supplier<IntStream> inner = () -> IntStream.range(12, 15);
    return IntStream.range(0, 10).
            mapToObj(i -> inner.get().
                mapToObj(j -> new Pair(i, j))).
            flatMap(Function.identity()).
            collect(Collectors.toList());
}

Though it seems unnecessary for me to extract the stream into the variable.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334