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

@@ -128,7 +128,7 @@ public class StreamTest {
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
);
out.println("1)");
transactions.stream()
.filter(t -> t.getYear() == 2011)
@@ -183,7 +183,7 @@ public class StreamTest {
.flatMap(a -> IntStream.rangeClosed(a, 100)
.mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)})
.filter(t -> t[2] % 1 == 0)
)
)
.limit(5)
.forEach(triple -> out.printf("(%.0f, %.0f, %.0f)\n", triple[0], triple[1], triple[2]));
out.println("----------------------------------------");
@@ -231,14 +231,14 @@ public class StreamTest {
Map<Dish.Type, List<Dish>> caloricDishesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
filtering(dish -> dish.getCalories() > 500,
toList())));
filtering(dish -> dish.getCalories() > 500,
toList())));
out.println(caloricDishesByType);
out.println("----------------------------------------");
Map<Dish.Type, List<String>> dishNamesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
mapping(Dish::getName, toList())));
mapping(Dish::getName, toList())));
out.println(dishNamesByType);
out.println("----------------------------------------");
Map<String, List<String>> dishTags = Map.of(
@@ -251,18 +251,18 @@ public class StreamTest {
"pizza", asList("tasty", "salty"),
"prawns", asList("tasty", "roasted"),
"salmon", asList("delicious", "fresh")
);
);
Map<Dish.Type, Set<String>> dishNamesByType2 =
menu.stream()
.collect(groupingBy(Dish::getType,
flatMapping(dish -> {
// FIXME: The example of book is not safety
List<String> tags = dishTags.get(dish.getName());
if (tags != null) {
return tags.stream();
}
return Stream.empty();
}, toSet())));
flatMapping(dish -> {
// FIXME: The example of book is not safety
List<String> tags = dishTags.get(dish.getName());
if (tags != null) {
return tags.stream();
}
return Stream.empty();
}, toSet())));
out.println(dishNamesByType2);
out.println("----------------------------------------");
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
@@ -278,6 +278,43 @@ public class StreamTest {
})));
out.println(dishesByTypeCaloricLevel);
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}
@@ -291,7 +328,7 @@ public class StreamTest {
new Dish("tomato", 30, Dish.Type.VEGETARIAN),
new Dish("tunny", 120, Dish.Type.FISH),
new Dish("potato", 70, Dish.Type.VEGETARIAN)
);
);
return Collections.unmodifiableList(dishes);
}

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));
}
}