One of the prime motivations for the introduction of Java streams was to allow parallel operations. This led to a requirement that operations on Java streams such as map and filter be independent of the position of the item in the stream or the items around it. This has the advantage of making it easy to split streams for parallel processing. It has the disadvantage of making certain operations more complex.

So the simple answer is that there is no easy way to do things such as take every nth item or map each item to the sum of all previous items.

The most straightforward way to implement your requirement is to use the index of the list you are streaming from:

List<String> list = ...;
return IntStream.range(0, list.size())
    .filter(n -> n % 3 == 0)
    .mapToObj(list::get)
    .toList();

A more complicated solution would be to create a custom collector that collects every nth item into a list.

class EveryNth<C> {
    private final int nth;
    private final List<List<C>> lists = new ArrayList<>();
    private int next = 0;

    private EveryNth(int nth) {
        this.nth = nth;
        IntStream.range(0, nth).forEach(i -> lists.add(new ArrayList<>()));
    }

    private void accept(C item) {
        lists.get(next++ % nth).add(item);
    }

    private EveryNth<C> combine(EveryNth<C> other) {
        other.lists.forEach(l -> lists.get(next++ % nth).addAll(l));
        next += other.next;
        return this;
    }

    private List<C> getResult() {
        return lists.get(0);
    }

    public static Collector<Integer, ?, List<Integer>> collector(int nth) {
        return Collector.of(() -> new EveryNth(nth), 
            EveryNth::accept, EveryNth::combine, EveryNth::getResult));
}

This could be used as follows:

Stream.of("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George")
    .parallel().collect(EveryNth.collector(3)).toList();

Which returns the result ["Anne", "Dean", "George"] as you would expect.

This is a very inefficient algorithm even with parallel processing. It splits all items it accepts into n lists and then just returns the first. Unfortunately it has to keep all items through the accumulation process because it's not until they are combined that it knows which list is the nth one.

Given the complexity and inefficiency of the collector solution I would definitely recommend sticking with the indices based solution above in preference to this if you can. If you aren't using a collection that supports get (e.g. you are passed a Stream rather than a List) then you will either need to collect the stream using Collectors.toList or use the EveryNth solution above.

Answer from sprinter on Stack Overflow
Top answer
1 of 12
59

One of the prime motivations for the introduction of Java streams was to allow parallel operations. This led to a requirement that operations on Java streams such as map and filter be independent of the position of the item in the stream or the items around it. This has the advantage of making it easy to split streams for parallel processing. It has the disadvantage of making certain operations more complex.

So the simple answer is that there is no easy way to do things such as take every nth item or map each item to the sum of all previous items.

The most straightforward way to implement your requirement is to use the index of the list you are streaming from:

List<String> list = ...;
return IntStream.range(0, list.size())
    .filter(n -> n % 3 == 0)
    .mapToObj(list::get)
    .toList();

A more complicated solution would be to create a custom collector that collects every nth item into a list.

class EveryNth<C> {
    private final int nth;
    private final List<List<C>> lists = new ArrayList<>();
    private int next = 0;

    private EveryNth(int nth) {
        this.nth = nth;
        IntStream.range(0, nth).forEach(i -> lists.add(new ArrayList<>()));
    }

    private void accept(C item) {
        lists.get(next++ % nth).add(item);
    }

    private EveryNth<C> combine(EveryNth<C> other) {
        other.lists.forEach(l -> lists.get(next++ % nth).addAll(l));
        next += other.next;
        return this;
    }

    private List<C> getResult() {
        return lists.get(0);
    }

    public static Collector<Integer, ?, List<Integer>> collector(int nth) {
        return Collector.of(() -> new EveryNth(nth), 
            EveryNth::accept, EveryNth::combine, EveryNth::getResult));
}

This could be used as follows:

Stream.of("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George")
    .parallel().collect(EveryNth.collector(3)).toList();

Which returns the result ["Anne", "Dean", "George"] as you would expect.

This is a very inefficient algorithm even with parallel processing. It splits all items it accepts into n lists and then just returns the first. Unfortunately it has to keep all items through the accumulation process because it's not until they are combined that it knows which list is the nth one.

Given the complexity and inefficiency of the collector solution I would definitely recommend sticking with the indices based solution above in preference to this if you can. If you aren't using a collection that supports get (e.g. you are passed a Stream rather than a List) then you will either need to collect the stream using Collectors.toList or use the EveryNth solution above.

2 of 12
14

EDIT - Nov 28, 2017

As user @Emiel suggests in the comments, the best way to do this would be to use Stream.itearate to drive the list through a sequence of indices:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);

List<Integer> result = Stream.iterate(0, i -> i + skip)
    .limit(limit)
    .map(list::get)
    .collect(Collectors.toList());

System.out.println(result); // [1, 4, 7, 10]

This approach doesn't have the drawbacks of my previous answer, which comes below (I've decided to keep it for historical reasons).


Another approach would be to use Stream.iterate() the following way:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);

List<Integer> result = Stream.iterate(list, l -> l.subList(skip, l.size()))
    .limit(limit)
    .map(l -> l.get(0))
    .collect(Collectors.toList());

System.out.println(result); // [1, 4, 7, 10]

The idea is to create a stream of sublists, each one skipping the first N elements of the previous one (N=3 in the example).

We have to limit the number of iterations so that we don't try to get a sublist whose bounds are out of range.

Then, we map our sublists to their first element and collect our results. Keeping the first element of every sublist works as expected because every sublist's begin index is shifted N elements to the right, according to the source list.

This is also efficient, because the List.sublist() method returns a view of the original list, meaning that it doesn't create a new List for each iteration.


EDIT: After a while, I've learnt that it's much better to take either one of @sprinter's approachs, since subList() creates a wrapper around the original list. This means that the second list of the stream would be a wrapper of the first list, the third list of the stream would be a wrapper of the second list (which is already a wrapper!), and so on...

While this might work for small to medium-sized lists, it should be noted that for a very large source list, many wrappers would be created. And this might end up being expensive, or even generating a StackOverflowError.

🌐
Manning Publications
livebook.manning.com › book › java-8-in-action › chapter-5
Chapter 5. Working with streams · Java 8 in Action: Lambdas, streams, and functional-style programming
August 14, 2014 - you can use the Streams API (internal iteration), which supports the filter and collect operations, to manage the iteration over the collection of data for you.
Top answer
1 of 5
24

You can build a custom Collector for this task.

Map<String, String> map = 
    Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
          .collect(MappingErrors.collector());

with:

private static final class MappingErrors {

    private Map<String, String> map = new HashMap<>();

    private String first, second;

    public void accept(String str) {
        first = second;
        second = str;
        if (first != null && first.startsWith("err")) {
            map.put(first, second);
        }
    }

    public MappingErrors combine(MappingErrors other) {
        throw new UnsupportedOperationException("Parallel Stream not supported");
    }

    public Map<String, String> finish() {
        return map;
    }

    public static Collector<String, ?, Map<String, String>> collector() {
        return Collector.of(MappingErrors::new, MappingErrors::accept, MappingErrors::combine, MappingErrors::finish);
    }

}

In this collector, two running elements are kept. Each time a String is accepted, they are updated and if the first starts with "err", the two elements are added to a map.


Another solution is to use the StreamEx library which provides a pairMap method that applies a given function to the every adjacent pair of elements of this stream. In the following code, the operation returns a String array consisting of the first and second element of the pair if the first element starts with "err", null otherwise. null elements are then filtered out and the Stream is collected into a map.

Map<String, String> map = 
    StreamEx.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
            .pairMap((s1, s2) -> s1.startsWith("err") ? new String[] { s1, s2 } : null)
            .nonNull()
            .toMap(a -> a[0], a -> a[1]);

System.out.println(map);
2 of 5
9

You can write a custom collector, or use the much simpler approach of streaming over the list's indexes:

Map<String, String> result = IntStream.range(0, data.size() - 1)
        .filter(i -> data.get(i).startsWith("err"))
        .boxed()
        .collect(toMap(data::get, i -> data.get(i+1)));

This assumes that your data is in a random access friendly list or that you can temporarily dump it into one.

If you cannot randomly access the data or load it into a list or array for processing, you can always make a custom pairing collector so you can write

Map<String, String> result = data.stream()
        .collect(pairing(
                (a, b) -> a.startsWith("err"), 
                AbstractMap.SimpleImmutableEntry::new,
                toMap(Map.Entry::getKey, Map.Entry::getValue)
        ));

Here's the source for the collector. It's parallel-friendly and might come in handy in other situations:

public static <T, V, A, R> Collector<T, ?, R> pairing(BiPredicate<T, T> filter, BiFunction<T, T, V> map, Collector<? super V, A, R> downstream) {

    class Pairing {
        T left, right;
        A middle = downstream.supplier().get();
        boolean empty = true;

        void add(T t) {
            if (empty) {
                left = t;
                empty = false;
            } else if (filter.test(right, t)) {
                downstream.accumulator().accept(middle, map.apply(right, t));
            }
            right = t;
        }

        Pairing combine(Pairing other) {
            if (!other.empty) {
                this.add(other.left);
                this.middle = downstream.combiner().apply(this.middle, other.middle);
                this.right = other.right;
            }
            return this;
        }

        R finish() {
            return downstream.finisher().apply(middle);
        }
    }

    return Collector.of(Pairing::new, Pairing::add, Pairing::combine, Pairing::finish);
}
People also ask

What is a Java Stream?
A Java stream is a pipeline of functions that can be evaluated. Java streams are not a data structure and cannot mutate data; they can only transform data.
🌐
jrebel.com
jrebel.com › blog › java-streams-in-java-8
Using Java Streams in Java 8 and Beyond | JRebel by Perforce
How to get every nth item from the list using the Stream API?
One of the main reasons for introducing Java threads was to allow concurrent operations. This led to the requirement that Java Streams operations, such as map and filter , do not depend on the position of the element in the stream or the elements around it. This has the advantage of making it easy to separate threads for parallel processing. The disadvantage is the complexity of some operations. · Thus, there is no easy way to do something, for example, to take every nth item or compare each item to the sum of all previous items. · The easiest way to fulfill your requirement is to use a list i
🌐
quick-geek.imtqy.com
quick-geek.imtqy.com › answers › 941843 › index.html
How to get every nth item from the list using the Stream API?
Top answer
1 of 7
29

You can actually use an IntStream to simulate your list's pagination.

List<String> list = Arrays.asList("a","b","c","d","e","f","g","h","i","j");

int pageSize = 3;

IntStream.range(0, (list.size() + pageSize - 1) / pageSize)
        .mapToObj(i -> list.subList(i * pageSize, Math.min(pageSize * (i + 1), list.size())))
        .forEach(System.out::println);

which outputs:

[a, b, c]
[d, e, f]
[g, h, i]
[j]

If you want to generate Strings, you can use String.join since you are dealing with a List<String> directly:

.mapToObj(i -> String.join("", list.subList(i * pageSize, Math.min(pageSize * (i + 1), list.size()))))
2 of 7
12

You can create your own Collector. The easiest way is to call Collector.of().

Since your use case requires values to be processed in order, here is an implementation that simply doesn't support parallel processing.

public static Collector<String, List<List<String>>, List<List<String>>> blockCollector(int blockSize) {
    return Collector.of(
            ArrayList<List<String>>::new,
            (list, value) -> {
                List<String> block = (list.isEmpty() ? null : list.get(list.size() - 1));
                if (block == null || block.size() == blockSize)
                    list.add(block = new ArrayList<>(blockSize));
                block.add(value);
            },
            (r1, r2) -> { throw new UnsupportedOperationException("Parallel processing not supported"); }
    );
}

Test

List<String> input = Arrays.asList("a","b","c","d","e","f","g","h","i","j");
List<List<String>> output = input.stream().collect(blockCollector(3));
output.forEach(System.out::println);

Output

[a, b, c]
[d, e, f]
[g, h, i]
[j]
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › stream › Stream.html
Stream (Java Platform SE 8 )
2 weeks ago - Creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. The resulting stream is ordered if both of the input streams are ordered, and parallel if either of the input streams is parallel.
🌐
ZetCode
zetcode.com › java › stream
Java Stream - processing data in Java with streams
May 23, 2025 - From the path, we create a stream using Files.lines; each element of the stream is a line from the file. ... We iterate through the stream and print each line to the console. Depending on who controls the iteration process, we distinguish between external and internal iteration. External iteration, also known as explicit iteration, is handled by the programmer and was the only type available before Java 8.
🌐
JRebel
jrebel.com › blog › java-streams-in-java-8
Using Java Streams in Java 8 and Beyond | JRebel by Perforce
Map: Transforms the stream elements into something else, it accepts a function to apply to each and every element of the stream and returns a stream of the values the parameter function produced. This is the bread and butter of the Java streaming API.
Find elsewhere
🌐
Baeldung
baeldung.com › home › java › java streams › taking every n-th element from finite and infinite streams in java
Taking Every N-th Element from Finite and Infinite Streams in Java | Baeldung
March 7, 2025 - Java Stream API provides various methods to operate and work with a sequence of elements. However, it’s not easy if we want to process only part of the stream, e.g., every N-th element.
Top answer
1 of 4
1

One of the things to keep in mind is that Stream was primarily designed to be a way of taking advantage of parallel processing. An implication of this is that they have a number of conditions associated with them that are aimed at giving the VM a lot of freedom to process the elements in any convenient order. An example of this is insisting that reduction functions are associative. Another is that local variables manipulated are final. These types of conditions mean the stream items can be evaluated and collected in any order.

A natural consequence of this is that the best use cases for Stream involve no dependencies between the values of the stream. Things such as mapping a stream of integers to their cumulative values are trivial in languages like LISP but a pretty unnatural fit for Java streams (see this question).

There are clever ways of getting around some of these restrictions by using sequential to force the Stream to not be parallel but my experience has been that these are more trouble than they are worth. If your problem involves an essentially sequential series of items in which state is required to process the values then I recommend using traditional collections and iteration. The code will end up being clearer and will perform no worse given the stream cannot be parallelised anyway.

Having said all that, if you really want to do this then the most straightforward way is to have a collector that stores every third item then sends them out as a stream again:

class EveryThird {

    private final List<Integer> list = new ArrayList<>();
    private int count = 0;

    public void accept(Integer i) {
        if (count++ % 3 == 0)
            list.add(i);
    }

    public EveryThird combine(EveryThird other) {
        list.addAll(other.list);
        count += other.count;
        return this;
    }

    public Stream<Integer> stream() {
        return list.stream();
    }
}

This can then be used like:

IntStream.range(0, 10000)
    .collect(EveryThird::new, EveryThird::accept, EveryThird::combine)
    .stream()

But that's not really what collectors are designed for and this is pretty inefficient as it's unnecessarily collecting the stream. As stated above my recommendation is to use traditional iteration for this sort of situation.

2 of 4
1

My StreamEx library enhances standard Stream API. In particular it adds the headTail method which allows recursive definition of custom operations. It takes a function which receives stream head (first element) and tail (stream of the rest elements) and should return the resulting stream which will be used instead of the original one. For example, you can define every3 operation as follows:

public static <T> StreamEx<T> every3(StreamEx<T> input) {
    return input.headTail(
        (first, tail1) -> tail1.<T>headTail(
            (second, tail2) -> tail2.headTail(
                (third, tail3) -> every3(tail3))).prepend(first));
}

Here prepend is also used which just prepends the given element to the stream (this operation is just a best friend of headTail.

In general using headTail you can define almost any intermediate operation you want, including existing ones and new ones. You may find some samples here.

Note that I implemented some mechanism which optimizes tails in such recursive operation definition, so properly defined operation will not eat the whole stack when processing the long stream.

🌐
Baeldung
baeldung.com › home › java › java streams › java stream skip() vs limit()
Java Stream skip() vs limit() | Baeldung
January 8, 2024 - When it gets to skip(), this operation ... it creates and returns a stream with the remaining items. In order to do this, the skip() operation has to keep the state of the elements seen at each moment....
🌐
Stackify
stackify.com › streams-guide-java-8
A Guide to Java Streams: In-Depth Tutorial With Examples
September 4, 2024 - This method can be used with streams of any type (Stream<T>, IntStream, LongStream, or DoubleStream). When applied, it creates a new stream that contains all elements from the first stream followed by all elements from the second stream.
Top answer
1 of 6
25

You can build a custom Collector for this task.

Map<String, String> map = 
    Stream.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
          .collect(MappingErrors.collector());

with:

private static final class MappingErrors {

    private Map<String, String> map = new HashMap<>();

    private String first, second;

    public void accept(String str) {
        first = second;
        second = str;
        if (first != null && first.startsWith("err")) {
            map.put(first, second);
        }
    }

    public MappingErrors combine(MappingErrors other) {
        throw new UnsupportedOperationException("Parallel Stream not supported");
    }

    public Map<String, String> finish() {
        return map;
    }

    public static Collector<String, ?, Map<String, String>> collector() {
        return Collector.of(MappingErrors::new, MappingErrors::accept, MappingErrors::combine, MappingErrors::finish);
    }

}

In this collector, two running elements are kept. Each time a String is accepted, they are updated and if the first starts with "err", the two elements are added to a map.


Another solution is to use the StreamEx library which provides a pairMap method that applies a given function to the every adjacent pair of elements of this stream. In the following code, the operation returns a String array consisting of the first and second element of the pair if the first element starts with "err", null otherwise. null elements are then filtered out and the Stream is collected into a map.

Map<String, String> map = 
    StreamEx.of("a", "b", "err1", "c", "d", "err2", "e", "f", "g", "h", "err3", "i", "j")
            .pairMap((s1, s2) -> s1.startsWith("err") ? new String[] { s1, s2 } : null)
            .nonNull()
            .toMap(a -> a[0], a -> a[1]);

System.out.println(map);
2 of 6
9

You can write a custom collector, or use the much simpler approach of streaming over the list's indexes:

Map<String, String> result = IntStream.range(0, data.size() - 1)
        .filter(i -> data.get(i).startsWith("err"))
        .boxed()
        .collect(toMap(data::get, i -> data.get(i+1)));

This assumes that your data is in a random access friendly list or that you can temporarily dump it into one.

If you cannot randomly access the data or load it into a list or array for processing, you can always make a custom pairing collector so you can write

Map<String, String> result = data.stream()
        .collect(pairing(
                (a, b) -> a.startsWith("err"), 
                AbstractMap.SimpleImmutableEntry::new,
                toMap(Map.Entry::getKey, Map.Entry::getValue)
        ));

Here's the source for the collector. It's parallel-friendly and might come in handy in other situations:

public static <T, V, A, R> Collector<T, ?, R> pairing(BiPredicate<T, T> filter, BiFunction<T, T, V> map, Collector<? super V, A, R> downstream) {

    class Pairing {
        T left, right;
        A middle = downstream.supplier().get();
        boolean empty = true;

        void add(T t) {
            if (empty) {
                left = t;
                empty = false;
            } else if (filter.test(right, t)) {
                downstream.accumulator().accept(middle, map.apply(right, t));
            }
            right = t;
        }

        Pairing combine(Pairing other) {
            if (!other.empty) {
                this.add(other.left);
                this.middle = downstream.combiner().apply(this.middle, other.middle);
                this.right = other.right;
            }
            return this;
        }

        R finish() {
            return downstream.finisher().apply(middle);
        }
    }

    return Collector.of(Pairing::new, Pairing::add, Pairing::combine, Pairing::finish);
}
🌐
Baeldung
baeldung.com › home › java › java streams › the java stream api tutorial
The Java Stream API Tutorial | Baeldung
October 5, 2023 - The first element of the resulting stream is the first parameter of the iterate() method. When creating every following element, the specified function is applied to the previous element. In the example above the second element will be 42.
🌐
How to do in Java
howtodoinjava.com › home › java streams › java stream api: real-world examples for beginners
Java Stream API: Real-world Examples for Beginners
September 19, 2023 - Streams can be defined as a sequence of elements from a source that supports aggregate operations on them. The source here refers to a Collection or Arrays who provides data to a Stream. Stream keeps the order of the data as it is in the source. And aggregate operations or bulk operations are ...
🌐
Imtqy
quick-geek.imtqy.com › answers › 941843 › index.html
How to get every nth item from the list using the Stream API?
February 7, 2019 - List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List<Integer> list2 = list.stream().takenth(3).collect(Collectors.toList()); System.out.println(list2); // => [1, 4, 7, 10] ... One of the main reasons for introducing Java threads ...
🌐
Reflectoring
reflectoring.io › comprehensive-guide-to-java-streams
Comprehensive Guide to Java Streams
May 5, 2022 - Unlike collections, a Java stream is not a data structure instead it takes input from Collections, Arrays, or I/O channels (like files). The operations in a stream use internal iteration for processing the elements of a stream.
🌐
Pluralsight
pluralsight.com › tech insights & how-to guides › tech guides & tutorials
Java 8 Stream API: Part 2 | Pluralsight
December 15, 2018 - An internal variable that accumulates ... are passed as arguments to the BinaryOperator represented by the lambda expression (sum, n) -> sum + x....
🌐
Solved
code.i-harness.com › en › q › 1e236f9
two - when to use java streams - Solved
November 27, 2018 - class EveryNth<C> { private final int nth; private final List<List<C>> lists = new ArrayList<>(); private int next = 0; private EveryNth(int nth) { this.nth = nth; IntStream.range(0, nth).forEach(i -> lists.add(new ArrayList<>())); } private void accept(C item) { lists.get(next++ % nth).add(item); } private EveryNth<C> combine(EveryNth<C> other) { other.lists.forEach(l -> lists.get(next++ % nth).addAll(l)); next += other.next; return this; } private List<C> getResult() { return lists.get(0); } public static Collector<Integer, ?, List<Integer>> collector(int nth) { return Collector.of(() -> new EveryNth(nth), EveryNth::accept, EveryNth::combine, EveryNth::getResult)); } ... List<String> list = Arrays.asList("Anne", "Bill", "Chris", "Dean", "Eve", "Fred", "George"); list.stream().parallel().collect(EveryNth.collector(3)).forEach(System.out::println);