Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using java 8 streams to generate pairs of integers

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());
}
like image 659
Barry Fortune Avatar asked Aug 04 '15 16:08

Barry Fortune


2 Answers

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());
like image 162
user140547 Avatar answered Oct 29 '22 17:10

user140547


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;
}
like image 1
Jeffrey Avatar answered Oct 29 '22 17:10

Jeffrey