目录
3.4.CompletableFuture: 组合式异步编程
3.5.1.Instant、Duration 以及Period
一.基础知识
1.1.Predicate谓词传递代码
package lambdasinaction.chap1;
import java.util.*;
import java.util.function.Predicate;
public class FilteringApples{
public static void main(String ... args){
List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
new Apple(155, "green"),
new Apple(120, "red"));
// [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
List<Apple> greenApples = filterApples(inventory, FilteringApples::isGreenApple);
System.out.println(greenApples);
// [Apple{color='green', weight=155}]
List<Apple> heavyApples = filterApples(inventory, FilteringApples::isHeavyApple);
System.out.println(heavyApples);
// [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
List<Apple> greenApples2 = filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
System.out.println(greenApples2);
// [Apple{color='green', weight=155}]
List<Apple> heavyApples2 = filterApples(inventory, (Apple a) -> a.getWeight() > 150);
System.out.println(heavyApples2);
// []
List<Apple> weirdApples = filterApples(inventory, (Apple a) -> a.getWeight() < 80 ||
"brown".equals(a.getColor()));
System.out.println(weirdApples);
}
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
public static List<Apple> filterHeavyApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
public static boolean isGreenApple(Apple apple) {
return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
//Predicate传递判断的方法
public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
//test测试
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
public static class Apple {
private int weight = 0;
private String color = "";
public Apple(int weight, String color){
this.weight = weight;
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
}
1.2.行为参数化+匿名类
package lambdasinaction.chap2;
import java.util.*;
public class FilteringApples{
public static void main(String ... args){
List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red"));
// [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
List<Apple> greenApples = filterApplesByColor(inventory, "green");
System.out.println(greenApples);
// [Apple{color='red', weight=120}]
List<Apple> redApples = filterApplesByColor(inventory, "red");
System.out.println(redApples);
// [Apple{color='green', weight=80}, Apple{color='green', weight=155}]
List<Apple> greenApples2 = filter(inventory, new AppleColorPredicate());
System.out.println(greenApples2);
// [Apple{color='green', weight=155}]
List<Apple> heavyApples = filter(inventory, new AppleWeightPredicate());
System.out.println(heavyApples);
// []
List<Apple> redAndHeavyApples = filter(inventory, new AppleRedAndHeavyPredicate());
System.out.println(redAndHeavyApples);
// [Apple{color='red', weight=120}]
List<Apple> redApples2 = filter(inventory, new ApplePredicate() {
public boolean test(Apple a){
return a.getColor().equals("red");
}
});
System.out.println(redApples2);
}
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if("green".equals(apple.getColor())){
result.add(apple);
}
}
return result;
}
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
}
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){
List<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
public static List<Apple> filter(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
public static class Apple {
private int weight = 0;
private String color = "";
public Apple(int weight, String color){
this.weight = weight;
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
//行为参数接口
interface ApplePredicate{
public boolean test(Apple a);
}
static class AppleWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWeight() > 150;
}
}
static class AppleColorPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "green".equals(apple.getColor());
}
}
static class AppleRedAndHeavyPredicate implements ApplePredicate{
public boolean test(Apple apple){
return "red".equals(apple.getColor())
&& apple.getWeight() > 150;
}
}
}
package lambdasinaction.chap2;
public class MeaningOfThis
{
public final int value = 4;
public void doIt()
{
int value = 6;
//匿名类
Runnable r = new Runnable(){
public final int value = 5;
public void run(){
int value = 10;
//因为this指的是包含它的Runnable,而不是外面的类MeaningOfThis。
System.out.println(this.value);
}
};
r.run();
}
public static void main(String...args)
{
MeaningOfThis m = new MeaningOfThis();
m.doIt(); // 5
}
}
1.3.Lambda表达式
1.3.1.环绕执行(execute around)模式
package lambdasinaction.chap3;
import java.io.*;
public class ExecuteAround {
public static void main(String ...args) throws IOException{
// method we want to refactor to make more flexible
String result = processFileLimited();
System.out.println(result);
System.out.println("---");
String oneLine = processFile((BufferedReader b) -> b.readLine());
System.out.println(oneLine);
String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());
System.out.println(twoLines);
}
public static String processFileLimited() throws IOException {
try (BufferedReader br =
new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))) {
return br.readLine();
}
}
// Lambda表达式允许你直接内联,为 函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
public static String processFile(BufferedReaderProcessor p) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))){
return p.process(br);
}
}
// processFile的行为参数化
public interface BufferedReaderProcessor{
public String process(BufferedReader b) throws IOException;
}
}
1.3.2.使用函数式接口
package lambdasinaction.chap3;
import java.util.*;
import static java.util.Comparator.comparing;
public class Sorting {
public static void main(String...args){
// 1 传递代码
List<Apple> inventory = new ArrayList<>();
inventory.addAll(Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red")));
// [Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}]
inventory.sort(new AppleComparator());
System.out.println(inventory);
// reshuffling things a little
inventory.set(1, new Apple(30, "green"));
// 2 使用匿名类
// [Apple{color='green', weight=30}, Apple{color='green', weight=80}, Apple{color='green', weight=155}]
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}});
System.out.println(inventory);
// reshuffling things a little
inventory.set(1, new Apple(20, "red"));
// 3 使用 Lambda表达式
// [Apple{color='red', weight=20}, Apple{color='green', weight=30}, Apple{color='green', weight=155}]
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
System.out.println(inventory);
// reshuffling things a little
inventory.set(1, new Apple(10, "red"));
// 4 使用方法引用
// [Apple{color='red', weight=10}, Apple{color='red', weight=20}, Apple{color='green', weight=155}]
inventory.sort(comparing(Apple::getWeight));
System.out.println(inventory);
}
public static class Apple {
private Integer weight = 0;
private String color = "";
public Apple(Integer weight, String color){
this.weight = weight;
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
static class AppleComparator implements Comparator<Apple> {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
}
二.函数式数据处理
2.1.引入流
Dish类的定义
package lambdasinaction.chap4;
import java.util.*;
public class Dish {
private final String name;
private final boolean vegetarian;
private final int calories;
private final Type type;
public Dish(String name, boolean vegetarian, int calories, Type type) {
this.name = name;
this.vegetarian = vegetarian;
this.calories = calories;
this.type = type;
}
public String getName() {
return name;
}
public boolean isVegetarian() {
return vegetarian;
}
public int getCalories() {
return calories;
}
public Type getType() {
return type;
}
public enum Type { MEAT, FISH, OTHER }
@Override
public String toString() {
return name;
}
public static final List<Dish> menu =
Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 400, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));
}
流的遍历(只能遍历一次)
package lambdasinaction.chap4;
import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.toList;
public class StreamVsCollection {
public static void main(String...args){
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
// uncommenting this line will result in an IllegalStateException
// because streams can be consumed only once
//s.forEach(System.out::println);
}
}
流的操作
package lambdasinaction.chap4;
import java.util.*;
import java.util.stream.*;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static lambdasinaction.chap4.Dish.menu;
public class StreamBasic {
public static void main(String...args){
// Java 7
getLowCaloricDishesNamesInJava7(Dish.menu).forEach(System.out::println);
System.out.println("---");
// Java 8
getLowCaloricDishesNamesInJava8(Dish.menu).forEach(System.out::println);
}
public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes){
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: dishes){
if(d.getCalories() > 400){
lowCaloricDishes.add(d);
}
}
List<String> lowCaloricDishesName = new ArrayList<>();
//匿名类
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){
return dishes.stream() //流
.filter(d -> d.getCalories() > 400) //过滤
.sorted(comparing(Dish::getCalories)) //排序比较
.map(Dish::getName) //映射 用 getName 方法参数化map,提取菜名
.collect(toList()); //收集 将Stream转 换为List
}
}
2.2.使用流
2.2.1.流的建立
package lambdasinaction.chap5;
import java.util.*;
import java.util.function.IntSupplier;
import java.util.stream.*;
import java.nio.charset.Charset;
import java.nio.file.*;
public class BuildingStreams {
public static void main(String...args) throws Exception{
// Stream.of 字符流
Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
// Stream.empty
Stream<String> emptyStream = Stream.empty();
// Arrays.stream
int[] numbers = {2, 3, 5, 7, 11, 13};
System.out.println(Arrays.stream(numbers).sum());
// Stream.iterate
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
// fibonnaci with iterate
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1],t[0] + t[1]})
.limit(10)
.forEach(t -> System.out.println("(" + t[0] + ", " + t[1] + ")"));
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1],t[0] + t[1]})
.limit(10)
. map(t -> t[0])
.forEach(System.out::println);
// random stream of doubles with Stream.generate
Stream.generate(Math::random)
.limit(10)
.forEach(System.out::println);
// stream of 1s with Stream.generate
IntStream.generate(() -> 1)
.limit(5)
.forEach(System.out::println);
IntStream.generate(new IntSupplier(){
public int getAsInt(){
return 2;
}
}).limit(5)
.forEach(System.out::println);
IntSupplier fib = new IntSupplier(){
private int previous = 0;
private int current = 1;
public int getAsInt(){
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return this.previous;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);
long uniqueWords = Files.lines(Paths.get("lambdasinaction/chap5/data.txt"), Charset.defaultCharset())
//将各个生成流扁平化为单个流
//一言以蔽之,flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接
//起来成为一个流。
.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
System.out.println("There are " + uniqueWords + " unique words in data.txt");
}
}
2.2.2.流的过滤
package lambdasinaction.chap5;
import lambdasinaction.chap4.*;
import java.util.stream.*;
import java.util.*;
import static java.util.stream.Collectors.toList;
import static lambdasinaction.chap4.Dish.menu;
public class Filtering{
public static void main(String...args){
// Filtering with predicate
List<Dish> vegetarianMenu =
menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
vegetarianMenu.forEach(System.out::println);
// Filtering unique elements
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
// Truncating a stream
List<Dish> dishesLimit3 =
menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
dishesLimit3.forEach(System.out::println);
// Skipping elements
List<Dish> dishesSkip2 =
menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
dishesSkip2.forEach(System.out::println);
}
}
2.2.3.流的查询
package lambdasinaction.chap5;
import lambdasinaction.chap4.*;
import java.util.stream.*;
import java.util.*;
import static lambdasinaction.chap4.Dish.menu;
public class Finding{
public static void main(String...args){
if(isVegetarianFriendlyMenu()){
System.out.println("Vegetarian friendly");
}
System.out.println(isHealthyMenu());
System.out.println(isHealthyMenu2());
Optional<Dish> dish = findVegetarianDish();
dish.ifPresent(d -> System.out.println(d.getName()));
}
private static boolean isVegetarianFriendlyMenu(){
return menu.stream().anyMatch(Dish::isVegetarian);
}
private static boolean isHealthyMenu(){
return menu.stream().allMatch(d -> d.getCalories() < 1000);
}
private static boolean isHealthyMenu2(){
return menu.stream().noneMatch(d -> d.getCalories() >= 1000);
}
private static Optional<Dish> findVegetarianDish(){
return menu.stream().filter(Dish::isVegetarian).findAny();
}
}
2.2.4.流的映射
package lambdasinaction.chap5;
import lambdasinaction.chap4.*;
import java.util.*;
import static java.util.stream.Collectors.toList;
import static lambdasinaction.chap4.Dish.menu;
public class Mapping{
public static void main(String...args){
// map
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
System.out.println(dishNames);
// map
List<String> words = Arrays.asList("Hello", "World");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
System.out.println(wordLengths);
// flatMap
words.stream()
//Stream<String[]>转为Stream<String>
.flatMap((String line) -> Arrays.stream(line.split("")))
.distinct()
.forEach(System.out::println);
// flatMap
List<Integer> numbers1 = Arrays.asList(1,2,3,4,5);
List<Integer> numbers2 = Arrays.asList(6,7,8);
List<int[]> pairs =
numbers1.stream()
.flatMap((Integer i) -> numbers2.stream()
.map((Integer j) -> new int[]{i, j})
)
.filter(pair -> (pair[0] + pair[1]) % 3 == 0)
.collect(toList());
pairs.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")"));
}
}
2.2.5.流的数字化
package lambdasinaction.chap5;
import lambdasinaction.chap4.*;
import java.util.stream.*;
import java.util.*;
import static lambdasinaction.chap4.Dish.menu;
public class NumericStreams{
public static void main(String...args){
List<Integer> numbers = Arrays.asList(3,4,5,1,2);
Arrays.stream(numbers.toArray()).forEach(System.out::println);
int calories = menu.stream()
.mapToInt(Dish::getCalories)
.sum();
System.out.println("Number of calories:" + calories);
// max and OptionalInt
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
int max;
if(maxCalories.isPresent()){
max = maxCalories.getAsInt();
}
else {
// we can choose a default value
max = 1;
}
System.out.println(max);
// numeric ranges
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
System.out.println(evenNumbers.count());
Stream<int[]> pythagoreanTriples =
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).boxed()
.map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)}));
pythagoreanTriples.forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2]));
}
public static boolean isPerfectSquare(int n){
return Math.sqrt(n) % 1 == 0;
}
}
2.2.6.流的归约
package lambdasinaction.chap5;
import lambdasinaction.chap4.*;
import java.util.stream.*;
import java.util.*;
import static lambdasinaction.chap4.Dish.menu;
public class Reducing{
public static void main(String...args){
List<Integer> numbers = Arrays.asList(3,4,5,1,2);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum);
int sum2 = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum2);
int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b));
System.out.println(max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
min.ifPresent(System.out::println);
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
System.out.println("Number of calories:" + calories);
}
}
2.2.7.流的实践
Transaction类的定义
package lambdasinaction.chap5;
public class Transaction{
private Trader trader;
private int year;
private int value;
public Transaction(Trader trader, int year, int value)
{
this.trader = trader;
this.year = year;
this.value = value;
}
public Trader getTrader(){
return this.trader;
}
public int getYear(){
return this.year;
}
public int getValue(){
return this.value;
}
public String toString(){
return "{" + this.trader + ", " +
"year: "+this.year+", " +
"value:" + this.value +"}";
}
}
Trader类的定义
package lambdasinaction.chap5;
public class Trader{
private String name;
private String city;
public Trader(String n, String c){
this.name = n;
this.city = c;
}
public String getName(){
return this.name;
}
public String getCity(){
return this.city;
}
public void setCity(String newCity){
this.city = newCity;
}
public String toString(){
return "Trader:"+this.name + " in " + this.city;
}
}
PuttingIntoPractice
package lambdasinaction.chap5;
import lambdasinaction.chap5.*;
import java.util.*;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
public class PuttingIntoPractice{
public static void main(String ...args){
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 = Arrays.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)
);
// Query 1: Find all transactions from year 2011 and sort them by value (small to high).
List<Transaction> tr2011 = transactions.stream()
.filter(transaction -> transaction.getYear() == 2011)
.sorted(comparing(Transaction::getValue))
.collect(toList());
System.out.println(tr2011);
// Query 2: What are all the unique cities where the traders work?
List<String> cities =
transactions.stream()
.map(transaction -> transaction.getTrader().getCity())
.distinct()
.collect(toList());
System.out.println(cities);
// Query 3: Find all traders from Cambridge and sort them by name.
List<Trader> traders =
transactions.stream()
.map(Transaction::getTrader)
.filter(trader -> trader.getCity().equals("Cambridge"))
.distinct()
.sorted(comparing(Trader::getName))
.collect(toList());
System.out.println(traders);
// Query 4: Return a string of all traders’ names sorted alphabetically.
String traderStr =
transactions.stream()
.map(transaction -> transaction.getTrader().getName())
.distinct()
.sorted()
.reduce("", (n1, n2) -> n1 + n2);
System.out.println(traderStr);
// Query 5: Are there any trader based in Milan?
boolean milanBased =
transactions.stream()
.anyMatch(transaction -> transaction.getTrader()
.getCity()
.equals("Milan")
);
System.out.println(milanBased);
// Query 6: Update all transactions so that the traders from Milan are set to Cambridge.
transactions.stream()
.map(Transaction::getTrader)
.filter(trader -> trader.getCity().equals("Milan"))
.forEach(trader -> trader.setCity("Cambridge"));
System.out.println(transactions);
// Query 7: What's the highest value in all the transactions?
int highestValue =
transactions.stream()
.map(Transaction::getValue)
//计算生成的流中的最大值
.reduce(0, Integer::max);
System.out.println(highestValue);
}
}
2.3.用流收集数据
2.3.1.交易货币分组
package lambdasinaction.chap6;
import java.util.*;
import static java.util.stream.Collectors.groupingBy;
public class GroupingTransactions {
public static List<Transaction> transactions = Arrays.asList( new Transaction(Currency.EUR, 1500.0),
new Transaction(Currency.USD, 2300.0),
new Transaction(Currency.GBP, 9900.0),
new Transaction(Currency.EUR, 1100.0),
new Transaction(Currency.JPY, 7800.0),
new Transaction(Currency.CHF, 6700.0),
new Transaction(Currency.EUR, 5600.0),
new Transaction(Currency.USD, 4500.0),
new Transaction(Currency.CHF, 3400.0),
new Transaction(Currency.GBP, 3200.0),
new Transaction(Currency.USD, 4600.0),
new Transaction(Currency.JPY, 5700.0),
new Transaction(Currency.EUR, 6800.0) );
public static void main(String ... args) {
groupImperatively();
groupFunctionally();
}
private static void groupImperatively() {
//建立累积交易分组的 Map
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
//迭代Transaction的List
for (Transaction transaction : transactions) {
//提取Transaction 的货币
Currency currency = transaction.getCurrency();
List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);
//如果分组Map中没有这种货币的条目,就创建一个
if (transactionsForCurrency == null) {
transactionsForCurrency = new ArrayList<>();
transactionsByCurrencies.put(currency, transactionsForCurrency);
}
//将当前遍历的Transaction 加入同一货币的Transaction的List
transactionsForCurrency.add(transaction);
}
System.out.println(transactionsByCurrencies);
}
private static void groupFunctionally() {
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency));
System.out.println(transactionsByCurrencies);
}
public static class Transaction {
private final Currency currency;
private final double value;
public Transaction(Currency currency, double value) {
this.currency = currency;
this.value = value;
}
public Currency getCurrency() {
return currency;
}
public double getValue() {
return value;
}
@Override
public String toString() {
return currency + " " + value;
}
}
public enum Currency {
EUR, USD, JPY, GBP, CHF
}
}
2.3.2. 归约和汇总
Reducing
package lambdasinaction.chap6;
import static java.util.stream.Collectors.*;
import static lambdasinaction.chap6.Dish.menu;
public class Reducing {
public static void main(String ... args) {
System.out.println("Total calories in menu: " + calculateTotalCalories());
System.out.println("Total calories in menu: " + calculateTotalCaloriesWithMethodReference());
System.out.println("Total calories in menu: " + calculateTotalCaloriesWithoutCollectors());
System.out.println("Total calories in menu: " + calculateTotalCaloriesUsingSum());
}
private static int calculateTotalCalories() {
return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j));
}
private static int calculateTotalCaloriesWithMethodReference() {
return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum));
}
private static int calculateTotalCaloriesWithoutCollectors() {
return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();
}
private static int calculateTotalCaloriesUsingSum() {
return menu.stream().mapToInt(Dish::getCalories).sum();
}
}
Summarizing
package lambdasinaction.chap6;
import java.util.*;
import java.util.function.*;
import static java.util.stream.Collectors.*;
import static lambdasinaction.chap6.Dish.menu;
public class Summarizing {
public static void main(String ... args) {
System.out.println("Nr. of dishes: " + howManyDishes());
System.out.println("The most caloric dish is: " + findMostCaloricDish());
System.out.println("The most caloric dish is: " + findMostCaloricDishUsingComparator());
System.out.println("Total calories in menu: " + calculateTotalCalories());
System.out.println("Average calories in menu: " + calculateAverageCalories());
System.out.println("Menu statistics: " + calculateMenuStatistics());
System.out.println("Short menu: " + getShortMenu());
System.out.println("Short menu comma separated: " + getShortMenuCommaSeparated());
}
private static long howManyDishes() {
return menu.stream().collect(counting());
}
private static Dish findMostCaloricDish() {
return menu.stream().collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)).get();
}
private static Dish findMostCaloricDishUsingComparator() {
Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
BinaryOperator<Dish> moreCaloricOf = BinaryOperator.maxBy(dishCaloriesComparator);
return menu.stream().collect(reducing(moreCaloricOf)).get();
}
private static int calculateTotalCalories() {
return menu.stream().collect(summingInt(Dish::getCalories));
}
private static Double calculateAverageCalories() {
return menu.stream().collect(averagingInt(Dish::getCalories));
}
private static IntSummaryStatistics calculateMenuStatistics() {
return menu.stream().collect(summarizingInt(Dish::getCalories));
}
private static String getShortMenu() {
return menu.stream().map(Dish::getName).collect(joining());
}
private static String getShortMenuCommaSeparated() {
return menu.stream().map(Dish::getName).collect(joining(", "));
}
}
2.3.3.分组
package lambdasinaction.chap6;
import java.util.*;
import static java.util.stream.Collectors.*;
import static lambdasinaction.chap6.Dish.menu;
public class Grouping {
enum CaloricLevel { DIET, NORMAL, FAT };
public static void main(String ... args) {
System.out.println("Dishes grouped by type: " + groupDishesByType());
System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel());
System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel());
System.out.println("Count dishes in groups: " + countDishesInGroups());
System.out.println("Most caloric dishes by type: " + mostCaloricDishesByType());
System.out.println("Most caloric dishes by type: " + mostCaloricDishesByTypeWithoutOprionals());
System.out.println("Sum calories by type: " + sumCaloriesByType());
System.out.println("Caloric levels by type: " + caloricLevelsByType());
}
private static Map<Dish.Type, List<Dish>> groupDishesByType() {
return menu.stream().collect(groupingBy(Dish::getType));
}
private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {
return menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} ));
}
private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {
return menu.stream().collect(
groupingBy(Dish::getType,
groupingBy((Dish dish) -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} )
)
);
}
private static Map<Dish.Type, Long> countDishesInGroups() {
return menu.stream().collect(groupingBy(Dish::getType, counting()));
}
private static Map<Dish.Type, Optional<Dish>> mostCaloricDishesByType() {
return menu.stream().collect(
groupingBy(Dish::getType,
//Collectors.reducing工厂方法是所有这些特殊情况的一般化
reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)));
}
private static Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOprionals() {
return menu.stream().collect(
groupingBy(Dish::getType,
//把收集器返回的结果转换为另一种类型
collectingAndThen(
reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2),
Optional::get)));
}
private static Map<Dish.Type, Integer> sumCaloriesByType() {
return menu.stream().collect(groupingBy(Dish::getType,
summingInt(Dish::getCalories)));
}
private static Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType() {
return menu.stream().collect(
//这个方法接受两个参数:一个函数对流中的元素做变换,另一个则将变换的结果对象收集起来。
//其目的是在累加之前对每个输入元素应用一个映射函数,这样就可以让接受特定类型元素的收集器适应不同类型的对象
groupingBy(Dish::getType, mapping(
dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT; },
toSet() )));
}
}
2.3.4.分区
package lambdasinaction.chap6;
import java.util.*;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.*;
import static lambdasinaction.chap6.Dish.menu;
public class Partitioning {
// Dishes partitioned by vegetarian: {false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}
// Vegetarian Dishes by type: {false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]}, true={OTHER=[french fries, rice, season fruit, pizza]}}
// Most caloric dishes by vegetarian: {false=pork, true=pizza}
public static void main(String ... args) {
System.out.println("Dishes partitioned by vegetarian: " + partitionByVegeterian());
System.out.println("Vegetarian Dishes by type: " + vegetarianDishesByType());
System.out.println("Most caloric dishes by vegetarian: " + mostCaloricPartitionedByVegetarian());
}
private static Map<Boolean, List<Dish>> partitionByVegeterian() {
return menu.stream().collect(partitioningBy(Dish::isVegetarian));
}
private static Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType() {
return menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
}
private static Object mostCaloricPartitionedByVegetarian() {
return menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));
}
}
2.3.5.收集器接口
ToListCollector
package lambdasinaction.chap6;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.*;
public class ToListCollector<T> implements Collector<T, List<T>, List<T>> {
@Override
public Supplier<List<T>> supplier() {
return () -> new ArrayList<T>();
}
@Override
public BiConsumer<List<T>, T> accumulator() {
return (list, item) -> list.add(item);
}
@Override
public Function<List<T>, List<T>> finisher() {
return i -> i;
}
@Override
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
}
}
开发Collector接口
package lambdasinaction.chap6;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;
import static java.util.stream.Collector.Characteristics.*;
public class PartitionPrimeNumbers {
public static void main(String ... args) {
System.out.println("Numbers partitioned in prime and non-prime: " + partitionPrimes(100));
System.out.println("Numbers partitioned in prime and non-prime: " + partitionPrimesWithCustomCollector(100));
}
//将前n个自然数按质数和非质数分区
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((double) candidate)) - 1)
.noneMatch(i -> candidate % i == 0);
}
public static Map<Boolean, List<Integer>> partitionPrimesWithCustomCollector(int n) {
return IntStream.rangeClosed(2, n).boxed().collect(new PrimeNumbersCollector());
}
public static boolean isPrime(List<Integer> primes, Integer candidate) {
double candidateRoot = Math.sqrt((double) candidate);
//return primes.stream().filter(p -> p < candidateRoot).noneMatch(p -> candidate % p == 0);
return takeWhile(primes, i -> i <= candidateRoot).stream().noneMatch(i -> candidate % i == 0);
}
public static <A> List<A> takeWhile(List<A> list, Predicate<A> p) {
int i = 0;
for (A item : list) {
//检查列表中的当前项目是否满足谓词
if (!p.test(item)) {
//如果不满足,返回该项目之前的前缀子列表
return list.subList(0, i);
}
i++;
}
//列表中的所有项目都满足谓词,因此返回列表本身
return list;
}
//第一步:定义Collector类的签名 流中元素的类型,累加器类型,collect操作的结果类型
public static class PrimeNumbersCollector
implements Collector<Integer, Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> {
//第二步:实现归约过程
@Override
public Supplier<Map<Boolean, List<Integer>>> supplier() {
return () -> new HashMap<Boolean, List<Integer>>() {{
put(true, new ArrayList<Integer>());
put(false, new ArrayList<Integer>());
}};
}
@Override
public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
return (Map<Boolean, List<Integer>> acc, Integer candidate) -> {
//根据isPrime的结果,获取质数或非质数列表
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;
};
}
//第四步:finisher方法和收集器的characteristics方法
@Override
public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
return i -> i;
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}
}
public Map<Boolean, List<Integer>> partitionPrimesWithInlineCollector(int n) {
return Stream.iterate(2, i -> i + 1).limit(n)
.collect(
() -> new HashMap<Boolean, List<Integer>>() {{
put(true, new ArrayList<Integer>());
put(false, new ArrayList<Integer>());
}},
(acc, candidate) -> {
acc.get( isPrime(acc.get(true), candidate) )
.add(candidate);
},
(map1, map2) -> {
map1.get(true).addAll(map2.get(true));
map1.get(false).addAll(map2.get(false));
});
}
}
2.4.并行处理
2.4.1.并行流
package lambdasinaction.chap7;
import java.util.stream.*;
public class ParallelStreams {
//Iterative Sum done in: 2 msecs
public static long iterativeSum(long n) {
long result = 0;
for (long i = 0; i <= n; i++) {
result += i;
}
return result;
}
//Sequential Sum done in: 97 msecs
public static long sequentialSum(long n) {
//生成自然数无限流
return Stream.iterate(1L, i -> i + 1)
//限制到前 n个数
.limit(n)
//对所有数字求和来归纳流
.reduce(Long::sum).get();
}
//Parallel forkJoinSum done in: 111 msecs
public static long parallelSum(long n) {
return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(Long::sum).get();
}
//Range forkJoinSum done in: 9 msecs
public static long rangedSum(long n) {
return LongStream.rangeClosed(1, n).reduce(Long::sum).getAsLong();
}
//Parallel range forkJoinSum done in: 3 msecs
public static long parallelRangedSum(long n) {
return LongStream.rangeClosed(1, n).parallel().reduce(Long::sum).getAsLong();
}
//SideEffect sum done in: 25 msecs
public static long sideEffectSum(long n) {
Accumulator accumulator = new Accumulator();
LongStream.rangeClosed(1, n).forEach(accumulator::add);
return accumulator.total;
}
//SideEffect prallel sum done in: 42 msecs
public static long sideEffectParallelSum(long n) {
Accumulator accumulator = new Accumulator();
//parallel()将流转换为并行流
LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
return accumulator.total;
}
public static class Accumulator {
private long total = 0;
public void add(long value) {
total += value;
}
}
}
测试
package lambdasinaction.chap7;
import java.util.concurrent.*;
import java.util.function.*;
public class ParallelStreamsHarness {
public static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool();
public static void main(String[] args) {
System.out.println("Iterative Sum done in: " + measurePerf(ParallelStreams::iterativeSum, 10_000_000L) + " msecs");
System.out.println("Sequential Sum done in: " + measurePerf(ParallelStreams::sequentialSum, 10_000_000L) + " msecs");
System.out.println("Parallel forkJoinSum done in: " + measurePerf(ParallelStreams::parallelSum, 10_000_000L) + " msecs" );
System.out.println("Range forkJoinSum done in: " + measurePerf(ParallelStreams::rangedSum, 10_000_000L) + " msecs");
System.out.println("Parallel range forkJoinSum done in: " + measurePerf(ParallelStreams::parallelRangedSum, 10_000_000L) + " msecs" );
System.out.println("ForkJoin sum done in: " + measurePerf(ForkJoinSumCalculator::forkJoinSum, 10_000_000L) + " msecs" );
System.out.println("SideEffect sum done in: " + measurePerf(ParallelStreams::sideEffectSum, 10_000_000L) + " msecs" );
System.out.println("SideEffect prallel sum done in: " + measurePerf(ParallelStreams::sideEffectParallelSum, 10_000_000L) + " msecs" );
}
public static <T, R> long measurePerf(Function<T, R> f, T input) {
long fastest = Long.MAX_VALUE;
for (int i = 0; i < 10; i++) {
long start = System.nanoTime();
R result = f.apply(input);
long duration = (System.nanoTime() - start) / 1_000_000;
System.out.println("Result: " + result);
if (duration < fastest) fastest = duration;
}
return fastest;
}
}
高效使用并行流
- 留意装箱。自动装箱和拆箱操作会大大降低性能。
- 有些操作本身在并行流上的性能就比顺序流差。特别是limit和findFirst等依赖于元 素顺序的操作,它们在并行流上执行的代价非常大。
- 还要考虑流的操作流水线的总计算成本。设N是要处理的元素的总数,Q是一个元素通过 流水线的大致处理成本,则N*Q就是这个对成本的一个粗略的定性估计。Q值较高就意味 着使用并行流时性能好的可能性比较大。
- 对于较小的数据量,选择并行流几乎从来都不是一个好的决定。并行处理少数几个元素 的好处还抵不上并行化造成的额外开销。
- 要考虑流背后的数据结构是否易于分解。例如,ArrayList的拆分效率比LinkedList 高得多,因为前者用不着遍历就可以平均拆分,而后者则必须遍历。另外,用range工厂方法创建的原始类型流也可以快速分解。最后,你将在7.3节中学到,你可以自己实现 Spliterator来完全掌控分解过程。
- 流自身的特点,以及流水线中的中间操作修改流的方式,都可能会改变分解过程的性能。 例如,一个SIZED流可以分成大小相等的两部分,这样每个部分都可以比较高效地并行处 理,但筛选操作可能丢弃的元素个数却无法预测,导致流本身的大小未知。
- 还要考虑终端操作中合并步骤的代价是大是小(例如Collector中的combiner方法)。 如果这一步代价很大,那么组合每个子流产生的部分结果所付出的代价就可能会超出通 过并行流得到的性能提升。
2.4.2.分支/合并框架
package lambdasinaction.chap7;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
import static lambdasinaction.chap7.ParallelStreamsHarness.FORK_JOIN_POOL;
/**
* 分支/合并框架的目的是以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任
* 务的结果合并起来生成整体结果。它是ExecutorService接口的一个实现,它把子任务分配给 线程池(称为ForkJoinPool)中的工作线程。
*/
//继承 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;
}
//覆盖RecursiveTask抽象方法
@Override
protected Long compute() {
//该任务负责求和的部分的大小
int length = end - start;
if (length <= THRESHOLD) {
//如果大小小于 或等于阈值,顺序计算结果
return computeSequentially();
}
//创建一个子任务来为数组的前一半求和 分治算法的并行版本
ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, start + length/2);
//利用另 一个 ForkJoinPool 线程异步执行新创建的子任务
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;
}
//ForkJoin sum done in: 21 msecs
public static long forkJoinSum(long n) {
long[] numbers = LongStream.rangeClosed(1, n).toArray();
//创建一个ForkJoinTask(RecursiveTask的父类)
ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
//创建了一个新的ForkJoinPool
return FORK_JOIN_POOL.invoke(task);
}
}
三.高效Java8编程
3.1.重构、测试和调试
3.1.1.策略模式
package lambdasinaction.chap8;
public class StrategyMain {
public static void main(String[] args) {
// old school
Validator v1 = new Validator(new IsNumeric());
System.out.println(v1.validate("aaaa"));
Validator v2 = new Validator(new IsAllLowerCase ());
System.out.println(v2.validate("bbbb"));
// with lambdas
Validator v3 = new Validator((String s) -> s.matches("\\d+"));
System.out.println(v3.validate("aaaa"));
Validator v4 = new Validator((String s) -> s.matches("[a-z]+"));
System.out.println(v4.validate("bbbb"));
}
interface ValidationStrategy {
public boolean execute(String s);
}
static private class IsAllLowerCase implements ValidationStrategy {
public boolean execute(String s){
return s.matches("[a-z]+");
}
}
static private class IsNumeric implements ValidationStrategy {
public boolean execute(String s){
return s.matches("\\d+");
}
}
static private class Validator{
private final ValidationStrategy strategy;
public Validator(ValidationStrategy v){
this.strategy = v;
}
public boolean validate(String s){
return strategy.execute(s); }
}
}
3.1.2.模板方法
package lambdasinaction.chap8;
import java.util.function.Consumer;
public class OnlineBankingLambda {
public static void main(String[] args) {
new OnlineBankingLambda().processCustomer(1337, (Customer c) -> System.out.println("Hello!"));
}
//processCustomer方法搭建了在线银行算法的框架:获取客户提供的ID,然后提供服务让
//用户满意。不同的支行可以通过继承OnlineBanking类,对该方法提供差异化的实现。
public void processCustomer(int id, Consumer<Customer> makeCustomerHappy){
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy.accept(c);
}
// dummy Customer class
static private class Customer {}
// dummy Database class
static private class Database{
static Customer getCustomerWithId(int id){ return new Customer();}
}
}
3.1.3.观察者模式
package lambdasinaction.chap8;
import java.util.ArrayList;
import java.util.List;
public class ObserverMain {
public static void main(String[] args) {
Feed f = new Feed();
f.registerObserver(new NYTimes());
f.registerObserver(new Guardian());
f.registerObserver(new LeMonde());
f.notifyObservers("The queen said her favourite book is Java 8 in Action!");
Feed feedLambda = new Feed();
feedLambda.registerObserver((String tweet) -> {
if(tweet != null && tweet.contains("money")){
System.out.println("Breaking news in NY! " + tweet); }
});
feedLambda.registerObserver((String tweet) -> {
if(tweet != null && tweet.contains("queen")){
System.out.println("Yet another news in London... " + tweet); }
});
feedLambda.notifyObservers("Money money money, give me money!");
}
//首先,你需要一个观察者接口,它将不同的观察者聚合在一起。它仅有一个名为inform的
//方法,一旦接收到一条新的新闻,该方法就会被调用
interface Observer{
void inform(String tweet);
}
interface Subject{
void registerObserver(Observer o);
void notifyObservers(String tweet);
}
//现在,你可以声明不同的观察者(比如,这里是三家不同的报纸机构),依据新闻中不同的 关键字分别定义不同的行为:
static private class NYTimes implements Observer{
@Override
public void inform(String tweet) {
if(tweet != null && tweet.contains("money")){
System.out.println("Breaking news in NY!" + tweet);
}
}
}
static private class Guardian implements Observer{
@Override
public void inform(String tweet) {
if(tweet != null && tweet.contains("queen")){
System.out.println("Yet another news in London... " + tweet);
}
}
}
static private class LeMonde implements Observer{
@Override
public void inform(String tweet) {
if(tweet != null && tweet.contains("wine")){
System.out.println("Today cheese, wine and news! " + tweet);
}
}
}
static private class Feed implements Subject{
private final List<Observer> observers = new ArrayList<>();
public void registerObserver(Observer o) {
this.observers.add(o);
}
public void notifyObservers(String tweet) {
observers.forEach(o -> o.inform(tweet));
}
}
}
3.1.4.责任链模式
package lambdasinaction.chap8;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class ChainOfResponsibilityMain {
public static void main(String[] args) {
ProcessingObject<String> p1 = new HeaderTextProcessing();
ProcessingObject<String> p2 = new SpellCheckerProcessing();
p1.setSuccessor(p2);
String result1 = p1.handle("Aren't labdas really sexy?!!");
System.out.println(result1);
UnaryOperator<String> headerProcessing =
(String text) -> "From Raoul, Mario and Alan: " + text;
UnaryOperator<String> spellCheckerProcessing =
(String text) -> text.replaceAll("labda", "lambda");
Function<String, String> pipeline = headerProcessing.andThen(spellCheckerProcessing);
String result2 = pipeline.apply("Aren't labdas really sexy?!!");
System.out.println(result2);
}
static private abstract class ProcessingObject<T> {
protected ProcessingObject<T> successor;
public void setSuccessor(ProcessingObject<T> successor) {
this.successor = successor;
}
public T handle(T input) {
T r = handleWork(input);
if (successor != null) {
return successor.handle(r);
}
return r;
}
abstract protected T handleWork(T input);
}
static private class HeaderTextProcessing
extends ProcessingObject<String> {
public String handleWork(String text) {
return "From Raoul, Mario and Alan: " + text;
}
}
static private class SpellCheckerProcessing
extends ProcessingObject<String> {
public String handleWork(String text) {
return text.replaceAll("labda", "lambda");
}
}
}
3.1.5.工厂模式
package lambdasinaction.chap8;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class FactoryMain {
public static void main(String[] args) {
Product p1 = ProductFactory.createProduct("loan");
Supplier<Product> loanSupplier = Loan::new;
Product p2 = loanSupplier.get();
Product p3 = ProductFactory.createProductLambda("loan");
}
static private class ProductFactory {
public static Product createProduct(String name){
switch(name){
case "loan": return new Loan();
case "stock": return new Stock();
case "bond": return new Bond();
default: throw new RuntimeException("No such product " + name);
}
}
public static Product createProductLambda(String name){
Supplier<Product> p = map.get(name);
if(p != null) return p.get();
throw new RuntimeException("No such product " + name);
}
}
static private interface Product {}
static private class Loan implements Product {}
static private class Stock implements Product {}
static private class Bond implements Product {}
final static private Map<String, Supplier<Product>> map = new HashMap<>();
static {
map.put("loan", Loan::new);
map.put("stock", Stock::new);
map.put("bond", Bond::new);
}
}
3.1.6.peek
package lambdasinaction.chap8;
import java.util.List;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
public class Peek {
public static void main(String[] args) {
//流提供的peek方法在分析Stream流水线时,能将中间变量的值输出到日志中,是非常有用的工具。
List<Integer> result = Stream.of(2, 3, 4, 5)
.peek(x -> System.out.println("taking from stream: " + x)).map(x -> x + 17)
.peek(x -> System.out.println("after map: " + x)).filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter: " + x)).limit(3)
.peek(x -> System.out.println("after limit: " + x)).collect(toList());
}
}
3.2.默认方法
3.2.1.API演变问题
3.2.2.概述默认方法
默认方法由default修饰符修饰,并像类中声明的其他方法一样包含方法体。
3.2.3.默认方法的使用模式
- 可选模式
- 行为的多继承
默认方法让之前无法想象的事儿以一种优雅的方式得以实现,即行为的多继承。这是一种让
类从多个来源重用代码的能力。
1. 类型的多继承
2. 利用正交方法的精简接口
3.2.4.解决冲突的规则
如果一个类使用相同的函数签名从多个地方(比如另一个类或接口)继承了方法,通过三条
规则可以进行判断。(1) 类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优
先级。(2) 如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择
拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。(3) 最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪一个默认方法的实现。
这个新添加到C接口中的抽象方法hello比由接口A继承而来的hello方法拥有更高的优先级,因为C接口更加具体。因此,类D现在需要为hello显式地添加实现,否则该程序无法通过编译。
小结
3.3.用Optional取代null
3.3.1.Optional 类入门
package lambdasinaction.chap10;
import java.util.*;
public class Person {
//人可能有车,也可能没 有车,因此将这个字段 声明为Optional
private Optional<Car> car;
public Optional<Car> getCar() {
return car;
}
}
//=======================================================================================
package lambdasinaction.chap10;
import java.util.*;
public class Car {
//车可能进行了保险,也可 能没有保险,所以将这个 字段声明为Optional
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() {
return insurance;
}
}
//=======================================================================================
package lambdasinaction.chap10;
public class Insurance {
//保险公司必 须有名字
//所以,如果你遇到一个公司没有名称,你需要调查你的数据出了 什么问题,而不应该再添加一段代码,将这个问题隐藏。
private String name;
public String getName() {
return name;
}
}
3.3.2.应用 Optional 的几种模式
1.创建 Optional 对象
2.使用 map 从 Optional 对象中提取和转换值
3.使用 flatMap 链接 Optional 对象
package lambdasinaction.chap10;
import java.util.*;
public class OptionalMain {
//类似地,传递给optional的flatMap方法的函 数会将原始包含正方形的optional对象转换为包含三角形的optional对象。
// 如果将该方法传递 给map方法,结果会是一个Optional对象,而这个Optional对象中包含了三角形;
// 但flatMap 方法会将这种两层的Optional对象转换为包含三角形的单一Optional对象。
public String getCarInsuranceName(Optional<Person> person) {
//从纯理论的角度而言,你可以将这种合并操作简单地看成 把两个Optional对象结合在一起,如果其中有一个对象为空,
// 就构成一个空的Optional对象。
return person.flatMap(Person::getCar)
//第二步与第一步大同小异,它会将Optional<Car>转换为Optional<Insurance>。第三步
//则会将Optional<Insurance>转化为Optional<String>对象,由于Insurance.getName() 方法的返回类型为String,
// 这里就不再需要进行flapMap操作了。
.flatMap(Car::getInsurance)
.map(Insurance::getName)
//如果Optional的结果 值为空,设置默认值
.orElse("Unknown");
}
}
注意:Optional并未实现 Serializable接口,如果一定要实现序列化的域模型,作为替代方案, 我们建议你像下面这个例子那样,提供一个能访问声明为Optional、变量值可能缺失的接口, 代码清单如下:
public class Person {
private Car car;
public Optional<Car> getCarAsOptional() {
return Optional.ofNullable(car);
}
}
4.默认行为及解引用 Optional 对象
5.两个 Optional 对象的组合
nullSafeFindCheapestInsurance
6.使用 filter 剔除特定的值
filter方法接受一个谓词作为参数。如果Optional对象的值存在,并且它符合谓词的条件,
filter方法就返回其值;否则它就返回一个空的Optional对象。如果你还记得我们可以将 Optional看成最多包含一个元素的Stream对象,这个方法的行为就非常清晰了。如果Optional 对象为空,它不做任何操作,反之,它就对Optional对象中包含的值施加谓词操作。如果该操 作的结果为true,它不做任何改变,直接返回该Optional对象,否则就将该值过滤掉,将 Optional的值置空。
3.3.3.使用 Optional 的实战示例
package lambdasinaction.chap10;
import java.util.*;
import static java.util.Optional.of;
import static java.util.Optional.empty;
public class OperationsWithOptional {
public static void main(String... args) {
System.out.println(max(of(3), of(5)));
System.out.println(max(empty(), of(5)));
}
public static final Optional<Integer> max(Optional<Integer> i, Optional<Integer> j) {
return i.flatMap(a -> j.map(b -> Math.max(a, b)));
}
}
package lambdasinaction.chap10;
import org.junit.*;
import java.util.*;
import static java.util.Optional.*;
import static org.junit.Assert.assertEquals;
public class ReadPositiveIntParam {
@Test
public void testMap() {
Properties props = new Properties();
props.setProperty("a", "5");
props.setProperty("b", "true");
props.setProperty("c", "-3");
assertEquals(5, readDurationImperative(props, "a"));
assertEquals(0, readDurationImperative(props, "b"));
assertEquals(0, readDurationImperative(props, "c"));
assertEquals(0, readDurationImperative(props, "d"));
assertEquals(5, readDurationWithOptional(props, "a"));
assertEquals(0, readDurationWithOptional(props, "b"));
assertEquals(0, readDurationWithOptional(props, "c"));
assertEquals(0, readDurationWithOptional(props, "d"));
}
public static int readDurationImperative(Properties props, String name) {
String value = props.getProperty(name);
if (value != null) {
try {
int i = Integer.parseInt(value);
if (i > 0) {
return i;
}
} catch (NumberFormatException nfe) { }
}
return 0;
}
public static int readDurationWithOptional(Properties props, String name) {
//使用Optional.ofNullable封装map的返回值
return ofNullable(props.getProperty(name))
.flatMap(ReadPositiveIntParam::s2i)
.filter(i -> i > 0).orElse(0);
}
public static Optional<Integer> s2i(String s) {
try {
//如果String能转换为对应的Integer,将其封装在Optioal对象中返回
return of(Integer.parseInt(s));
} catch (NumberFormatException e) {
//否则返回一个空的Optional对象
return empty();
}
}
}
3.4.CompletableFuture: 组合式异步编程
3.4.1. Future 接口
Future 接口的局限性
使用 CompletableFuture 构建异步应用
为了展示CompletableFuture的强大特性,我们会创建一个名为“最佳价格查询器”
(best-price-finder)的应用,它会查询多个在线商店,依据给定的产品或服务找出最低的价格。这个过程中,你会学到几个重要的技能。
- 提供异步API
- 同步API的代码变为非阻塞代码,你会了解如何使用流水线将两个接续的异步操作合并为一个异步计算操作,这种情况肯定会出现,比如,在线商店返回了你想要购买商品的原始价格,并附带着一个折扣代码——最终,要计算出该 商品的实际价格,你不得不访问第二个远程折扣服务,查询该折扣代码对应的折扣比率。
- 响应式的方式处理异步操作的完成事件,以及随着各个商店返回它的商品价格,最佳价格查询器如何持续地更新每种商品的最佳推荐,而不是等待所有的商店都返回他们各自的价格(这种方式存在着一定的风险,一旦某家商店的服务中断,用户可能遭遇白屏)。
3.4.2.实现异步 API
package lambdasinaction.chap11.v1;
import static lambdasinaction.chap11.Util.delay;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
public class Shop {
private final String name;
private final Random random;
public Shop(String name) {
this.name = name;
random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));
}
//为了实现最佳价格查询器应用,让我们从每个商店都应该提供的API定义入手。首先,商店
//应该声明依据指定产品名称返回价格的方法:
/**
* 很明显,这个API的使用者(这个例子中为最佳价格查询器)调用该方法时,它依旧会被
* 阻塞。为等待同步事件完成而等待1秒钟,这是无法接受的,尤其是考虑到最佳价格查询器对网络中的所有商店都要重复这种操作。
*/
public double getPrice(String product) {
return calculatePrice(product);
}
private double calculatePrice(String product) {
//该方法的内部实现会查询商店的数据库,但也有可能执行一些其他耗时的任务,比如联系其
//他外部服务(比如,商店的供应商,或者跟制造商相关的推广折扣)。我们在本章剩下的内容中,
// 采用delay方法模拟这些长期运行的方法的执行,它会人为地引入1秒钟的延迟,方法声明如下。
delay();
//getPrice方法会调用delay方法,并返回一个随机计算的值,代码
//清单如下所示。返回随机计算的价格这段代码看起来有些取巧。它使用charAt,依据产品的名称,生成一个随机值作为价格。
return random.nextDouble() * product.charAt(0) + product.charAt(1);
}
/**将同步方法转换为异步方法
* java.util.concurrent.Future接口表示一个异步计算(即调用线程可以继续运行,不会因为调用方法而阻塞)的结果。
* 这意味着Future是一 个暂时还不可知值的处理器,这个值在计算完成后,可以通过调用它的get方法取得。
* 因为这样的设计,getPriceAsync方法才能立刻返回,给调用线程一个机会,能在同一时间去执行其他 有价值的计算任务。
* 新的CompletableFuture类提供了大量的方法,让我们有机会以多种可能 的方式轻松地实现这个方法,
* 比如下面就是这样一段实现代码。
*/
public Future<Double> getPriceAsync(String product) {
//使用工厂方法supplyAsync创建CompletableFuture对象
/**
* supplyAsync方法接受一个生产者(Supplier)作为参数,返回一个CompletableFuture对象,
* 该对象完成异步执行后会读取调用生产者方法的返回值。生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,
* 但是你也可以使用supplyAsync方法的重载版本,
* 传递第二个参数指定不同的执行线程执行生产者方法。一般而言,向CompletableFuture的工厂方法传递可选参数,
* 指定生产者方法的执行线程是可行的,
*/
// return CompletableFuture.supplyAsync(() -> calculatePrice(product));
//创建CompletableFuture对象,它会包含计算的结果
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
/**
* 你调用fork创建了另一个线程去执行实际的价格计算工作,不等该耗时计算任务结束,
* 直接返回一个Future实例。当请求的产品价格最终计算得出时,你可以使用它的complete方法,
* 结束completableFuture对象的运行,并设置变量的值。
*/
new Thread( () -> {
try {
//在另一个线程中以异步方式执行计算
double price = calculatePrice(product);
//需长时间计算的任务结 束并得出结果时,设置Future的返回值
futurePrice.complete(price);
}catch (Exception ex) {
/**
* 用于提示错误的异常会被限制 在试图计算商品价格的当前线程的范围内,
* 最终会杀死该线程,而这会导致等待get方法返回结 果的客户端永久地被阻塞
*/
//否则就抛出导致失 败的异常,完成这次Future操作
futurePrice.completeExceptionally(ex); }
}).start();
//无需等待还没结束的计算,直接返回Future对象
return futurePrice;
}
public String getName() {
return name;
}
}
package lambdasinaction.chap11.v1;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class ShopMain {
/**
* 由于商店提供了异步API,该次
* 调用立刻返回了一个Future对象,通过该对象客户可以在将来的某个时刻取得商品的价格。
* 这种方式下,客户在进行商品价格查询的同时,还能执行一些其他的任务,比如查询其他家商店中商品的价格,
* 不会呆呆地阻塞在那里等待第一家商店返回请求的结果。最后,如果所有有意义的工作都已经完成,
* 客户所有要执行的工作都依赖于商品价格时,再调用Future的get方法。
* 执行了这个操作后,客户要么获得Future中封装的值(如果异步任务已经完成),要么发生阻塞,
* 直到该异步任务完成,期望的值能够访问。
*
* Invocation returned after 42 msecs
* Doing something else...
* Price is 123.26
* Price returned after 1074 msecs
*/
public static void main(String[] args) {
Shop shop = new Shop("BestShop");
long start = System.nanoTime();
//查询商店,试图取得商品的价格
Future<Double> futurePrice = shop.getPriceAsync("my favorite product");
long invocationTime = ((System.nanoTime() - start) / 1_000_000);
System.out.println("Invocation returned after " + invocationTime
+ " msecs");
// Do some more tasks, like querying other shops
doSomethingElse();
// while the price of the product is being calculated
try {
//从Future对象中读取价格,如果价格未知,会发生阻塞
double price = futurePrice.get();
System.out.printf("Price is %.2f%n", price);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
long retrievalTime = ((System.nanoTime() - start) / 1_000_000);
System.out.println("Price returned after " + retrievalTime + " msecs");
}
private static void doSomethingElse() {
System.out.println("Doing something else...");
}
}
3.5. 新的日期和时间API
3.5.1.Instant、Duration 以及Period
Instant
定义 Duration 或 Period
Temporal接口定义了如何读取和操纵为时间建模的对象的值。我们需要创建两个Temporal对象之间的duration。Duration类的静态工厂方 法between就是为这个目的而设计的。你可以创建两个LocalTimes对象、两个LocalDateTimes对象,或者两个Instant对象之间的duration,如下所示:
3.5.2.操纵、解析和格式化日期
使用 TemporalAdjuster
打印输出及解析日期时间对象
package lambdasinaction.chap12;
import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;
import static java.time.temporal.TemporalAdjusters.nextOrSame;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.chrono.JapaneseDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class DateTimeExamples {
private static final ThreadLocal<DateFormat> formatters = new ThreadLocal<DateFormat>() {
protected DateFormat initialValue() {
return new SimpleDateFormat("dd-MMM-yyyy");
}
};
public static void main(String[] args) {
useOldDate();
useLocalDate();
useTemporalAdjuster();
useDateFormatter();
}
private static void useOldDate() {
Date date = new Date(114, 2, 18);
System.out.println(date);
System.out.println(formatters.get().format(date));
Calendar calendar = Calendar.getInstance();
calendar.set(2014, Calendar.FEBRUARY, 18);
System.out.println(calendar);
}
private static void useLocalDate() {
LocalDate date = LocalDate.of(2014, 3, 18);
int year = date.getYear(); // 2014
Month month = date.getMonth(); // MARCH
int day = date.getDayOfMonth(); // 18
DayOfWeek dow = date.getDayOfWeek(); // TUESDAY
int len = date.lengthOfMonth(); // 31 (days in March)
boolean leap = date.isLeapYear(); // false (not a leap year)
System.out.println(date);
int y = date.get(ChronoField.YEAR);
int m = date.get(ChronoField.MONTH_OF_YEAR);
int d = date.get(ChronoField.DAY_OF_MONTH);
LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20
int hour = time.getHour(); // 13
int minute = time.getMinute(); // 45
int second = time.getSecond(); // 20
System.out.println(time);
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 2014-03-18T13:45
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date);
System.out.println(dt1);
LocalDate date1 = dt1.toLocalDate();
System.out.println(date1);
LocalTime time1 = dt1.toLocalTime();
System.out.println(time1);
Instant instant = Instant.ofEpochSecond(44 * 365 * 86400);
Instant now = Instant.now();
Duration d1 = Duration.between(LocalTime.of(13, 45, 10), time);
Duration d2 = Duration.between(instant, now);
System.out.println(d1.getSeconds());
System.out.println(d2.getSeconds());
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
System.out.println(threeMinutes);
JapaneseDate japaneseDate = JapaneseDate.from(date);
System.out.println(japaneseDate);
}
private static void useTemporalAdjuster() {
LocalDate date = LocalDate.of(2014, 3, 18);
date = date.with(nextOrSame(DayOfWeek.SUNDAY));
System.out.println(date);
date = date.with(lastDayOfMonth());
System.out.println(date);
date = date.with(new NextWorkingDay());
System.out.println(date);
date = date.with(nextOrSame(DayOfWeek.FRIDAY));
System.out.println(date);
date = date.with(new NextWorkingDay());
System.out.println(date);
date = date.with(nextOrSame(DayOfWeek.FRIDAY));
System.out.println(date);
date = date.with(temporal -> {
DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
System.out.println(date);
}
private static class NextWorkingDay implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
}
}
private static void useDateFormatter() {
LocalDate date = LocalDate.of(2014, 3, 18);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
//创建一个本地化的DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
//LocalDate的formate方法使用指定的模式生成了一个代表该日期的字符串。
//静态的parse方法使用同样的格式器解析了刚才生成的字符串
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(date.format(formatter));
System.out.println(date.format(italianFormatter));
//构造一个DateTimeFormatter
//比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不 精确地匹配指定的模式)、填充,以及在格式器中指定可选节
DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
System.out.println(date.format(complexFormatter));
}
}
四.超越Java8
4.1.函数式编程
package lambdasinaction.chap13;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SubsetsMain {
public static void main(String[] args) {
List<List<Integer>> subs = subsets(Arrays.asList(1, 4, 9));
subs.forEach(System.out::println);
}
public static List<List<Integer>> subsets(List<Integer> l) {
if (l.isEmpty()) {
List<List<Integer>> ans = new ArrayList<>();
ans.add(Collections.emptyList());
return ans;
}
Integer first = l.get(0);
List<Integer> rest = l.subList(1,l.size());
List<List<Integer>> subans = subsets(rest);
List<List<Integer>> subans2 = insertAll(first, subans);
return concat(subans, subans2);
}
public static List<List<Integer>> insertAll(Integer first, List<List<Integer>> lists) {
List<List<Integer>> result = new ArrayList<>();
for (List<Integer> l : lists) {
List<Integer> copyList = new ArrayList<>();
copyList.add(first);
copyList.addAll(l);
result.add(copyList);
}
return result;
}
static List<List<Integer>> concat(List<List<Integer>> a, List<List<Integer>> b) {
List<List<Integer>> r = new ArrayList<>(a);
r.addAll(b);
return r;
}
}
4.2.递归和迭代
package lambdasinaction.chap13;
import java.util.stream.LongStream;
public class Recursion {
public static void main(String[] args) {
System.out.println(factorialIterative(5));
System.out.println(factorialRecursive(5));
System.out.println(factorialStreams(5));
System.out.println(factorialTailRecursive(5));
}
//迭代式的阶乘计算
public static int factorialIterative(int n) {
int r = 1;
for (int i = 1; i <= n; i++) {
r*=i;
}
return r;
}
//递归式的阶乘计算 方法调用自身
/**
* 每次执行factorialRecursive 方法调用都会在调用栈上创建一个新的栈帧,
* 用于保存每个方法调用的状态(即它需要进行的乘 法运算),这个操作会一直指导程序运行直到结束。
* 这意味着你的递归迭代方法会依据它接收的 输入成比例地消耗内存。
* @param n
* @return
*/
public static long factorialRecursive(long n) {
return n == 1 ? 1 : n*factorialRecursive(n-1);
}
//基于Stream的阶乘
public static long factorialStreams(long n){
return LongStream.rangeClosed(1, n)
.reduce(1, (long a, long b) -> a * b);
}
/**
* 基于“尾递”的阶乘
*
* 函数式语言提供了一种方法解决这一问题:尾
* 调优化(tail-call optimization)。基本的思想是你可以编写阶乘的一个迭代定义,
* 不过迭代调用发 生在函数的最后(所以我们说调用发生在尾部)。这种新型的迭代调用经过优化后执行的速度快很多。
* @param n
* @return
*/
public static long factorialTailRecursive(long n) {
return factorialHelper(1, n);
}
/**
* 方法factorialHelper属于“尾递”类型的函数,原因是递归调用发生在方法的最后。对
* 比我们前文中factorialRecursive方法的定义,这个方法的最后一个操作是乘以n,从而得到 递归调用的结果。
*
* 这种形式的递归是非常有意义的,现在我们不需要在不同的栈帧上保存每次递归计算的中间
* 值,编译器能够自行决定复用某个栈帧进行计算。实际上,在factorialHelper的定义中,
* 立即数(阶乘计算的中间结果)直接作为参数传递给了该方法。
* 再也不用为每个递归调用分配单独 的栈帧用于跟踪每次递归调用的中间值——通过方法的参数能够直接访问这些值.
*
* @param acc
* @param n
* @return
*/
public static long factorialHelper(long acc, long n) {
return n == 1 ? acc : factorialHelper(acc * n, n-1);
}
}
4.3.StreamForker
package lambdasinaction.appc;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
/**
* Adapted from http://mail.openjdk.java.net/pipermail/lambda-dev/2013-November/011516.html
*
* 要达到在一个流上并发地执行多个操作的效果,你需要做的第一件事就是创建一个 StreamForker,
* 这个StreamForker会对原始的流进行封装,在此基础之上你可以继续定义你 希望执行的各种操作.
*/
public class StreamForker<T> {
private final Stream<T> stream;
private final Map<Object, Function<Stream<T>, ?>> forks = new HashMap<>();
public StreamForker(Stream<T> stream) {
this.stream = stream;
}
public StreamForker<T> fork(Object key, Function<Stream<T>, ?> f) {
forks.put(key, f);
return this;
}
public Results getResults() {
ForkingStreamConsumer<T> consumer = build();
try {
stream.sequential().forEach(consumer);
} finally {
consumer.finish();
}
return consumer;
}
private ForkingStreamConsumer<T> build() {
List<BlockingQueue<T>> queues = new ArrayList<>();
Map<Object, Future<?>> actions =
forks.entrySet().stream().reduce(
new HashMap<Object, Future<?>>(),
(map, e) -> {
map.put(e.getKey(),
getOperationResult(queues, e.getValue()));
return map;
},
(m1, m2) -> {
m1.putAll(m2);
return m1;
});
return new ForkingStreamConsumer<>(queues, actions);
}
private Future<?> getOperationResult(List<BlockingQueue<T>> queues, Function<Stream<T>, ?> f) {
BlockingQueue<T> queue = new LinkedBlockingQueue<>();
queues.add(queue);
Spliterator<T> spliterator = new BlockingQueueSpliterator<>(queue);
Stream<T> source = StreamSupport.stream(spliterator, false);
return CompletableFuture.supplyAsync( () -> f.apply(source) );
}
public static interface Results {
public <R> R get(Object key);
}
private static class ForkingStreamConsumer<T> implements Consumer<T>, Results {
static final Object END_OF_STREAM = new Object();
private final List<BlockingQueue<T>> queues;
private final Map<Object, Future<?>> actions;
ForkingStreamConsumer(List<BlockingQueue<T>> queues, Map<Object, Future<?>> actions) {
this.queues = queues;
this.actions = actions;
}
@Override
public void accept(T t) {
queues.forEach(q -> q.add(t));
}
@Override
public <R> R get(Object key) {
try {
return ((Future<R>) actions.get(key)).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
void finish() {
accept((T) END_OF_STREAM);
}
}
private static class BlockingQueueSpliterator<T> implements Spliterator<T> {
private final BlockingQueue<T> q;
BlockingQueueSpliterator(BlockingQueue<T> q) {
this.q = q;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
T t;
while (true) {
try {
t = q.take();
break;
}
catch (InterruptedException e) {
}
}
if (t != ForkingStreamConsumer.END_OF_STREAM) {
action.accept(t);
return true;
}
return false;
}
@Override
public Spliterator<T> trySplit() {
return null;
}
@Override
public long estimateSize() {
return 0;
}
@Override
public int characteristics() {
return 0;
}
}
}
package lambdasinaction.appc;
import lambdasinaction.chap6.*;
import static java.util.stream.Collectors.*;
import static lambdasinaction.chap6.Dish.menu;
import java.util.*;
import java.util.stream.*;
public class StreamForkerExample {
public static void main(String[] args) throws Exception {
processMenu();
}
private static void processMenu() {
Stream<Dish> menuStream = menu.stream();
StreamForker.Results results = new StreamForker<Dish>(menuStream)
//一个流上并发地执行多个操作
.fork("shortMenu", s -> s.map(Dish::getName).collect(joining(", ")))
.fork("totalCalories", s -> s.mapToInt(Dish::getCalories).sum())
.fork("mostCaloricDish", s -> s.collect(
reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2))
.get())
.fork("dishesByType", s -> s.collect(groupingBy(Dish::getType)))
.getResults();
String shortMeny = results.get("shortMenu");
int totalCalories = results.get("totalCalories");
Dish mostCaloricDish = results.get("mostCaloricDish");
Map<Dish.Type, List<Dish>> dishesByType = results.get("dishesByType");
System.out.println("Short menu: " + shortMeny);
System.out.println("Total calories: " + totalCalories);
System.out.println("Most caloric dish: " + mostCaloricDish);
System.out.println("Dishes by type: " + dishesByType);
}
}