Collect successive pairs from a stream
Solution 1:
The Java 8 streams library is primarily geared toward splitting streams into smaller chunks for parallel processing, so stateful pipeline stages are quite limited, and doing things like getting the index of the current stream element and accessing adjacent stream elements are not supported.
A typical way to solve these problems, with some limitations, of course, is to drive the stream by indexes and rely on having the values being processed in some random-access data structure like an ArrayList from which the elements can be retrieved. If the values were in arrayList
, one could generate the pairs as requested by doing something like this:
IntStream.range(1, arrayList.size())
.mapToObj(i -> new Pair(arrayList.get(i-1), arrayList.get(i)))
.forEach(System.out::println);
Of course the limitation is that the input cannot be an infinite stream. This pipeline can be run in parallel, though.
Solution 2:
My StreamEx library which extends standard streams provides a pairMap
method for all stream types. For primitive streams it does not change the stream type, but can be used to make some calculations. Most common usage is to calculate differences:
int[] pairwiseDiffs = IntStreamEx.of(input).pairMap((a, b) -> (b-a)).toArray();
For object stream you can create any other object type. My library does not provide any new user-visible data structures like Pair
(that's the part of library concept). However if you have your own Pair
class and want to use it, you can do the following:
Stream<Pair> pairs = IntStreamEx.of(input).boxed().pairMap(Pair::new);
Or if you already have some Stream
:
Stream<Pair> pairs = StreamEx.of(stream).pairMap(Pair::new);
This functionality is implemented using custom spliterator. It has quite low overhead and can parallelize nicely. Of course it works with any stream source, not just random access list/array like many other solutions. In many tests it performs really well. Here's a JMH benchmark where we find all input values preceding a larger value using different approaches (see this question).
Solution 3:
You can do this with the Stream.reduce() method (I haven't seen any other answers using this technique).
public static <T> List<Pair<T, T>> consecutive(List<T> list) {
List<Pair<T, T>> pairs = new LinkedList<>();
list.stream().reduce((a, b) -> {
pairs.add(new Pair<>(a, b));
return b;
});
return pairs;
}
Solution 4:
This is not elegant, it's a hackish solution, but works for infinite streams
Stream<Pair> pairStream = Stream.iterate(0, (i) -> i + 1).map( // natural numbers
new Function<Integer, Pair>() {
Integer previous;
@Override
public Pair apply(Integer integer) {
Pair pair = null;
if (previous != null) pair = new Pair(previous, integer);
previous = integer;
return pair;
}
}).skip(1); // drop first null
Now you can limit your stream to the length you want
pairStream.limit(1_000_000).forEach(i -> System.out.println(i));
P.S. I hope there is better solution, something like clojure (partition 2 1 stream)