Java8的优雅写法: 流与λ

思路:具体原理参加《Java8实战》的讲解。本文主要是为了工作方便,列出工作中最常用的几种写法便于迅速查询以及仿写。

行为参数化/函数作为入参の实例

行为参数化的思想,即代码段(通常是处理方法/策略)可以作为函数入参。
实际上就是策略模式,将策略模式提取为接口而已。

// 只对Apple这个类适用的版本
public interface ApplePredicate{
    
    
	boolean test (Apple apple);
}
// 某种策略
public class AppleGreenColorPredicate implements ApplePredicate{
    
    
	public boolean test(Apple apple) {
    
    
		return "green".equals(apple.getColor());
	}
}

public static List<Apple> filterApples(List<Apple> inventory,
	ApplePredicate p) {
    
    
	List<Apple> result = new ArrayList<>();
	for(Apple apple: inventory){
    
    
		if(p.test(apple)){
    
    
			result.add(apple);
		}
	}
	return result;
}

// 对所有类型都适用的版本,实际上就是Java8中java.util.function的函数式接口
public interface Predicate<T>{
    
    
	boolean test(T t);
}
// 该函数式接口用法:
public static <T> List<T> filter(List<T> list, Predicate<T> p){
    
    
	List<T> result = new ArrayList<>();
	for(T e: list){
    
    
		if(p.test(e)){
    
    
			result.add(e);
		}
	}
	return result;
}

// 写法1:有效但是low,需要真的有类去继承这个函数式的接口。
// 一般是固化下来的某种策略
List<Apple> redAndHeavyApples =
	filterApples(inventory, new AppleRedAndHeavyPredicate());
// 写法2:使用λ表达式,这里λ表达式代替了匿名类
List<Apple> result = 
	filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
// 写法3:更优雅的固化形式
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

在这里插入图片描述

Java8自带的函数式接口

函数式接口只包含一个抽象方法,默认方法是种非抽象方法,可以包含多种默认方法。

Predicate:断言

@FunctionalInterface
public interface Predicate<T>{
    
    
	boolean test(T t);
}
// Eg:
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    
    
	List<T> results = new ArrayList<>();
	for(T s: list){
    
    
		if(p.test(s)){
    
    
			results.add(s);
		}
	}
	return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

Consumer:消费者

@FunctionalInterface
public interface Consumer<T>{
    
    
	void accept(T t);
}
// Eg:
public static <T> void forEach(List<T> list, Consumer<T> c){
    
    
	for(T i: list){
    
    
		c.accept(i);
	}
}
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));

Function:T-》R

@FunctionalInterface
public interface Function<T, R>{
    
    
	R apply(T t);
}
public static <T, R> List<R> map(List<T> list,
	Function<T, R> f) {
    
    
	List<R> result = new ArrayList<>();
	for(T s: list){
    
    
		result.add(f.apply(s));
	}
	return result;
}
// [7, 2, 6]
List<Integer> l = map(Arrays.asList("lambdas","in","action"),
	(String s) -> s.length());

Supplier:

UnaryOperator:

BinaryOperator:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

函数式接口复合写法

// 函数复合
Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline
= addHeader.andThen(Letter::checkSpelling)
.andThen(Letter::addFooter);
// 谓词复合
// negate、 and和or
Predicate<Apple> notRedApple = redApple.negate();
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);

集合filter

推荐写法:

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);
// eg:
filter(inventory, (Apple a) -> a.getWeight() > 150 );

List<Apple> heavyApples =
	inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
	.collect(toList());

自定义筛选接口(类似策略模式):

public interface Predicate<T> {
    
    
	boolean test(T t);
}
static List<Apple> filterApples(List<Apple> inventory,
Predicate<Apple> p) {
    
    
	List<Apple> result = new ArrayList<>();
	for (Apple apple: inventory){
    
    
		if (p.test(apple)) {
    
    
			result.add(apple);
		}
	}
	return result;
}
// 这是Java8自带的行为参数化
filterApples(inventory, Apple::isGreenApple);
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()) );

Map分组

使用groupingBy

Map<Currency, List<Transaction>> transactionsByCurrencies =
	transactions.stream()
	.filter((Transaction t) -> t.getPrice() > 1000)
	.collect(groupingBy(Transaction::getCurrency));

排序/比较器

// λ写法,生成一个排过序的List
List sortList = inventory.sort(
	(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 方法引用版
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
List sortList = inventory.sort(c);
// 逆序
inventory.sort(comparing(Apple::getWeight).reversed());
// 多个比较器
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));

容器转换/聚合/转为多种Map

参见文章:Stream API
主要是Collector中的各种静态方法

Student s1 = new Student("aa", 10,1);
Student s2 = new Student("bb", 20,2);
Student s3 = new Student("cc", 10,3);
List<Student> list = Arrays.asList(s1, s2, s3);
 
//装成list
List<Integer> ageList = 
	list.stream().map(Student::getAge)
	.collect(Collectors.toList()); // [10, 20, 10]
 
//转成set
Set<Integer> ageSet = 
	list.stream().map(Student::getAge)
	.collect(Collectors.toSet()); // [20, 10]
 
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = 
	list.stream()
	.collect(Collectors.toMap(Student::getName, Student::getAge));
	 // {cc=10, bb=20, aa=10}
 
//字符串分隔符连接
String joinName = 
	list.stream().map(Student::getName)
	.collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
 
//聚合操作
//1.学生总数
Long count = list.stream().collect(Collectors.counting()); // 3
// Long count = list.stream().count();
//2.最大年龄 (最小的minBy同理),接收一个Comparator参数(可以自定义)用以进行比较
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream()
	.collect(maxBy(dishCaloriesComparator));

Integer maxAge = list.stream().map(Student::getAge)
.collect(Collectors.maxBy(Integer::compare)).get(); // 20
//3.所有人的年龄
Integer sumAge = list.stream()
.collect(Collectors.summingInt(Student::getAge)); // 40
//4.平均年龄
Double averageAge = list.stream()
.collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334
// 包含上述所有方法
DoubleSummaryStatistics statistics = 
	list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() 
+ ",max:" + statistics.getMax() + ",sum:" + statistics.getSum()
+ ",average:" + statistics.getAverage());
 
//分组
Map<Integer, List<Student>> ageMap = 
	list.stream().collect(Collectors.groupingBy(Student::getAge));
//另一个分组例子:
public enum CaloricLevel {
    
     DIET, NORMAL, FAT }
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;
		}));
//多重分组,先根据类型分再根据年龄分,groupingBy第二个参数如果不选择默认返回的是list
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = 
	list.stream()
	.collect(Collectors.groupingBy(Student::getType,Collectors.groupingBy(Student::getAge)));

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;} )));
// 第二个参数选择为某个类
Map<Dish.Type, Dish> mostCaloricByType =
	menu.stream()
		.collect(groupingBy(Dish::getType,
				collectingAndThen(
					maxBy(comparingInt(Dish::getCalories)),
					Optional::get)));
// 第二个参数选择为integer
Map<Dish.Type, Integer> totalCaloriesByType =
menu.stream().collect(groupingBy(Dish::getType,
summingInt(Dish::getCalories)));
// 第二个参数选择映射为set
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
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() )));

//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁,partitioningBy也可以接收两个参数
// partitioningBy需要一个谓词,也就是返回一个布尔值的函数。
Map<Boolean, List<Student>> partMap = 
list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegetarian));
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));
 
//规约
Integer allAge = list.stream().map(Student::getAge)
.collect(Collectors.reducing(Integer::sum)).get(); //40
int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();

新线程的优雅写法

// λ写法,新定义一个thread
Thread t = new Thread(() -> System.out.println("Hello world"));

参考

lambda表达式

λ表达式语法详细参见:《Java8实战》第三章
这里做精炼总结:

lambda基础

3.1节:通用写法

// parameter代表入参,return返回值代表了出参
// 行为参数化的部分有函数名,与return返回值共同构成了函数的签名
// 需要与函数式接口中API的签名保持一致
(parameters) -> expression
(parameters) -> {
    
     statements; }

3.2节 + 3.3节:λ表达式的应用场所与如何用

  1. 只能用于函数式接口,如3.1,需要满足函数签名
  2. 环绕执行模式
    实际上就是策略模式的写法,只是多出来一步抽取函数式接口的过程

3.4节:函数式接口使用,参见文中函数式接口
3.5节:记住一点:对值封闭,而不是对变量封闭。如果涉及到局部变量,要求是隐式final的。

lambda表达式与方法引用互转

3.6节:lambda表达式-》方法引用
三种类型的转换方法:
在这里插入图片描述

Stream

参见文章:Stream API

猜你喜欢

转载自blog.csdn.net/weixin_38370441/article/details/112177997