Esempi di collector e custom collector

This commit is contained in:
Fabio Scotto di Santolo
2019-11-23 15:13:54 +01:00
parent 60630cd7f1
commit ab75cb8f6b
3 changed files with 176 additions and 15 deletions

View File

@@ -0,0 +1,56 @@
package org.gym.fp.moderjava;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH;
public class PrimeNumbersCollector implements Collector<Integer, Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> {
@Override
public Supplier<Map<Boolean, List<Integer>>> supplier() {
return () -> new HashMap<>() {{
put(true, new ArrayList<>());
put(false, new ArrayList<>());
}};
}
@Override
public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
return (Map<Boolean, List<Integer>> acc, Integer candidate) -> {
acc.get(isPrime(acc.get(true), candidate))
.add(candidate);
};
}
@Override
public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
return (Map<Boolean, List<Integer>> map1, Map<Boolean, List<Integer>> map2) -> {
map1.get(true).addAll(map2.get(true));
map1.get(false).addAll(map2.get(false));
return map1;
};
}
@Override
public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
private boolean isPrime(List<Integer> primes, int candidate) {
int candidateRoot = (int) Math.sqrt(candidate);
return primes.stream()
.takeWhile(i -> i <= candidateRoot)
.noneMatch(i -> candidate % i == 0);
}
}

View File

@@ -278,6 +278,43 @@ public class StreamTest {
}))); })));
out.println(dishesByTypeCaloricLevel); out.println(dishesByTypeCaloricLevel);
out.println("----------------------------------------"); out.println("----------------------------------------");
Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
out.println(partitionedMenu);
out.println("----------------------------------------");
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
out.println(vegetarianDishesByType);
out.println("----------------------------------------");
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream()
.collect(partitioningBy(Dish::isVegetarian,
collectingAndThen(maxBy(comparingInt(Dish::getCalories)), Optional::get)));
out.println(mostCaloricPartitionedByVegetarian);
out.println("----------------------------------------");
out.println(partitionPrimes(100));
out.println("----------------------------------------");
List<Dish> dishes = menu.stream()
.filter(Dish::isVegetarian)
.collect(new ToListCollector<>());
out.println(dishes);
out.println("----------------------------------------");
out.println(partitionPrimesWithCustomCollector(100));
out.println("----------------------------------------");
}
public static Map<Boolean, List<Integer>> partitionPrimes(int n) {
return IntStream.rangeClosed(2, n).boxed()
.collect(partitioningBy(candidate -> isPrime(candidate)));
}
public static boolean isPrime(int candidate) {
return IntStream.rangeClosed(2, candidate - 1)
.limit((long) Math.floor(Math.sqrt(candidate)) - 1)
.noneMatch(i -> candidate % i == 0);
}
static Map<Boolean, List<Integer>> partitionPrimesWithCustomCollector(int n) {
return IntStream.rangeClosed(2, n).boxed().collect(new PrimeNumbersCollector());
} }
enum CaloricLevel {DIET, NORMAL, FAT} enum CaloricLevel {DIET, NORMAL, FAT}

View File

@@ -0,0 +1,68 @@
package org.gym.fp.moderjava;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.CONCURRENT;
import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH;
// Collector<T, A, R> =>
// T = tipo generico degli elementi nello stream
// A = tipo dell'accumulatore, risultato parziale della riduzione
// R = tipo dell'oggetto risultato dall'operazione
public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
/**
* @return l'accumulatore vuoto
*/
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
/**
* @return la funzione che accumula gli elementi T in A
*/
@Override
public BiConsumer<List<T>, T> accumulator() {
return List::add;
}
/**
* Questo metodo è invocato per combinare gli accumulatori
* elaborati da diversi sotto-stream
*
* @return la combinazione degli accumulatori
*/
@Override
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
/**
* Questo metodo viene invocato al termine del processo
* di accumulazione degli elementi dello {@link java.util.stream.Stream}
*
* @return la funzione che trasforma A in R
*/
@Override
public Function<List<T>, List<T>> finisher() {
return Function.identity();
}
/**
* @return insieme dei comportamenti che il collector deve assumere
*/
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
}
}