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.