Java Stream API Extension

more-
gatherers

Missing Stream API functionality you always longed for—provided via Gatherers.

17 Gatherers,
zero dependencies

Every method returns a Gatherer you can plug directly into .gather(). Grouped by functionality.

↓
Last & Sampling 3 methods
last()
Takes the last element from the stream.
last(int n)
Takes the last n elements from the stream.
sampling(int n)
Takes every n-th element from the stream.
⇔
Zip 10 methods
zip(Stream<T2>)
Zips Stream elements with another Stream. Returns Map.Entry<T1, T2>.
zip(Stream<T2>, BiFunction<T1, T2, R>)
Zips with another Stream using a custom zipper function.
zip(Iterator<T2>)
Zips Stream elements with an Iterator. Returns Map.Entry<T1, T2>.
zip(Iterator<T2>, BiFunction<T1, T2, R>)
Zips with an Iterator using a custom zipper function.
zipWithIterable(Iterable<T2>)
Zips Stream elements with an Iterable. Returns Map.Entry<T1, T2>.
zipWithIterable(Iterable<T2>, BiFunction<T1, T2, R>)
Zips with an Iterable using a custom zipper function.
zipWithIndex()
Zips each element with its index. Returns Map.Entry<T, Long>.
zipWithIndex(BiFunction<Long, T, R>)
Zips each element with its index using a custom mapper.
≠
Distinct 4 methods
distinctBy(Function<T, R>)
Distinct elements by key extractor, keeping the first occurrence.
distinctByKeepLast(Function<T, R>)
Distinct elements by key extractor, keeping the last occurrence.
distinctUntilChanged()
Removes consecutive duplicate elements.
distinctUntilChanged(Function<T, R>)
Removes consecutive duplicates by key extractor.
▨
Window, Filter & Group 4 methods
windowSliding(int windowSize, int step)
Sliding window with custom step. Extends Gatherers.windowSliding(int).
filteringByIndex(BiPredicate<Long, T>)
Filters elements based on their index and value.
groupingBy(Function<T, K>, Collector<T, ?, R>)
Groups elements by key with a custom collector.
groupingBy(Function<T, K>)
Groups elements by key, collecting into lists.

See it in action

Drop a Gatherer into any .gather() call. That's it.

Get the last N elements last
Stream.of(1, 2, 3, 4, 5)
  .gather(MoreGatherers.last(3))
  .toList();  // [3, 4, 5]
Zip elements with their index zip
Stream.of("a", "b", "c")
  .gather(MoreGatherers.zipWithIndex())
  .toList();  // [a=0, b=1, c=2]
Remove consecutive duplicates distinct
Stream.of(1, 1, 2, 2, 3, 1)
  .gather(MoreGatherers.distinctUntilChanged())
  .toList();  // [1, 2, 3, 1]
Sample every N-th element sampling
Stream.of(1, 2, 3, 4, 5)
  .gather(MoreGatherers.sampling(2))
  .toList();  // [1, 3, 5]
Sliding window with step window
Stream.of(1, 2, 3, 4, 5)
  .gather(MoreGatherers.windowSliding(3, 2))
  .toList();  // [[1, 2, 3], [3, 4, 5]]
Zip two streams together zip
Stream.of("a", "b", "c")
  .gather(MoreGatherers.zip(Stream.of(1, 2, 3)))
  .toList();  // [a=1, b=2, c=3]
Group elements by key group
Stream.of(1, 2, 3, 4, 5, 6)
  .gather(MoreGatherers.groupingBy(i -> i % 2 == 0 ? "even" : "odd"))
  .toList();  // [odd=[1, 3, 5], even=[2, 4, 6]]
Filter by index filter
Stream.of("a", "b", "c", "d", "e")
  .gather(MoreGatherers.filteringByIndex((idx, val) -> idx % 2 == 0))
  .toList();  // ["a", "c", "e"]

Add to your project

Requires Java 24+. Available on Maven Central.

<dependency>
    <groupId>com.pivovarit</groupId>
    <artifactId>more-gatherers</artifactId>
    <version>0.2.0</version>
</dependency>
implementation 'com.pivovarit:more-gatherers:0.2.0'
implementation("com.pivovarit:more-gatherers:0.2.0")

Built to complement,
not compete

What guides the design of this library.

•
Zero Dependencies
Implemented purely with core Java libraries. Nothing extra on your classpath.
•
Only What's Missing
Provides only Gatherers that can't be easily achieved with the standard Stream API. No duplication.
•
Familiar Conventions
Follows standard Stream API patterns and Project Reactor naming conventions.
•
Production Ready
Thoroughly tested with mutation testing. Every release is verified with PIT.