33

How to get the next element from a List using Java 8 Streams?

If I am iterating over a List, I want to compare current with next element of the list.

Is it doable using Java 8 Stream?

RubioRic
  • 2,442
  • 4
  • 28
  • 35
bhupen
  • 470
  • 1
  • 5
  • 10
  • 2
    Iterating how? How do you determine current and next elements? Why do you feel the need to do this with streams? – Sotirios Delimanolis Nov 14 '15 at 06:44
  • my goal is compare two consecutive element in the list, or maybe i want to check whether they are equal. – bhupen Nov 14 '15 at 13:34
  • 5
    That's a task, not a goal. There must be a reason why you do this. E.g. you want to do something if two elements are equal. What's interesting is how to do that something using streams. The question as it is has already been asked in similar forms. – a better oliver Nov 14 '15 at 15:55

6 Answers6

17

My free StreamEx library allows you to process the pairs of the stream elements using additional pairMap intermediate operation. Like this:

StreamEx.of(input).pairMap((current, next) -> doSomethingWith(current, next));

Where input is a Collection, array or Stream. For example, this way you can easily check whether input is sorted:

boolean isSorted = StreamEx.of(input)
                           .pairMap((current, next) -> next.compareTo(current))
                           .allMatch(cmp -> cmp >= 0);

There's also forPairs terminal operation which is a forEach analog to all pairs of input elements:

StreamEx.of(input).forPairs((current, next) -> doSomethingWith(current, next));

These features work nicely with any stream source (either random access or not) and fully support the parallel streams.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
14

One way is to generate an IntStream of the indices, and fetch the List elements by their index. This is only efficient if the List supports random access (i.e. if your List is a LinkedList, it would be a bad idea, since list.get(i) doesn't take constant time).

For example :

IntStream.range(0,list.size()-1).forEach(i -> {
    doSomething(list.get(i),list.get(i+1));
});

Another way is to store the last element in an array :

List<Element> list = ...
Element[] arr = new Element[1];
list.stream().forEach(e -> {
    if (arr[0] != null)
        doSomething(arr[0],e); 
    arr[0]=e;
});

This will only work for sequential streams.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • is there a way in streams to skip next iteration i.e. increment forEach index to i+2 instead of i+1 for some cases? – Stacky Jul 12 '21 at 00:17
  • @Stacky You can use `IntStream.iterate` to achieve a behavior similar to for loop, and increment the counter by whatever value you wish. For example: `IntStream.iterate (1, i -> i < 10, i -> i + 2).forEach (System.out::println);` – Eran Jul 12 '21 at 07:25
  • What if I need to conditionally increment i? For example: `SomeClass classLevelObj = new SomeClass(); IntStream.iterate (1, i -> i < 10, i -> i + 1).forEach (if(classLevelObj.cond) i+=2 else i+=3);` – Stacky Jul 13 '21 at 02:57
9

Stream.reduce could be used, depending on the goal. As you said, you'd like to compare consecutive elements, the following would print "Same 3":

Stream.of(1,2,3,3).reduce((a,b)->{
    if(a==b) System.out.println("Same "+a);
    return b; // will be "a" for next reduction
});
Marc Dzaebel
  • 425
  • 4
  • 12
2

If you had a list of Integers for example and wanted to check if they are sorted ascending, you could do as follows:

@Test
public void test_listIsSorted() {

    // create integer list for testing purposes: 0,1,2,3, .., 10
    List<Integer> integerList = IntStream.range(0, 10)
            .boxed()
            .collect(Collectors.toList());

    // stream list and compare item n with n+1
    integerList.stream()
            .reduce((integer1, integer2) -> {
                assert integer1 < integer2 : "ordering must be ascending";
                // return second value (which will be processed as "integer1" in the next iteration
                return integer2;
            });

}

This will compare pairs like: (0,1),(1,2),...

Georg Moser
  • 620
  • 7
  • 14
0

You always do one of following:

  1. Convert your stream to a stream of elements containing "history" of last few elements of the stream
  2. Process your stream in such a way that currently processed element is considered to be "next" element and previously processed element is considered to be "current" element.

Implementations of both solutions can be seen in this thread: Is it possible to get next element in the Stream?

Community
  • 1
  • 1
walkeros
  • 4,736
  • 4
  • 35
  • 47
0

I had to do the same and compare the differences of the elements of a stream (originally an array). So I used a method as a parameter to the UnaryOperator that .map() expects as follows...and it worked for me without any special gizmo's:

import java.util.Arrays;

class streamDiffUtil {
    public static void main(String[] args) {
        int[] elements = {1,2,5};
        int result = Arrays.stream(elements)
                .map(value -> calcMaxDiff(value, elements))
                .max()
                .getAsInt();
        System.out.println(result);
    }

    private static int calcMaxDiff(int j, int[] elements) {
        return Arrays.stream(elements)
                .map(value -> Math.abs(j-value))
                .max().getAsInt();
    }
}

What would be nice is to know how the method calcMaxDiff equates to a UnaryIntOperator in the .map signature. That is a little beyond me. Hope this helps you.

Saikat
  • 14,222
  • 20
  • 104
  • 125
Beezer
  • 1,084
  • 13
  • 18