© 2025 Piotr Przybył. Licensed under CC BY-NC-SA 4.0
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.
Picture by Pete Linforth from Pixabay
filtermapmapToIntmapToLongmapToDoubleflatMapflatMapToIntflatMapToLongflatMapToDoubledistinctsortedsorted(Comparator c)peeklimitskipboxedsequentialparallelunordered...forEachforEachOrderedtoArrayreducecollectminmaxcountanyMatchallMatchnoneMatchfindFirstfindAny...In Java 9:
takeWhiledropWhileIn Java 16:
mapMultimapMultiToIntmapMultiToLongmapMultiToDoubleIn Java 16:
toListIn Java 9:
Collectors.filteringCollectors.flatMappingIn Java 12:
Collectors.teeingIn ecosystem:
And in your own Utils!
Or better said: the problems
static Stream<String> eneStream() {
return Stream.of("""
Ene due rike fake \
torba borba ósme smake \
eus deus kosmateus \
i morele baks"""
.split(" "));
}
Set<String> uniqueLengthWords = eneStream()
.collect(
Collectors.collectingAndThen(
Collectors.toMap(
String::length,
word -> word,
(existing, _) -> existing
),
map -> new HashSet<>(map.values())
)
);
Collector<String, HashMap<Integer, String>, Set<String>> uniqueLengthCollector() {
return Collector.of(
HashMap::new,
(map, word) -> map.putIfAbsent(word.length(), word),
(map1, map2) -> {
map2.forEach(map1::putIfAbsent);
return map1;
},
map -> new HashSet<>(map.values())
);
}
private static void withCollectorAndUp() {
Set<String> uniqueLengthWords = eneStream()
.collect(Collectors.collectingAndThen(
uniqueLengthCollector(), // Apply the provided collector
set -> set.stream()
.map(String::toUpperCase)
.collect(Collectors.toSet()) // Convert collected words to uppercase
));
}
map() all elements...Stream.distinct...Stream.distinctBy(...)"!distinctBy(...)!
Stream.of(...)
.gather(a)
[.gather(b)]
[.gather(c)]
.collect(...);
Stream.of(...).gather(a)[.andThen(b).andThen(c)].collect(...);
Set<String> uniqueLengthWords = eneStream()
.gather(new UniqueByLengthGatherer())
.map(String::toUpperCase)
.collect(Collectors.toUnmodifiableSet());
class UniqueByLengthGatherer implements Gatherer<String, Set<Integer>, String> {
public Supplier<Set<Integer>> initializer() {
return HashSet::new;
}
public Integrator<Set<Integer>, String, String> integrator() {
return (state, element, downstream) -> {
if (!state.contains(element.length())) {
state.add(element.length());
downstream.push(element);
}
return true;
};
}
}
Set<String> uniqueLengthWords = eneStream()
.gather(Gatherer.ofSequential(HashSet::new, integrator))
.map(String::toUpperCase)
.collect(Collectors.toUnmodifiableSet());
Gatherer.Integrator<Set<Integer>, String, String> integrator =
(state, element, downstream) -> {
if (!state.contains(element.length())) {
state.add(element.length());
downstream.push(element);
}
return true;
};
public interface Gatherer<T, A, R> {
default Supplier<A> initializer() { }
Integrator<A, T, R> integrator();
default BinaryOperator<A> combiner() { }
default BiConsumer<A, Downstream<? super R>> finisher() { }
}
public interface Gatherer<IN, STATE, OUT> {
default Supplier<STATE> initializer() { }
Integrator<STATE, IN, OUT> integrator();
default BinaryOperator<STATE> combiner() { }
default BiConsumer<STATE, Downstream<? super OUT>> finisher() { }
}
Gatherer.ofSequential(integrator);
Gatherer.ofSequential(integrator, finisher);
Gatherer.ofSequential(initializer, integrator);
Gatherer.ofSequential(initializer, integrator, finisher);
Gatherer.of(integrator);
Gatherer.of(integrator, finisher);
Gatherer.of(initializer, integrator, combiner, finisher);
boolean integrate(STATE currState, IN nextElem, Downstream<? super OUT> downstream);
interface Greedy extends Integrator
combines two parallelizable gatherers
can still operate on downstream
can push things down the stream
Gatherers.fold(initial, BiFunction folder);
Gatherers.scan(initial, BiFunction scanner);
Gatherers.windowFixed(size);
Gatherers.windowSliding(size);
Gatherers.mapConcurrent(maxConcurrency, Function mapper);
private void folding() {
var list = IntStream.range(0, 10)
.mapToObj(i -> "" + i)
.gather(Gatherers.fold(() -> "", (string, number) -> string + number))
.toList();
System.out.println(list);
}
// [0123456789]
private void scanning() {
var list = IntStream.range(0, 10)
.mapToObj(i -> "" + i)
.gather(Gatherers.scan(() -> "", (string, number) -> string + number))
.toList();
System.out.println(list);
}
// [0, 01, 012, 0123, 01234, 012345, 0123456, 01234567, 012345678, 0123456789]
private void fixed() {
var list = IntStream.range(0, 10)
.mapToObj(i -> "" + i)
.gather(Gatherers.windowFixed(3))
.toList();
System.out.println(list);
}
// [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
private void sliding() {
var list = IntStream.range(0, 10)
.mapToObj(i -> "" + i)
.gather(Gatherers.windowSliding(3))
.toList();
System.out.println(list);
}
// [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7],
// [6, 7, 8], [7, 8, 9]]
IntStream.range(0, 100).parallel().map(id -> enriched(id)).toList();
IntStream.range(100, 200).parallel().map(id -> enriched(id)).toList();
Gatherers.mapConcurrent(maxConcurrency, Function mapper);
softwaregarden.dev/en/posts/new-java/gatherers/concurrent/
IntStream.range(0, 200)
.gather(Gatherers.mapConcurrent(100, id -> enriched(id))
.toList()