工作中常用到的Java8特性
Lambda表达式
lambda表达式是是一个匿名函数,即没有函数名的函数
举个我们平时创建线程的栗子
//Runnable实现类
public class RunnableImpl implements Runnable {
@Override
public void run() {
logger.info("Runnable实现类创建并启动一个新的线程");
}
}
//采用传统的Runnable对象作为参数新建并启动一个线程
new Thread(new RunnableImpl()).start();
//采用匿名内部类的方式新建并启动一个线程
new Thread(new Runnable() {
@Override
public void run() {
logger.info("匿名内部类方式创建并启动一个新的线程");
}
}).start();
//对匿名内部类进行简化,写成lambda表达式
new Thread(()-> logger.info("lambda表达式创建并启动一个新的线程")).start();
优点
:lambda表达式相比传统的Runnable对象作为参数传递给Thread类方式创建线程的方式,代码更简洁
方法引用
方法引用可以将一个方法封装成一个变量。::双冒号是方法引用的符号
//方法引用,将字符串100转换成Integer类型
Function<String, Integer> function = Integer::parseInt;
Integer IntegerResult= function.apply("100");
方法引用的返回值类型是函数式接口
被引用方法的参数个数,类型,返回值类型需要和函数式接口中方法的声明一致,只要满足这个要求,可以返回任意类型的函数式接口
//Function函数式接口,apply方法接收T类型的变量,返回R类型的结果
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Integer.paseInt()方法接收字符串类型参数,返回Integer类型的结果
//比较两个int值大小,下面两种返回值类型均可
Comparator<Integer> compare = Integer::compare;
int compare1 = compare.compare(1, 2);
IntBinaryOperator compare = Integer::compare;
int compare1 = compare.applyAsInt(1, 2);
在java.util.function包下有很多函数式接口
自定义一个函数式接口
//函数式接口的run方法会接收T和S类型两个参数,返回R类型的结果
@FunctionalInterface
public interface KiteFunction<T,R,S> {
R run(T t, S s);
}
自定义一个和KiteFunction的run方法队对应的方法
//dateFormat方法的功能是按照指定的格式,把日期进行格式化,返回字符串的日期
public class TimeConverse {
public static String dateFormat(LocalDateTime localDateTime,String pattern){
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
String dateStr = localDateTime.format(dateTimeFormatter);
return dateStr;
}
}
KiteFunction<LocalDateTime, String, String> dateFormat = TimeConverse::dateFormat;
String dateStr = dateFormat.run(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
System.out.println(dateStr);
如果TimeConverse的dateFormat方法只会被调用一次,我们也可以直接将其在kiteFunction接口的run方法中实现(原理类似于Runnable接口创建启动一个新线程),这种是匿名内部类的方式
String dateStr2 = new KiteFunction<LocalDateTime, String, String>() {
@Override
public String run(LocalDateTime localDateTime, String pattern) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
return localDateTime.format(dateTimeFormatter);
}
}.run(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
上面匿名内部类可以使用lambda表达式的形式进行替代
String dateStr2 = ((KiteFunction<LocalDateTime, String, String>) (localDateTime, pattern) -> {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
return localDateTime.format(dateTimeFormatter);
}).run(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss");
Stream流
Stream流中大量运用了方法引用和lambda表达式,并提供了很多简单易用封装好的工具API,使用这些API我们可以快速高效地对复杂数据进行处理
- flatMap 对多维数据进行扁平化处理
- peek函数类似于foreach函数,区别是foreach函数执行后没有返回结果,而peek返回了Stream,但是如果执行peek函数后,没有对数据进行收集操作,peek函数内部的方法不会被执行
- reduce函数,对数据进行累加操作
//reduce方法定义
Optional<T> reduce(BinaryOperator<T> accumulator);
//实际用法,对List<Integer>中的元素进行累加求和
Integer result = stream.reduce((x, y) -> x + y).get();
x是Stream中的第一个元素,y是第二个元素
//reduce重载方法定义
T reduce(T identity, BinaryOperator<T> accumulator);
//实际用户,累加求和时先赋一个初始值
stream.reduce(100, (x, y) -> x + y)
累加时,identity会作为第一个参数
Stream流程常用方法
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)
);
//1、找到2011年所有的交易,并对交易值进行倒叙排序
List<Transaction> collect = transactions.stream().filter(t -> t.getYear() == 2011).sorted(Comparator.comparing(Transaction::getValue).reversed()).collect(Collectors.toList());
System.out.println(collect);
//2、交易员都在哪些不同的城市工作过
transactions.stream().map(Transaction::getTrader).map(Trader::getCity).distinct().forEach(e->System.out.println(e));
//3、判断有无交易员在米兰城市待过
boolean isExit = transactions.stream().map(Transaction::getTrader).map(Trader::getCity).anyMatch(e -> "Milan".equals(e));
System.out.println(isExit);
//4、打印生活在剑桥的交易员的所有交易金额
long sum = transactions.stream().filter(transaction -> "Cambridge".equals(transaction.getTrader().getCity())).collect(Collectors.summarizingInt(Transaction::getValue)).getSum();
System.out.println("剑桥总交易金额"+sum);
//5、所有的交易中,交易值最大的是
Transaction transaction = transactions.stream().max(Comparator.comparing(Transaction::getValue)).get();
System.out.println(transaction);
总结
我们平时说的函数式编程,
函数
即方法引用,式
即Lambda表达式 Stream流可以提高我们的编程效率,但是也需要合理的使用StreamAPI,涉及到复杂的业务逻辑时需要写好注释
转载自:https://juejin.cn/post/7036171562659577887