java lambda表达式
Stream流
概述
java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或数组进行链状流式的操作,对集合或数组操作
常用操作
创建流
单列集合: 集合对象
.stream()
List<T> tList = new ArrayList();
Stream<T> stream = tList.stream();
数组:Arrays.stream(数组)
或者使用Stream.of
来创建
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
双列集合:转换成单列集合后再创建
Map<String,Integer> map = new HashMap<>();
map.put("徐超",19);
map.put("黑子",17);
map.put("芜湖",18);
Stream<Map.Entry<String,Integer>> stream = map.entrySet().stream();
中间操作
filter
可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中
例如:
List<Author> authors = getAuthor();
authors.stream().filter(author->author.getName().length>1).forEach(author->System.out.printLn(author.getName()))
map
可以对流中的元素进行计算或转换
例如:
List<Author> authors = getAuthor();
authors.stream().map(author->author.getName()).forEach(System.out::printLn)
distinct
可以去除流中的重复元素。
注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的,所以需要重写equals方法
例如:
List<Author> authors = getAuthor();
authors.stream().distinct().forEach(author->System.out.printLn(author.getName()))
sorted
可以对流中的元素进行排序
注意:如果调用空参的sorted()方法,需要流中的元素实现了Comparable这个接口
例如:
List<Author> authors = getAuthor();
authors.stream().distinct().sorted().forEach(author->System.out.printLn(author.getName()))
limit
可以设置流的最大长度,超出的部分将被抛弃
例如:
List<Author> authors = getAuthor();
authors.stream().distinct().sorted().limit(2).forEach(author->System.out.printLn(author.getName()))
skip
跳过流中的前n个元素,返回剩下的元素
例如:
List<Author> authors = getAuthor();
authors.stream().distinct().sorted().skip(2).forEach(author->System.out.printLn(author.getName()))
flatMap
map只能把一个对象转换成另一个对象来作为流中的元素,而flatMap可以把一个对象转换成多个对象作为流中的元素。
例如:
List<Author> authors = getAuthor();
authors.stream().distinct().sorted().flatMap(author->author.getBooks().stream()).distinct().forEach(book->System.out.printLn(book.getName()))
authors.stream().distinct().sorted().flatMap(author->author.getBooks().stream()).distinct().flatMap(book->Arrays.stream(book.getCategory().split(","))).
forEach(System.out::printLn))
终结操作
forEach
对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作
count
可以用来获取当前流中元素的个数
max&min
可以用来获取流中的最值
collect
把当前流转换成一个集合
例如
List<Author> authors = getAuthor();
List<String> names = authors.stream().map(author->author.getName()).collect(Collectors.toList());
Set<Book> books= authors.stream().flatMap(author->author.getBooks().stream()).collect(Collectors.toSet());
Map<String,List<Book>> map = authors.stream().collect(Collectors.toMap(author->author.getName(),author->author.getBooks()));
anyMatch
用来判断是否有任意符合匹配条件的元素,结果为boolean类型
例如:
List<Author> authors = getAuthor();
Boolean bool = authors.stream().anyMatch(author->author.getAge()>=18);
allMatch
用来判断流中的元素是否都符合匹配条件,都符合返回true,否则返回false
例如:
List<Author> authors = getAuthor();
Boolean bool = authors.stream().allMatch(author->author.getAge()>=18);
noneMatch
用来判断流中的元素是否都不符合匹配条件,都不符合返回true,否则返回false
例如:
List<Author> authors = getAuthor();
Boolean bool = authors.stream().noneMatch(author->author.getAge()>=18);
findAny
获取流中的任意一个元素,该方法没有办法保证获取的一定是流中的第一个元素
例如:
List<Author> authors = getAuthor();
Optional<Author> optionalAuthor = authors.stream().filter(author->author.getAge()>=18).findAny();
optionalAuthor.ifPresent(author->System.out.printLn(author.getName()))
findFirst
获取流中的第一个元素
例如:
List<Author> authors = getAuthor();
Optional<Author> optionalAuthor = authors.stream().sorted((a,b)->a.getAge().compareTo(b.getAge())).findFirst();
optionalAuthor.ifPresent(author->System.out.printLn(author.getName()))
reduce
对流中的数据按照制定的计算方式计算出一个结果
reduce的作用是把stream中的元素组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算
List<Author> authors = getAuthor();
//两个参数 ideantity需要传入一个初始值
Optional<Integer> sum = authors.stream().map(author->author.getAge()).reduce(0,(result,age)->result+age);
Optional<Integer> Max = authors.stream().map(author->author.getAge()).reduce(Integer.MIN_VALUE,(result,element)->result<element?element:result);
Optional<Integer> Min = authors.stream().map(author->author.getAge()).reduce(Integer.MAX_VALUE,(result,element)->result>element?element:result);
注意事项
- 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
- 流是一次性的(一旦一个流对象经过一个终结操作后,这个流就不能再被使用)
- 不会影响原数据(我们在流中可以多数据做很多处理,但是正常情况下是不会影响原来集合中的元素的,这往往也是我们期望的)
Optional
概述
我们在编写代码的时候出现最多的就是空指针异常,所以很多情况下我们需要做各种非空判断
尤其是对象中的属性还是一个对象的情况下,这种判断会更多
而过多的判断语句会让我们的代码显得臃肿不堪
Optional可以避免这个问题
使用
创建对象
Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部,然后我们去使用Optional封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常
我们一般使用Opitonal的静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
你可能会觉得还要加一行代码来封装数据比较麻烦,但是如果改造下getAuthor()方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。
Optional<Author> authorOptional = getAuthor();
如果你确定一个对象不是空的时候可以使用Optional的静态方法of来把数据封装成Optional对象,of传入的参数必须不为null
Author author = getAuthor();
Optional<Author> authorOptional = Optional.of(author);
如果你确定一个对象是空的时候,需要把null封装成Opitonal对象返回,可以使用Optional的静态方法empty来封装
Optional.empty();
安全消费值
我们获取到一个Optional对象后肯定需要对其中的数据进行使用,这时候我们可以使用ifPresent方法来消费其中的值,这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。
Author author = getAuthor();
Optional<Author> authorOptional = Optional.of(author);
authorOptional.ifPresent(author -> System.out.printLn(author.getName()))
安全获取值
如果我们期望安全的获取值,不推荐使用get()方法,而是以下方法
- orElseGet
获取数据并且设置数据为空时的默认值,如果数据不为空就能获取到该数据,如果为空则根据你传入的参数来创建对象作为默认值返回
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
Author author1 = authorOptional.orElseGet(()-> new Author())
- orElseThrow
获取数据,如果数据不为空就能获取到该数据,如果为空则根据你传入的参数来创建异常抛出
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
try{
Author author = authorOptional.orElseThrow((Supplier<Throwable>)()-> new RuntimeException("author为空"));
}catch(Throwable throwable){
throwable.printStackTrace();
}
过滤
我们可以使用filter方法对数据进行过滤,如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
authorOptional.filter(author->author.getAge()>100).ifPresent(author -> System.out.printLn(author.getName()))
判断
我们可以使用isPresent()方法进行是否存在数据的判断,如果为空返回值为false,如果不为空,返回值为true,但是这种方式并不能体现Optional的好处,更推荐使用ifPresent()方法
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
if(authorOptional.isPresent()){
System.out.printLn(authorOptional.get().getName());
}
数据转换
Optional还提供了map可以让我们对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
authorOptional.map(author->author.getBooks()).ifPresent(books ->books.forEach(book->System.out.printLn(book.getName())))
函数式接口
概述
只有一个抽象方法的接口为函数接口
JDK的函数式接口都加上了@FunctionalInterface注解进行标识,但是无论是否加上该注解只要接口中只有一个抽象方法都是函数式接口
常见的函数式接口
- Consumer消费接口
根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1dsU1d0-1640685846621)(C:\Users\徐超\AppData\Roaming\Typora\typora-user-images\image-20211208163109223.png)]
- Function计算转换接口
根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数计算成转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwlBX7OJ-1640685846623)(C:\Users\徐超\AppData\Roaming\Typora\typora-user-images\image-20211208163508451.png)]
- Predicate判断接口
根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数条件判断,返回判断结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TETho8X0-1640685846624)(C:\Users\徐超\AppData\Roaming\Typora\typora-user-images\image-20211208163936514.png)]
- Supplier生产型接口
根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中创建对象,把创建好的对象返回
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pn7mzMyp-1640685846630)(C:\Users\徐超\AppData\Roaming\Typora\typora-user-images\image-20211208164125961.png)]
常用的默认方法
- and
我们在使用Predicate接口时候可能需要进行判断条件的拼接,而and方法相当于是使用&&来拼接两个判断条件
例如:
Stream<Author> authorStream = getAuthor().stream();
authorStream.filter(new Predicate<Author>(){
@Overridde
public boolean test(Author author){
return author.getAge()>17;
}
}.and(new Predicate<Author>(){
@Overridde
public boolean test(Author author){
return author.getName().length()>1;
}
})).forEach(author->System.out.printLn(author));
- or
我们在使用Predicate接口时候可能需要进行判断条件的拼接,而or方法相当于是使用||来拼接两个判断条件
Stream<Author> authorStream = getAuthor().stream();
authorStream.filter(new Predicate<Author>(){
@Overridde
public boolean test(Author author){
return author.getAge()>17;
}
}.or(new Predicate<Author>(){
@Overridde
public boolean test(Author author){
return author.getName().length()>1;
}
})).forEach(author->System.out.printLn(author));
- negate
Predicate接口中的方法,negate方法相当于是在判断添加前面加了个!表示取反
Stream<Author> authorStream = getAuthor().stream();
authorStream.filter(new Predicate<Author>(){
@Overridde
public boolean test(Author author){
return author.getAge()>17;
}
}.negate()).forEach(author->System.out.printLn(author));
方法引用
我们在使用lambda时,如果方法体中只有一个放的调用的话(包括构造方法),我们可以用方法引用进一步简化代码
推荐用法
我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么,我们只需要在写完lambda方法发现方法体只有一行代码,并且在方法的调用时使用快捷键尝试是否能够转换成方法引用即可。
基本格式
类名或者对象名::方法名
并行流
当流中有大量元素时,我们可以使用并行流去提高操作的效率,其实并行流就是把任务分配给多个线程去完成,如果我们自己去用代码实现的话会非常的复杂,并且要求对并发编程有足够的理解和认识,而如果我们使用Stream的话,只需要修改一个方法的调用就可以使用并行流来帮助我们实现,从而提高效率
例如:
Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
Integer sum = stream.paralle().peek(new Consumer<Integer>{
@Override
public void accept(Integer num){
System.out.printLn(num+Thread.currentThread().getName())
}
}).filter(num -> num >5).reduce((result,ele)->result+ele).get();
List<Author> authors = getAuthors();
authors.paralleStream().forEach(System.out::println);
新时间日期API
LocalDate、LocalTime、LocalDateTime类的实例是不可变对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
//LocalDate LocalTime LocalDateTime
LocalDateTime ldt = LocalDateTime.now();
LocalDateTime ldt2 = LocalDateTime.of(2019,12,21,18,57,59);
//在原来的年份加上两年
ldt.plusYear(2);
//在原来的月份减去两月
ldt.minusMonths(2);
//获取年份
ldt.getYear();
//获取月份值
ldt.getMonthValue();
//获取月份对象
ldt.getMonth();
//将dayofmonth设为一个新值,并返回一个新的LocalDateTime
LocalDateTime ldt3 = ldt.withDayOfMonth(10);
//Instant:时间戳(以 Unix 元年:1970年1月1日 00:00:00到某个时间之间的毫秒值)
Instant instant = Instant.now(); //默认获取UTC时区的时间
OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));//返回UTC偏移8个时区的时间
Long millis = instant.toEpochMilli();//返回时间戳
Instant instant2 = Instant.ofEpochSecond(1);//对Unix元年作加秒操作
//Duration: 计算两个时间之间的间隔
Instant start = Instant.now();
Thread.sleep(1000);
Instant end = Instant.now();
Duration duration = Duration.between(start,now);
duration.toMillis();
LocalTime start = LocalTime.now();
Thread.sleep(1000);
LocalTime end = LocalTime.now();
Duration duration = Duration.between(start,now);
duration.toMillis();
//Period:计算两个日期之间的间隔
LocalDate ld1 = LocalDate.of(2015,1,1);
LocalDate ld2 = LocalDate.now();
Period period = Period.between(start,now);
period.getYears();period.getMonths();period.getDays();
日期的操纵
-
TemporalAdjuster:时间矫正器。有时我们可能需要获取例如:将日期调整到"下个周日"等操作
-
TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现
获取下个周日
LocalDate nextSunday = LocalDate.now().with(
TemporalAdjusters.next(DayOfWeek.SUNDAY)
);
//自定义:下一个工作日
LocalDateTime nextWorkDay = LocalDate.now.with((l)->{
//接口传入的参数为Temporal,需要强转
LocalDateTime localDateTime = (LocalDateTime) l;
DayOfWeek dow = localDateTime.getDayOfWeek(); if(dow.equals(DayOfWeek.FRIDAY)){
return localDateTime.plusDays(3);
}else if(dow.equals(DayOfWeek.SATURDAY)){
return localDateTime.plusDays(2);
}else{
return localDateTime.plusDays(1);
}
});
- DateTimeFormatter:格式化时间/日期
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
String dateStr = ldt.format(dtf);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDate = dtf2.format(ldt);
LocalDateTime ldt2 = ldt.parse(strDate,dtf2);
时区的处理
java8加入了对时区的支持,带时区的时间分别为: ZonedDate、ZonedTime、ZonedDateTime。其中每个时区都对应着ID,地区ID都为"{区域}/{城市}"的格式
例如:Asia/Shanghai等
ZonedId:该类包含了所有的时区信息
getAvaillableZoneIds():可以获取所有时区信息
of(id):用指定的时区信息获取zoneId对象
Set<String> set = ZoneId.getAvaillableZoneIds()
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
ZonedDateTime zdt = LocalDateTime.now(ZoneId.of("Europe/Tallinn")).atZone(ZoneId.of("Europe/Tallinn"));
MM月dd日 HH:mm:ss");
String strDate = dtf2.format(ldt);
LocalDateTime ldt2 = ldt.parse(strDate,dtf2);
时区的处理
java8加入了对时区的支持,带时区的时间分别为: ZonedDate、ZonedTime、ZonedDateTime。其中每个时区都对应着ID,地区ID都为"{区域}/{城市}"的格式
例如:Asia/Shanghai等
ZonedId:该类包含了所有的时区信息
getAvaillableZoneIds():可以获取所有时区信息
of(id):用指定的时区信息获取zoneId对象
```java
Set<String> set = ZoneId.getAvaillableZoneIds()
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
ZonedDateTime zdt = LocalDateTime.now(ZoneId.of("Europe/Tallinn")).atZone(ZoneId.of("Europe/Tallinn"));