skip and limit paginate, but only over a deterministic order - sort before you page or the same page number returns different rows on the next run. The sort must come first, the skip second.
record Author(int id, String name) {}
record Book(String title, int authorId) {}
var authors = List.of(new Author(1, "Ava"), new Author(2, "Ben"));
var books = List.of(new Book("Streams", 1), new Book("Kafka", 2), new Book("SQL", 1),
new Book("JVM", 2), new Book("HTTP", 1));
Map<Integer, String> nameOf = authors.stream().collect(Collectors.toMap(Author::id, Author::name));
int page = 1, size = 2; // zero-based
var rows = books.stream()
.map(b -> nameOf.get(b.authorId()) + " - " + b.title())
.sorted()
.skip((long) page * size)
.limit(size)
.toList();
System.out.println("page " + page + " (size " + size + "): " + rows);
page 1 (size 2): [Ava - Streams, Ben - JVM]
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-20