Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collect successive pairs from a stream

Given a stream such as { 0, 1, 2, 3, 4 },

how can I most elegantly transform it into given form:

{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }

(assuming, of course, I've defined class Pair)?

Edit: This isn't strictly about ints or primitive streams. The answer should be general for a stream of any type.

like image 453
Aleksandr Dubinsky Avatar asked Dec 09 '13 11:12

Aleksandr Dubinsky


People also ask

What is the use of collect method in stream?

The collect() method in Stream API collects all objects from a stream object and stored in the type of collection. The user has to provide what type of collection the results can be stored. We specify the collection type using the Collectors Enum.

How do you loop through each element in a stream?

Java Stream forEach() method is used to iterate over all the elements of the given Stream and to perform an Consumer action on each element of the Stream.

Which is the correct way of obtaining a stream from the collection?

You obtain a stream from a collection by calling the stream() method of the given collection. Here is an example of obtaining a stream from a collection: List<String> items = new ArrayList<String>(); items.


2 Answers

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.

like image 181
Stuart Marks Avatar answered Oct 19 '22 20:10

Stuart Marks


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).

like image 21
Tagir Valeev Avatar answered Oct 19 '22 18:10

Tagir Valeev