4

Suppose we have a collection (or stream?)

{"1","2","3","4"...}

now we want to take two elements and compose a pair like this

{{"1","2"}, {"2","3"},{"3","4"}..}

We know how to do that the ugly way (for loop).

I wonder how do we do this with java 8 streams?

Because approaches in java streams and reactive extensions are pretty much same (both are monads) I thought there might be something alike RX .pick(2) which would trigger event when 2 elements arrive...

wassgren
  • 18,651
  • 6
  • 63
  • 77
vach
  • 10,571
  • 12
  • 68
  • 106

2 Answers2

1

It's not pretty either but you can do it with a collector. I've used an ArrayList with Arrays inside, but you can use different intermediate collection types if you want.

What I've done here is a collector that adds every item as a second item on the last array added to the accumulator and as a first item on a new array that will be added to the accumulator.

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

ArrayList<Integer[]> pairs =
    stream.collect(
        () -> new ArrayList<>(), 
        (acc, next) -> {
            if(acc.isEmpty()) {
                acc.add(new Integer[] {next, null});
            } else {
                acc.get(acc.size() - 1)[1] = next;
                acc.add(new Integer[] {next, null});
            }
        },
        ArrayList::addAll
    );

pairs.forEach(a -> System.out.println(a[0] + ", " + a[1]));

//-> 1, 2
//-> 2, 3
//-> 3, 4
//-> 4, 5
//-> 5, 6
//-> 6, null
Sauli Tähkäpää
  • 2,034
  • 1
  • 10
  • 9
0

It is possible to use the IntStream.range to loop in a stream-kind-of-way.

String[] str = {"1", "2", "3", "4"};
final String[][] result = IntStream.range(0, str.length - 1)
        .mapToObj(i -> new String[]{str[i], str[i + 1]})
        .toArray(String[][]::new);

The result contains {{"1", "2"}, {"2", "3"}, {"3", "4"}}.

wassgren
  • 18,651
  • 6
  • 63
  • 77