一、Lambda表达式
什么是Lambda表达式
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。
为什么要使用?
lambda是一个匿名函数,我们可以把lambda理解为一个可以传递的代码(将代码像数据一样传递),可以写出更简洁更灵活的代码,增强了代码的可读性。
lambda 的结构
-
一个 Lambda 表达式可以有零个或多个参数
-
参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
-
所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
-
空圆括号代表参数集为空。例如:() -> 42
-
当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
-
Lambda 表达式的主体可包含零条或多条语句
-
如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
-
如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用” ,也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
(a) 方法引用
三种表现形式:
对象::实例方法名
例如:Consumer con = System.out::println;
类::静态方法名
例如:BiFunction<Integer, Integer, Integer> biFun = Integer::compare;
类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用者,第二个参数是实例方法的参数时可用)
例如:BiFunction<String, String, Boolean> fun = String::equals;
(b)构造器引用
格式:ClassName::new
例如:Function<Integer, Employee> fun = Employee::new;
©数组引用
格式:Type[]::new
例如:Function<Integer, String[]> fun = String[]::new;
/**
* ArraysList
*/
//传统方式
Integer[] numArr= {1,6,3,2,4,5,7};
List<Integer> list = Arrays.asList(numArr);
for (Integer num:numArr){
System.out.println("===>"+num);
}
System.out.println("-----------------------");
//lambda方式
list.forEach((num)->{System.out.println("===>"+num); });
二、Stream流
定义:
流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
对stream的操作可以分为两类,中间操作(intermediate operations)和结束操作(terminal operations):
中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream。
结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。
Stream操作的三个步骤
创建stream:一个数据源(如:集合、数组),获取一个流
中间操作(过滤、map):一个中间操作链,对数据源的数据进行处理
终止操作:一个终止操作,执行中间操作链,并产生结果
stream的创建:
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();
// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);
// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");
Stream的中间操作:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
Stream的终止操作:
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
三、新的日期API
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。
新的日期API好处:
-
之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum
-
java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。
-
java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍
-
新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对
LocalDate/LocalTime/LocalDateTime
LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。
now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plus或minus方法可以增减日期或时间信息;
TemporalAdjusters
这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
DateTimeFormatter
以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:
// 当前时间
LocalDate now = LocalDate.now();
System.out.println("当前时间:" + now);
// 前一天时间
LocalDate yesterday = now.minusDays(1);
System.out.println("昨天:" + yesterday);
// 上个月
LocalDate lastMonth = now.minusMonths(1);
System.out.println("上个月:" + lastMonth);
// 上一周
LocalDate lastWeek = now.minusWeeks(1);
System.out.println("上一周:" + lastWeek);
// 一周后
LocalDate nextWeek = now.plusWeeks(1);
System.out.println("一周后:" + nextWeek);
// 指定日期
LocalDate ofDate = LocalDate.of(2020, 4, 7);
System.out.println("指定日期:" + ofDate);
// 转换日期对象
LocalDate parseDate = LocalDate.parse("2020-04-09");
System.out.println("转换日期对象:" + parseDate);
// 取本月第1天:
LocalDate firstDayOfThisMonth = now.with(TemporalAdjusters.firstDayOfMonth());
LocalDate firstDayOfThisMonth1 = now.withDayOfMonth(1);
System.out.println("本月第一天:" + firstDayOfThisMonth);
System.out.println("本月第一天1:" + firstDayOfThisMonth1);
// 取本月第2天:
LocalDate secondDayOfThisMonth = now.withDayOfMonth(2);
System.out.println("本月第二天:" + secondDayOfThisMonth);
// 取本月最后一天,再也不用计算是28,29,30还是31:
LocalDate lastDayOfThisMonth = now.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月最后一天:" + lastDayOfThisMonth);
// 获取当前月份,得到结果不用加1
int month = now.getMonthValue();
System.out.println("当前月份:" + month);
int dayOfYear = now.getDayOfYear();
System.out.println("2020年已经度过的天数:" + dayOfYear);
LocalDateTime nowTime = LocalDateTime.now();
String formatDate = DateTimeFormatter.ISO_LOCAL_DATE.format(nowTime);
System.out.println("formatDate:" + formatDate);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
String formatDateTime = formatter.format(nowTime);
System.out.println("formatDateTime:" + formatDateTime);
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("YYYY/MM/dd HH:mm:ss");
String formatDateTime1 = formatter1.format(nowTime);
System.out.println("formatDateTime1:" + formatDateTime1);
运行结果: