javajava-stream

Peek the next Element in a stream


Is there a way to peek the next element in a stream? The idea rose from a stream of a list of objects, where two following objects should be compared (to smooth some diffs, but that shouldn't matter here). As an old for loop this would look like:

List<Car> autobahn = getCars();
for (int i = 0; i < autobahn.size()-1; i++) {
    if(autobahn.get(i).speed>autobahn.get(i+1).speed)
        autobahn.get(i).honk();
}

The best way so far as stream would be:

autobahn.stream()
            .limit(autobahn.size()-1)
            .filter(car -> car.speed < autobahn.get(autobahn.indexOf(car)+1).speed)
            .forEach(car -> car.honk());

The main-problem with this solution is the indexOf method, since there might be twice the same car on the autobahn. A better solution would be some way to peek the next (or the one before) element (with an helping class, this might be even possible, but looks horrible)

BoxedCar boxedCar = new BoxedCar(autobahn.get(0));
autobahn.stream()
            .skip(1)
            .filter(car -> boxedCar.setContent(car))
            .forEach(car -> car.winTheRace());

with helperclass

class BoxedCar {

    Car content;

    BoxedCar(Car content) {
        this.content = content;
    }
    boolean setContent(Car content) {
        double speed = this.content.speed;
        this.content = content;
        return content.speed > speed;
    }
}

or to divert the Stream<Car> into a kind of Stream<(Car,Car)> with the second stream somehow created by the first one (this sounds also awful and here I have no idea, how this would look).

Is there a nice way to do this with streams, or are we stuck to the for-loop?


Solution

  • Sticking with the for loop wouldn't be a bad idea. The Stream API isn't designed for this type of requirement. You can refer to that answer for more insight.

    However, a simple way to do this using the Stream API would be to use a Stream over the indexes of your list, supposing that you have random access.

    IntStream.range(0, autobahn.size() - 1)
             .filter(i -> autobahn.get(i).speed > autobahn.get(i+1).speed)
             .forEach(i -> autobahn.get(i).honk());
    

    Note that this highly resemble the for loop.