sorted, distinct, limit, skip in Java
Stateful intermediate operations.
Stream.iterate(1, n -> n + 1).sorted().limit(3) — you'd expect [1, 2, 3]. Instead your program hangs forever. Why can't limit save it?Operations with a memory
filter and map judge each element alone. **distinct and sorted are stateful**: they must remember what they've seen. sorted is like a teacher who can't announce the top student until every exam is handed in — it must see all elements before emitting the first.
distinct and sorted
**distinct() keeps the first occurrence of each element, judging duplicates by equals/hashCode. sorted() uses natural order; sorted(comparator)** uses your rule.
Stream.of("pear", "fig", "apple")
.sorted(Comparator.comparing(
String::length))
.toList(); // [fig, pear, apple]distinct alone
What does this print?
List<Integer> r = Stream.of(4, 1, 4, 2, 1)
.distinct()
.toList();
System.out.println(r);[1, 2, 4][4, 1, 2][4, 1, 4, 2, 1]
Show the answer
[4, 1, 2] — distinct drops repeats but keeps encounter order. It does not sort; chain .sorted() for [1, 2, 4].
skip and limit: paging
**skip(n) drops the first n elements; limit(n) keeps at most** n. Together they fetch one page: page p of size s is skip(p * s).limit(s).
Page three
What does this print?
var r = Stream.iterate(1, n -> n + 1)
.skip(10)
.limit(3)
.toList();
System.out.println(r);[10, 11, 12][11, 12, 13][1, 2, 3]
Show the answer
[11, 12, 13] — skip throws away 1 to 10, then limit keeps the next three. limit also makes the infinite source safe.
sorted on an infinite stream
sorted waits for every element before emitting anything. The source never ends, so limit(3) never receives a single element — the program runs forever (or runs out of memory). Put **limit before sorted**.
Stream.iterate(1, n -> n + 1)
.sorted() // waits forever
.limit(3)
.toList();Paging and leaderboards
skip/limit power "page 3 of results" in small in-memory lists, and sorted(...).limit(10) builds top-10 leaderboards. On big datasets teams push sorting and paging into the database instead, because a stream must load everything to sort it.
Key takeaways
- sorted() uses natural order; sorted(cmp) uses a Comparator
- distinct() removes duplicates using equals/hashCode
- skip(n) drops the first n; limit(n) keeps at most n
- sorted on an infinite stream never finishes
sorted() on objects that aren't Comparable compiles fine but throws a ClassCastException at runtime when the terminal op runs.
Practice questions
What does this print?
System.out.println(
Stream.of(5, 3, 5, 1, 3)
.distinct()
.sorted()
.toList());- [5, 3, 1]
- [1, 3, 5]
- [1, 3, 3, 5, 5]
- [5, 5, 3, 3, 1]
Check your answer
[1, 3, 5]. distinct removes the repeated 5 and 3, leaving 5, 3, 1; sorted then puts them in natural ascending order.
What does this print?
List<Integer> page = IntStream.rangeClosed(1, 10)
.boxed()
.skip(3)
.limit(4)
.toList();
System.out.println(page);- [3, 4, 5, 6]
- [4, 5, 6, 7, 8]
- [4, 5, 6, 7]
- [1, 2, 3, 4]
Check your answer
[4, 5, 6, 7]. skip(3) drops 1, 2 and 3; limit(4) then keeps the next four: 4 to 7. skip + limit is the classic way to fetch one page of results.