Merge branch 'develop'

This commit is contained in:
Fabio Scotto di Santolo
2019-12-14 16:52:08 +01:00
33 changed files with 1488 additions and 0 deletions

4
.idea/compiler.xml generated
View File

@@ -6,11 +6,15 @@
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="modernjava" />
<module name="scala" />
<module name="modern-java" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="fpgym" target="8" />
<module name="modern-java" target="9" />
<module name="modernjava" target="9" />
<module name="scala" target="1.5" />
</bytecodeTargetLevel>
</component>

6
.idea/junitgenerator-prj-settings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JUnitGeneratorProjectSettings">
<option name="selectedTemplateKey" value="JUnit 4" />
</component>
</project>

45
modern-java/pom.xml Normal file
View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>modern-java</artifactId>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>
<parent>
<artifactId>fpgym</artifactId>
<groupId>org.gym.fp</groupId>
<version>1.0</version>
</parent>
<properties>
<maven.compiler.source>9</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,60 @@
package org.gym.fp.moderjava;
import org.gym.fp.moderjava.concurrent.Shop;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.*;
import static java.lang.System.out;
public class ConcurrencyTest {
public static void main(String[] args) throws Exception {
doExecutorsServiceTest();
doFutureTest();
}
private static void doExecutorsServiceTest() throws Exception {
int x = 1337;
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<Integer> y = executorService.submit(() -> f(x));
Future<Integer> z = executorService.submit(() -> g(x));
out.println("Result y + z = " + (y.get() + z.get()));
executorService.shutdown();
}
private static void doFutureTest() throws Exception {
Collection<Shop> shops = getShops();
Future<Integer> future = CompletableFuture.supplyAsync(() -> {
out.println("Primo completable future");
return 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> {
out.println("Secondo completable future");
return 30;
}), (first, second) -> first + second);
out.println(future.get(1, TimeUnit.NANOSECONDS));
}
static int f(int x) {
return 42;
}
static int g(int x) {
return x;
}
static Collection<Shop> getShops() {
return Arrays.asList(
new Shop("BestShop1"),
new Shop("BestShop2"),
new Shop("BestShop3"),
new Shop("BestShop4"),
new Shop("BestShop5"),
new Shop("BestShop6"),
new Shop("BestShop7"),
new Shop("BestShop8")
);
}
}

View File

@@ -0,0 +1,141 @@
package org.gym.fp.moderjava;
import org.gym.fp.moderjava.dsl.*;
import static java.lang.System.out;
import static org.gym.fp.moderjava.dsl.LambdaOrderBuilder.order;
import static org.gym.fp.moderjava.dsl.MethodChainingOrderBuilder.forCustomer;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.at;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.buy;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.on;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.order;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.sell;
import static org.gym.fp.moderjava.dsl.NestedFunctionOrderBuilder.stock;
public class DslTest {
public static void main(String[] args) {
doUnacceptableDslTest();
doMethodChainingPatternTest();
doNestedFunctionsPatternTest();
doLambdaSequencingPatternTest();
doMixedPatternTest();
doUsingMethodReferenceDslTest();
}
private static void doUnacceptableDslTest() {
Order order = new Order();
order.setCustomer("BigBank");
Trade trade1 = new Trade();
trade1.setType(Trade.Type.BUY);
Stock stock1 = new Stock();
stock1.setSymbol("IBM");
stock1.setMarket("NYSE");
trade1.setStock(stock1);
trade1.setPrice(125.00);
trade1.setQuantity(80);
order.addTrade(trade1);
Trade trade2 = new Trade();
trade2.setType(Trade.Type.BUY);
Stock stock2 = new Stock();
stock2.setSymbol("GOOGLE");
stock2.setMarket("NASDAQ");
trade2.setStock(stock2);
trade2.setPrice(375.00);
trade2.setQuantity(50);
order.addTrade(trade2);
}
private static void doMethodChainingPatternTest() {
Order order = forCustomer("BigBank")
.buy(80)
.stock("IBM")
.on("NYSE")
.at(125.00)
.sell(50)
.stock("Google")
.on("NASDAQ")
.at(375.00)
.end();
out.println("Order: " + order.toString());
out.println("----------------------------------------");
}
private static void doNestedFunctionsPatternTest() {
Order order = order("BigBank",
buy(80,
stock("IBM", on("NYSE")),
at(125.00)),
sell(50,
stock("Google", on("NASDAQ")),
at(375.00))
);
out.println("Order: " + order.toString());
out.println("----------------------------------------");
}
private static void doLambdaSequencingPatternTest() {
Order order = order(o -> {
o.forCustomer("BigBank");
o.buy(t -> {
t.quantity(80);
t.price(125.00);
t.stock(s -> {
s.symbol("IBM");
s.market("NYSE");
});
});
o.sell(t -> {
t.quantity(50);
t.price(375.00);
t.stock(s -> {
s.symbol("GOOGLE");
s.market("NASDAQ");
});
});
});
out.println("Order: " + order.toString());
out.println("----------------------------------------");
}
private static void doMixedPatternTest() {
Order order = MixedBuilder.forCustomer("BigBank",
MixedBuilder.buy(t -> t.quantity(80)
.stock("IBM")
.on("NYSE")
.at(125.00)),
MixedBuilder.sell(t -> t.quantity(50)
.stock("Google")
.on("NASDAQ")
.at(375.00))
);
out.println("Order: " + order.toString());
out.println("----------------------------------------");
}
private static void doUsingMethodReferenceDslTest() {
Order order = MixedBuilder.forCustomer("BigBank",
MixedBuilder.buy(t -> t.quantity(80)
.stock("IBM")
.on("NYSE")
.at(125.00)),
MixedBuilder.sell(t -> t.quantity(50)
.stock("Google")
.on("NASDAQ")
.at(375.00))
);
double value = new TaxCalculator()
.with(Tax::regional)
.with(Tax::surcharge)
.calculate(order);
out.printf("Calculate value order with taxes: %.2f\n", value);
out.println("----------------------------------------");
}
}

View File

@@ -0,0 +1,48 @@
package org.gym.fp.moderjava;
import org.gym.fp.moderjava.optional.Car;
import org.gym.fp.moderjava.optional.Insurance;
import org.gym.fp.moderjava.optional.Person;
import java.util.Optional;
import static java.lang.System.out;
public class OptionalTest {
public static void main(String[] args) {
Insurance myInsurance1 = new Insurance("Allianz");
Car myCar1 = new Car(Optional.of(myInsurance1));
Person person1 = new Person(Optional.of(myCar1));
out.println("My insurance's name is " + getCarInsuranceName(Optional.ofNullable(person1)));
out.println("----------------------------------------");
Car myCar2 = new Car(Optional.ofNullable(null));
Person person2 = new Person(Optional.ofNullable(myCar2));
out.println("My insurance's name is " + getCarInsuranceName(Optional.ofNullable(person2)));
out.println("----------------------------------------");
out.println("----------------------------------------");
}
static String getCarInsuranceName(Optional<Person> maybePerson) {
return maybePerson
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}
static Insurance findCheapestInsurance(Person person, Car car) {
Insurance cheapestInsurance = new Insurance("Allianz");
return cheapestInsurance;
}
static Optional<Insurance> nullSafeFindCheapestInsurance(Optional<Person> person, Optional<Car> car) {
return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
// if (person.isPresent() && car.isPresent()) {
// return Optional.of(findCheapestInsurance(person.get(), car.get()));
// }
// return Optional.empty();
}
}

View File

@@ -0,0 +1,371 @@
package org.gym.fp.moderjava;
import org.gym.fp.moderjava.stream.*;
import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.lang.System.out;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.*;
public class StreamTest {
private static final String SENTENCE =
" Nel mezzo del cammin di nostra vita " +
"mi ritrovia in una selva oscura" +
" ché la dritta via era smarrita ";
public static void main(String[] args) {
doStreamFilterDemo();
doStreamMappingDemo();
doStreamFindOrMatchingDemo();
doStreamReducingDemo();
doStreamCollectingDemo();
doStreamParallelDemo();
}
private static void doStreamFilterDemo() {
out.println("FILTER EXAMPLE");
getDishes().stream()
.filter(Dish::isVegetarian)
.collect(toList())
.forEach(out::println);
out.println("----------------------------------------");
out.println("DISTINCT EXAMPLE");
getDishes().stream()
.filter(dish -> dish.getName().length() > 4)
.distinct()
.forEach(out::println);
out.println("----------------------------------------");
out.println("TAKEWHILE EXAMPLE");
getDishes().stream()
.takeWhile(dish -> dish.getCalories() > 40)
.forEach(out::println);
out.println("----------------------------------------");
out.println("DROPWHILE EXAMPLE");
getDishes().stream()
.dropWhile(dish -> dish.getCalories() > 40)
.forEach(out::println);
out.println("----------------------------------------");
}
private static void doStreamMappingDemo() {
List<String> words = asList("Hello", "World");
List<String> uniqueCharacters = words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
out.println(uniqueCharacters);
out.println("----------------------------------------");
out.println("Permutations");
List<Integer> numbers1 = asList(1, 2, 3);
List<Integer> numbers2 = asList(3, 4);
List<int[]> pairs1 = numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.collect(toList());
pairs1.forEach(pair -> out.println(Arrays.toString(pair)));
out.println("----------------------------------------");
List<int[]> pairs2 = numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.filter(pair -> (pair[0] + pair[1]) % 3 == 0)
.collect(toList());
pairs2.forEach(pair -> out.println(Arrays.toString(pair)));
out.println("----------------------------------------");
}
private static void doStreamFindOrMatchingDemo() {
out.println("ANYMATCH EXAMPLE");
if (getDishes().stream().anyMatch(Dish::isVegetarian)) {
out.println("The menu is (somewhat) vegetarian friendly!!!");
}
out.println("----------------------------------------");
out.println("ALLMATCH EXAMPLE");
if (getDishes().stream().allMatch(dish -> dish.getCalories() < 1000)) {
out.println("This menù is healthy");
}
out.println("----------------------------------------");
out.println("NONEMATCH EXAMPLE");
if (getDishes().stream().noneMatch(dish -> dish.getCalories() >= 1000)) {
out.println("This menù is healthy");
}
out.println("----------------------------------------");
out.println("FINDANY EXAMPLE");
Optional<Dish> dish = getDishes().stream()
.filter(Dish::isVegetarian)
.findAny();
dish.ifPresent(out::println);
out.println("----------------------------------------");
out.println("FINDFIRST EXAMPLE");
List<Integer> someNumbers = asList(1, 2, 3, 4, 5);
someNumbers.stream()
.map(n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst()
.ifPresent(out::println);
out.println("----------------------------------------");
}
private static void doStreamReducingDemo() {
out.println("REDUCING");
int sum = Stream.of(4, 5, 3, 9).reduce(0, Integer::sum);
out.println(sum);
out.println("----------------------------------------");
out.println("MAX & MIN");
Stream.of(4, 5, 3, 9).reduce(Integer::max).ifPresent(out::println);
Stream.of(4, 5, 3, 9).reduce(Integer::min).ifPresent(out::println);
out.println("----------------------------------------");
out.println("COUNT DISHES");
getDishes().stream()
.map(d -> 1)
.reduce(Integer::sum)
.ifPresent(out::println);
out.println("----------------------------------------");
out.println("PUTTING ALL IN PRACTICE");
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
List<Transaction> transactions = asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
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)
.sorted(Comparator.comparing(Transaction::getValue))
.collect(toList())
.forEach(out::println);
out.println("2)");
transactions.stream()
.map(Transaction::getTrader)
.map(Trader::getCity)
.distinct()
.collect(toList())
.forEach(out::println);
out.println("3)");
transactions.stream()
.map(Transaction::getTrader)
.filter(trader -> "Cambridge".equalsIgnoreCase(trader.getCity()))
.distinct() // anche se sul testo non è specificato che dovevano essere diversi
.sorted(Comparator.comparing(Trader::getName))
.collect(toList())
.forEach(out::println);
out.println("4)");
String result = transactions.stream()
.map(Transaction::getTrader)
.map(Trader::getName)
.distinct()
.sorted()
.reduce("", (acc, name) -> acc + name);
out.println(result);
out.println("5)");
boolean anyTraderInMilan = transactions.stream()
.map(Transaction::getTrader)
.anyMatch(trader -> "Milan".equalsIgnoreCase(trader.getCity()));
out.println(anyTraderInMilan);
out.println("6)");
transactions.stream()
.filter(transaction -> "Cambridge".equalsIgnoreCase(transaction.getTrader().getCity()))
.forEach(transaction -> out.println(transaction.getValue()));
out.println("7)");
transactions.stream()
.mapToInt(Transaction::getValue)
.max()
.ifPresent(out::println);
out.println("8)");
transactions.stream()
.mapToInt(Transaction::getValue)
.min()
.ifPresent(out::println);
out.println("----------------------------------------");
IntStream.rangeClosed(1, 100)
.boxed()
.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("----------------------------------------");
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
.limit(20)
.forEach(pair -> out.printf("(%d, %d) ", pair[0], pair[1]));
out.println();
out.println("----------------------------------------");
}
private static void doStreamCollectingDemo() {
List<Dish> menu = getDishes();
long howManyDishes = menu.stream().count();
out.println(howManyDishes);
out.println("----------------------------------------");
Comparator<Dish> dishCaloriesComparator = comparingInt(Dish::getCalories);
menu.stream()
.collect(maxBy(dishCaloriesComparator))
.ifPresent(out::println);
out.println("----------------------------------------");
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
out.println(totalCalories);
out.println("----------------------------------------");
double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));
out.println(avgCalories);
out.println("----------------------------------------");
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
out.println(menuStatistics);
out.println("----------------------------------------");
String shortMenu = menu.stream()
.map(Dish::getName)
.collect(joining(", "));
out.println(shortMenu);
out.println("----------------------------------------");
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
out.println(dishesByType);
out.println("----------------------------------------");
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
}));
out.println(dishesByCaloricLevel);
out.println("----------------------------------------");
Map<Dish.Type, List<Dish>> caloricDishesByType =
menu.stream()
.collect(groupingBy(Dish::getType,
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())));
out.println(dishNamesByType);
out.println("----------------------------------------");
Map<String, List<String>> dishTags = Map.of(
"pork", asList("greasy", "salty"),
"beef", asList("salty", "roasted"),
"chicken", asList("fried", "crisp"),
"french fries", asList("greasy", "fried"),
"rice", asList("light", "natural"),
"season fruit", asList("fresh", "natural"),
"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())));
out.println(dishNamesByType2);
out.println("----------------------------------------");
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
menu.stream()
.collect(groupingBy(Dish::getType, groupingBy(dish -> {
if (dish.getCalories() <= 400)
return CaloricLevel.DIET;
else if (dish
.getCalories() <= 700)
return CaloricLevel.NORMAL;
else
return CaloricLevel.FAT;
})));
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("----------------------------------------");
}
private static void doStreamParallelDemo() {
out.println("ForkJoin result (1, 100) = " + forkJoinSum(100));
out.println("----------------------------------------");
Stream<Character> stream1 = IntStream.range(0, SENTENCE.length()).mapToObj(SENTENCE::charAt);
out.printf("Found %d words\n", countWords(stream1));
out.println("----------------------------------------");
Spliterator<Character> spliterator = new WordCounterSpliterator(SENTENCE);
Stream<Character> stream2 = StreamSupport.stream(spliterator, true);
out.printf("Found %d words\n", countWords(stream2));
out.println("----------------------------------------");
}
private static int countWords(Stream<Character> stream) {
WordCounter wordCounter = stream.reduce(new WordCounter(0, true),
WordCounter::accumulate,
WordCounter::combine);
return wordCounter.getCounter();
}
private static long forkJoinSum(int n) {
long[] numbers = LongStream.rangeClosed(1, n).toArray();
ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
return new ForkJoinPool().invoke(task);
}
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}
private static List<Dish> getDishes() {
List<Dish> dishes = asList(
new Dish("pork", 300, Dish.Type.MEAT),
new Dish("salad", 50, Dish.Type.VEGETARIAN),
new Dish("chicken", 100, Dish.Type.MEAT),
new Dish("chicken", 100, Dish.Type.MEAT),
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,39 @@
package org.gym.fp.moderjava.concurrent;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
@ToString
@AllArgsConstructor
public class Shop {
@Getter
private String name;
public double getPrice(String product) {
return calculatePrice(product);
}
public Future<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}
private double calculatePrice(String product) {
delay();
return new Random().nextDouble() * product.charAt(0) + product.charAt(1);
}
static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,29 @@
package org.gym.fp.moderjava.dsl;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;
import static java.util.stream.Collectors.groupingBy;
public class GroupingBuilder<T, D, K> {
private final Collector<? super T, ?, Map<K, D>> collector;
public GroupingBuilder(Collector<? super T, ?, Map<K, D>> collector) {
this.collector = collector;
}
public Collector<? super T, ?, Map<K, D>> get() {
return collector;
}
public <J> GroupingBuilder<T, Map<K, D>, J> after(Function<? super T, ? extends J> classifier) {
return new GroupingBuilder<>(groupingBy(classifier, collector));
}
public static <T, D, K> GroupingBuilder<T, List<T>, K> groupOn(Function<? super T, ? extends K> classifier) {
return new GroupingBuilder<>(groupingBy(classifier));
}
}

View File

@@ -0,0 +1,65 @@
package org.gym.fp.moderjava.dsl;
import java.util.function.Consumer;
public class LambdaOrderBuilder {
private Order order = new Order();
public static Order order(Consumer<LambdaOrderBuilder> consumer) {
LambdaOrderBuilder builder = new LambdaOrderBuilder();
consumer.accept(builder);
return builder.order;
}
public void forCustomer(String customer) {
order.setCustomer(customer);
}
public void buy(Consumer<TradeBuilder> consumer) {
trade(consumer, Trade.Type.BUY);
}
public void sell(Consumer<TradeBuilder> consumer) {
trade(consumer, Trade.Type.SELL);
}
private void trade(Consumer<TradeBuilder> consumer, Trade.Type type) {
TradeBuilder builder = new TradeBuilder();
builder.trade.setType(type);
consumer.accept(builder);
order.addTrade(builder.trade);
}
public static class TradeBuilder {
private Trade trade = new Trade();
public void quantity(int quantity) {
trade.setQuantity(quantity);
}
public void price(double price) {
trade.setPrice(price);
}
public void stock(Consumer<StockBuilder> consumer) {
StockBuilder builder = new StockBuilder();
consumer.accept(builder);
trade.setStock(builder.stock);
}
}
public static class StockBuilder {
private Stock stock = new Stock();
public void symbol(String symbol) {
stock.setSymbol(symbol);
}
public void market(String market) {
stock.setMarket(market);
}
}
}

View File

@@ -0,0 +1,31 @@
package org.gym.fp.moderjava.dsl;
public class MethodChainingOrderBuilder {
public final Order order = new Order();
private MethodChainingOrderBuilder(String customer) {
order.setCustomer(customer);
}
public static MethodChainingOrderBuilder forCustomer(String customer) {
return new MethodChainingOrderBuilder(customer);
}
public TradeBuilder buy(int quantity) {
return new TradeBuilder(this, Trade.Type.BUY, quantity);
}
public TradeBuilder sell(int quantity) {
return new TradeBuilder(this, Trade.Type.SELL, quantity);
}
public MethodChainingOrderBuilder addTrade(Trade trade) {
order.addTrade(trade);
return this;
}
public Order end() {
return order;
}
}

View File

@@ -0,0 +1,68 @@
package org.gym.fp.moderjava.dsl;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class MixedBuilder {
public static Order forCustomer(String customer, TradeBuilder... builders) {
Order order = new Order();
order.setCustomer(customer);
Stream.of(builders).forEach(b -> order.addTrade(b.trade));
return order;
}
public static TradeBuilder buy(Consumer<TradeBuilder> consumer) {
return buildTrade(consumer, Trade.Type.BUY);
}
public static TradeBuilder sell(Consumer<TradeBuilder> consumer) {
return buildTrade(consumer, Trade.Type.SELL);
}
private static TradeBuilder buildTrade(Consumer<TradeBuilder> consumer, Trade.Type type) {
TradeBuilder builder = new TradeBuilder();
builder.trade.setType(type);
consumer.accept(builder);
return builder;
}
public static class TradeBuilder {
private Trade trade = new Trade();
public TradeBuilder quantity(int quantity) {
trade.setQuantity(quantity);
return this;
}
public TradeBuilder at(double price) {
trade.setPrice(price);
return this;
}
public StockBuilder stock(String symbol) {
return new StockBuilder(this, trade, symbol);
}
}
public static class StockBuilder {
private final TradeBuilder builder;
private final Trade trade;
private final Stock stock = new Stock();
public StockBuilder(TradeBuilder tradeBuilder, Trade trade, String symbol) {
this.builder = tradeBuilder;
this.trade = trade;
this.stock.setSymbol(symbol);
}
public TradeBuilder on(String market) {
this.stock.setMarket(market);
trade.setStock(this.stock);
return builder;
}
}
}

View File

@@ -0,0 +1,46 @@
package org.gym.fp.moderjava.dsl;
import java.util.stream.Stream;
public class NestedFunctionOrderBuilder {
public static Order order(String customer, Trade... trades) {
Order order = new Order();
order.setCustomer(customer);
Stream.of(trades).forEach(order::addTrade);
return order;
}
public static Trade buy(int quantity, Stock stock, double price) {
return buildTrade(quantity, stock, price, Trade.Type.BUY);
}
public static Trade sell(int quantity, Stock stock, double price) {
return buildTrade(quantity, stock, price, Trade.Type.SELL);
}
private static Trade buildTrade(int quantity, Stock stock, double price, Trade.Type buy) {
Trade trade = new Trade();
trade.setQuantity(quantity);
trade.setType(buy);
trade.setStock(stock);
trade.setPrice(price);
return trade;
}
public static double at(double price) {
return price;
}
public static Stock stock(String symbol, String market) {
Stock stock = new Stock();
stock.setSymbol(symbol);
stock.setMarket(market);
return stock;
}
public static String on(String market) {
return market;
}
}

View File

@@ -0,0 +1,31 @@
package org.gym.fp.moderjava.dsl;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@ToString
public class Order {
private String customer;
private List<Trade> trades = new ArrayList<>();
public void addTrade(Trade trade) {
trades.add(trade);
}
public String getCustomer() {
return customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
public double getValue() {
return trades.stream()
.mapToDouble(Trade::getValue)
.sum();
}
}

View File

@@ -0,0 +1,25 @@
package org.gym.fp.moderjava.dsl;
import lombok.ToString;
@ToString
public class Stock {
private String symbol;
private String market;
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public String getMarket() {
return market;
}
public void setMarket(String market) {
this.market = market;
}
}

View File

@@ -0,0 +1,21 @@
package org.gym.fp.moderjava.dsl;
public class StockBuilder {
private final MethodChainingOrderBuilder builder;
private final Trade trade;
private final Stock stock = new Stock();
public StockBuilder(MethodChainingOrderBuilder builder,
Trade trade, String symbol) {
this.builder = builder;
this.trade = trade;
this.stock.setSymbol(symbol);
}
public TradeBuilderWithStock on(String market) {
this.stock.setMarket(market);
trade.setStock(this.stock);
return new TradeBuilderWithStock(builder, trade);
}
}

View File

@@ -0,0 +1,17 @@
package org.gym.fp.moderjava.dsl;
public class Tax {
public static double regional(double value) {
return value * 1.1;
}
public static double general(double value) {
return value * 1.3;
}
public static double surcharge(double value) {
return value * 1.05;
}
}

View File

@@ -0,0 +1,17 @@
package org.gym.fp.moderjava.dsl;
import java.util.function.DoubleUnaryOperator;
public class TaxCalculator {
private DoubleUnaryOperator taxFunction = d -> d;
public TaxCalculator with(DoubleUnaryOperator f) {
this.taxFunction = taxFunction.andThen(f);
return this;
}
public double calculate(Order order) {
return this.taxFunction.applyAsDouble(order.getValue());
}
}

View File

@@ -0,0 +1,50 @@
package org.gym.fp.moderjava.dsl;
import lombok.ToString;
@ToString
public class Trade {
public enum Type {BUY, SELL}
private Type type;
private Stock stock;
private int quantity;
private double price;
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Stock getStock() {
return stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getValue() {
return quantity * price;
}
}

View File

@@ -0,0 +1,18 @@
package org.gym.fp.moderjava.dsl;
public class TradeBuilder {
private final MethodChainingOrderBuilder builder;
private final Trade trade = new Trade();
public TradeBuilder(MethodChainingOrderBuilder builder,
Trade.Type buy, int quantity) {
this.builder = builder;
this.trade.setType(buy);
this.trade.setQuantity(quantity);
}
public StockBuilder stock(String symbol) {
return new StockBuilder(builder, trade, symbol);
}
}

View File

@@ -0,0 +1,17 @@
package org.gym.fp.moderjava.dsl;
public class TradeBuilderWithStock {
private final MethodChainingOrderBuilder builder;
private final Trade trade;
public TradeBuilderWithStock(MethodChainingOrderBuilder builder, Trade trade) {
this.builder = builder;
this.trade = trade;
}
public MethodChainingOrderBuilder at(double price) {
this.trade.setPrice(price);
return builder.addTrade(trade);
}
}

View File

@@ -0,0 +1,17 @@
package org.gym.fp.moderjava.optional;
import lombok.Getter;
import lombok.ToString;
import java.util.Optional;
@ToString
public class Car {
@Getter
private Optional<Insurance> insurance;
public Car(Optional<Insurance> insurance) {
this.insurance = insurance;
}
}

View File

@@ -0,0 +1,16 @@
package org.gym.fp.moderjava.optional;
import lombok.Getter;
import lombok.ToString;
@ToString
public class Insurance {
@Getter
private String name;
public Insurance(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,18 @@
package org.gym.fp.moderjava.optional;
import lombok.Getter;
import lombok.ToString;
import java.util.Optional;
@ToString
public class Person {
@Getter
private Optional<Car> car;
public Person(Optional<Car> car) {
this.car = car;
}
}

View File

@@ -0,0 +1,26 @@
package org.gym.fp.moderjava.stream;
import lombok.AllArgsConstructor;
import lombok.Value;
@Value
@AllArgsConstructor
public final class Dish {
private final String name;
private final int calories;
private final Type type;
public enum Type {
MEAT, FISH, VEGETARIAN, OTHER
}
public boolean isVegetarian() {
return this.type == Type.VEGETARIAN;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,44 @@
package org.gym.fp.moderjava.stream;
import java.util.concurrent.RecursiveTask;
public class ForkJoinSumCalculator extends RecursiveTask<Long> {
public static final long THRESHOLD = 10_000;
private final long[] numbers;
private final int start;
private final int end;
public ForkJoinSumCalculator(long[] numbers) {
this(numbers, 0, numbers.length);
}
private ForkJoinSumCalculator(long[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
int length = end - start;
if (length <= THRESHOLD) {
return computeSequentially();
}
ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, start + length / 2);
leftTask.fork();
ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbers, start + length / 2, end);
Long rightResult = rightTask.compute();
Long leftResult = leftTask.join();
return leftResult + rightResult;
}
private Long computeSequentially() {
long sum = 0;
for (int i = start; i < end; i++) {
sum += numbers[i];
}
return sum;
}
}

View File

@@ -0,0 +1,56 @@
package org.gym.fp.moderjava.stream;
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

@@ -0,0 +1,68 @@
package org.gym.fp.moderjava.stream;
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));
}
}

View File

@@ -0,0 +1,11 @@
package org.gym.fp.moderjava.stream;
import lombok.AllArgsConstructor;
import lombok.Value;
@Value
@AllArgsConstructor
public class Trader {
private final String name;
private final String city;
}

View File

@@ -0,0 +1,12 @@
package org.gym.fp.moderjava.stream;
import lombok.AllArgsConstructor;
import lombok.Value;
@Value
@AllArgsConstructor
public class Transaction {
private final Trader trader;
private final int year;
private final int value;
}

View File

@@ -0,0 +1,25 @@
package org.gym.fp.moderjava.stream;
public class WordCounter {
private final int counter;
private final boolean lastSpace;
public WordCounter(int counter, boolean lastSpace) {
this.counter = counter;
this.lastSpace = lastSpace;
}
public WordCounter accumulate(Character c) {
if (Character.isWhitespace(c)) return lastSpace ? this : new WordCounter(counter, true);
else return lastSpace ? new WordCounter(counter + 1, false) : this;
}
public WordCounter combine(WordCounter wordCounter) {
return new WordCounter(this.counter + wordCounter.getCounter(), wordCounter.lastSpace);
}
public int getCounter() {
return this.counter;
}
}

View File

@@ -0,0 +1,45 @@
package org.gym.fp.moderjava.stream;
import java.util.Spliterator;
import java.util.function.Consumer;
public class WordCounterSpliterator implements Spliterator<Character> {
private final String string;
private int currentChar = 0;
public WordCounterSpliterator(String string) {
this.string = string;
}
@Override
public boolean tryAdvance(Consumer<? super Character> action) {
action.accept(string.charAt(currentChar++));
return currentChar < string.length();
}
@Override
public Spliterator<Character> trySplit() {
int currentSize = string.length() - currentChar;
if (currentSize < 10) return null;
for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) {
if (Character.isWhitespace(string.charAt(splitPos))) {
Spliterator<Character> spliterator = new WordCounterSpliterator(
string.substring(currentChar, splitPos));
currentChar = splitPos;
return spliterator;
}
}
return null;
}
@Override
public long estimateSize() {
return string.length() - currentChar;
}
@Override
public int characteristics() {
return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE;
}
}

View File

@@ -11,6 +11,7 @@
<modules>
<module>scala</module>
<module>modern-java</module>
</modules>
<dependencyManagement>