Skip to content

Commit 34788c9

Browse files
feat: added more stream gatherer examples
1 parent c17069a commit 34788c9

File tree

10 files changed

+299
-0
lines changed

10 files changed

+299
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.util.Set;
4+
import java.util.HashSet;
5+
import java.util.function.Function;
6+
import java.util.function.Supplier;
7+
import java.util.stream.Gatherer;
8+
9+
public class DistinctByGatherer<T, P> implements Gatherer<T, Set<P>, T> {
10+
11+
private final Function<T, P> selector;
12+
13+
public DistinctByGatherer(Function<T, P> selector) {
14+
this.selector = selector;
15+
}
16+
17+
@Override
18+
public Supplier<Set<P>> initializer() {
19+
return HashSet::new;
20+
}
21+
22+
@Override
23+
public Integrator<Set<P>, T, T> integrator() {
24+
return Integrator.ofGreedy((state, item, downstream) -> {
25+
P extracted = selector.apply(item);
26+
if (!state.contains(extracted)) {
27+
state.add(extracted);
28+
downstream.push(item);
29+
}
30+
31+
return true;
32+
});
33+
}
34+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.util.function.Predicate;
4+
import java.util.stream.Gatherer;
5+
6+
public class FindFirstGatherer<T> implements Gatherer<T, T, T> {
7+
private final Predicate<T> predicate;
8+
9+
public FindFirstGatherer(Predicate<T> predicate) {
10+
this.predicate = predicate;
11+
}
12+
13+
@Override
14+
public Integrator<T, T, T> integrator() {
15+
return Integrator.ofGreedy((_, item, downstream) -> {
16+
if (predicate.test(item)) {
17+
downstream.push(item);
18+
return false;
19+
} else {
20+
return true;
21+
}
22+
});
23+
}
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.util.function.BiFunction;
4+
import java.util.function.Function;
5+
import java.util.function.Predicate;
6+
7+
public class GathererFactory {
8+
9+
private GathererFactory() {
10+
}
11+
12+
public static <T, P> DistinctByGatherer<T, P> distinctBy(Function<T, P> extractor) {
13+
return new DistinctByGatherer<>(extractor);
14+
}
15+
16+
public static <T, P> ReduceByGatherer<T, P> reduceBy(Function<T, P> extractor, BiFunction<T, T, T> reducer) {
17+
return new ReduceByGatherer<>(extractor, reducer);
18+
}
19+
20+
public static <T, B extends Comparable<B>> MaxByGatherer<T, B> maxBy(Function<T, B> extractor) {
21+
return new MaxByGatherer<>(extractor);
22+
}
23+
24+
public static <T> FindFirstGatherer<T> findFirst(Predicate<T> predicate) {
25+
return new FindFirstGatherer<>(predicate);
26+
}
27+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.util.function.BiConsumer;
4+
import java.util.function.BinaryOperator;
5+
import java.util.function.Function;
6+
import java.util.function.Supplier;
7+
import java.util.stream.Gatherer;
8+
9+
class MaxByGatherer<T, B extends Comparable<B>> implements Gatherer<T, State<T>, T> {
10+
private final Function<T, B> extractor;
11+
12+
MaxByGatherer(Function<T, B> extractor) {
13+
this.extractor = extractor;
14+
}
15+
16+
@Override
17+
public Supplier<State<T>> initializer() {
18+
return State::new;
19+
}
20+
21+
@Override
22+
public Integrator<State<T>, T, T> integrator() {
23+
return Integrator.ofGreedy((state, item, _) -> {
24+
if (state.maxElement == null) {
25+
state.maxElement = item;
26+
return true;
27+
}
28+
29+
B currentItemValue = extractor.apply(item);
30+
B maxItemValue = extractor.apply(state.maxElement);
31+
32+
if (currentItemValue.compareTo(maxItemValue) > 0) {
33+
state.maxElement = item;
34+
}
35+
36+
return true;
37+
});
38+
}
39+
40+
@Override
41+
public BinaryOperator<State<T>> combiner() {
42+
43+
return (first, second) -> {
44+
if (first.maxElement == null && second.maxElement == null) {
45+
return null;
46+
} else if (first.maxElement == null) {
47+
return second;
48+
} else if (second.maxElement == null) {
49+
return first;
50+
}
51+
52+
B firstMaxValue = extractor.apply(first.maxElement);
53+
B secondMaxValue = extractor.apply(second.maxElement);
54+
55+
if (firstMaxValue.compareTo(secondMaxValue) > 0) {
56+
return first;
57+
} else {
58+
return second;
59+
}
60+
};
61+
}
62+
63+
@Override
64+
public BiConsumer<State<T>, Downstream<? super T>> finisher() {
65+
return (state, downstream) -> downstream.push(state.maxElement);
66+
}
67+
}
68+
69+
class State<T> {
70+
T maxElement;
71+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.math.BigDecimal;
4+
import java.util.Currency;
5+
6+
public record Money(BigDecimal amount, Currency currency) {
7+
public Money add(Money money) {
8+
return new Money(money.amount.add(this.amount), currency);
9+
}
10+
11+
public Money multiply(BigDecimal multiplier) {
12+
return new Money(amount.multiply(multiplier), currency);
13+
}
14+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import java.util.Map;
4+
import java.util.HashMap;
5+
import java.util.function.BiConsumer;
6+
import java.util.function.BiFunction;
7+
import java.util.function.Function;
8+
import java.util.function.Supplier;
9+
import java.util.stream.Gatherer;
10+
11+
public class ReduceByGatherer<T, P> implements Gatherer<T, Map<P, T>, T> {
12+
private final Function<T, P> selector;
13+
private final BiFunction<T, T, T> operation;
14+
15+
public ReduceByGatherer(Function<T, P> extractor, BiFunction<T, T, T> reducer) {
16+
this.selector = extractor;
17+
this.operation = reducer;
18+
}
19+
20+
@Override
21+
public Supplier<Map<P, T>> initializer() {
22+
return HashMap::new;
23+
}
24+
25+
@Override
26+
public Integrator<Map<P, T>, T, T> integrator() {
27+
return Integrator.ofGreedy((state, item, _) -> {
28+
state.merge(selector.apply(item), item, operation);
29+
return true;
30+
});
31+
}
32+
33+
@Override
34+
public BiConsumer<Map<P, T>, Downstream<? super T>> finisher() {
35+
return (state, downstream) -> state.values().forEach(downstream::push);
36+
}
37+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class DistinctByGathererTest {
11+
12+
@Test
13+
void testDistinctBy() {
14+
var result = List.of("a", "b", "c", "a", "b", "c")
15+
.stream()
16+
.gather(GathererFactory.distinctBy(String::toUpperCase))
17+
.collect(Collectors.toList());
18+
assertEquals(List.of("a", "b", "c"), result);
19+
}
20+
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class FindFirstGathererTest {
11+
12+
@Test
13+
void testFindFirst() {
14+
var result = List.of("a", "c", "d", "a", "b", "c")
15+
.stream()
16+
.gather(GathererFactory.findFirst(e-> e.equals("b")))
17+
.collect(Collectors.toList());
18+
assertEquals(List.of("b"), result);
19+
}
20+
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.math.BigDecimal;
6+
import java.util.Currency;
7+
import java.util.List;
8+
import java.util.stream.Collectors;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class MaxByGathererTest {
13+
14+
private static final Currency EUR = Currency.getInstance("EUR");
15+
private static final Currency USD = Currency.getInstance("USD");
16+
17+
@Test
18+
void testMaxGatherer() {
19+
var result = List.of(
20+
new Money(BigDecimal.valueOf(12), EUR),
21+
new Money(BigDecimal.valueOf(10), USD)
22+
)
23+
.stream()
24+
.parallel()
25+
.gather(GathererFactory.maxBy(Money::amount))
26+
.collect(Collectors.toList());
27+
assertEquals(List.of(new Money(BigDecimal.valueOf(12), EUR)), result);
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package de.claudioaltamura.java23.streamgatherer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class ReduceByGathererTest {
11+
12+
@Test
13+
void testReduceBy() {
14+
var result = List.of("a", "b", "C", "A", "b", "C")
15+
.stream()
16+
.gather(GathererFactory.reduceBy(String::toLowerCase, String::concat))
17+
.collect(Collectors.toList());
18+
assertEquals(List.of("aA", "bb", "CC"), result);
19+
}
20+
21+
}

0 commit comments

Comments
 (0)