java8文档_java 8 的日常开发总结

a119e8bd38c0516992c097a5424fd9fd.png

这篇文章主要记录了 lambda 表达式的使用 ,Stream 流的常用方法总结,Java 8 的时间常用总结,Optional 容器使用, lambda 让编程更优雅,Stream 流让数据起来更简单, java 8 的时间让时间处理不在头痛,Optional 容器类用来存放数据,可以方便我们判断容器中是否有元素,如果有元素还可以执行一些自定义逻辑等,可以很好的取代 if (value !=null){} 的形式。

理解 lambda 表达式

  • 什么是 lambda 表达式?

一个简单的函数体代表了一个实体类,一个操作符 -> , 操作符左侧是函数的参数,右侧是函数体。

  • 什么时候可以用 lambda 表达式?

有这样的一个接口,并且接口只有一个抽象方法,那么这个接口的实例化可以通过 lambda 表达式的形式呈现出来。

  • 函数式接口

一个只有一个抽象方法的接口,在定义的时候可以在接口上方添加 @FunctionalInterface 进行检查校验,该注解会检查该接口是否符合标准。

  • java.util.function 包中几个重要的函数式接口
Consumer<T> 接口,消费者接口,接收一个参数,没有返回值。
Supplier<T> 接口,供给型接口,没有参数,有返回值。
Function <T,R>接口 ,函数接口,接收一个参数,有返回值。
Predicate<T> 接口,断言接口,接收一个参数,返回值是 boolean 值。

该包下的其它接口接本上从上述四个接口演变而成。

  • 进一步简化 lambda 的表达式,方法引用,构造器引用,数组引用,均使用操作符 :: 简化 lambda 的表达式。使用各种引用简化 lambda 表达式的核心是函数式接口中定义的抽象方法有现成的具体实现。
  • 方法引用的几点理解:

函数式接口有返回值和没有返回值分析(针对实例对像::方法名 类::静态方法名的简写形式的分析):

1. 如果没有返回值

类中方法参数同函数式接口中定义的方法参数吻合即可(类型和个数相同),不需要关心类中方法是否有返回值。

2. 如果有返回值

类中的方法参数和返回值同函数式接口中定义的方法参数和返回值必须相同。

针对(类::方法名)形式的 lambda 分析如下:

分析函数式接口中的抽象方法构成:

1.如果第一个参数是引用类数据类型。

2.其他参数的类型和个数在第一个参数类中有明确的方法对应,就可以使用类::方法名形式的 lambda 表达式代替

3.如果函数式接口有返回值,第一个参数类中不仅要匹配参数类型和个数而且返回值类型也要求一致。举例如下:

Function<User, String> function = User::getUserName;

上例所示函数式接口 Function 中的抽象方法中仅有一个参数 User ,返回值是 String 类型,User 中的 getUserName() 方法中没有参数,返回值是 String 类型,函数式接口中的抽象方法除第一个参数 User 外没有其他参数,这样就可以使用 User::getUserName 的形式来表达。

  • 构造器引用

当函数式接口的返回值是一个实体类,可以用构造器的形式展现 className::new

函数式接口的抽象方法的参数类型和个数必须和构造器的参数的类型和个数一致

  • 数组引用

当函数式接口的返回值是一个数组,可以用数组引用的形式展现 Type[]::new

强大的 stream

  • 什么是流(Stream) ?

流是数据渠道,流是用于操作数据源(集合,数组等)所生成的元素序列。

集合讲的是数据,流讲的是筛选和计算。

并行流:就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流

顺序流:流没有进行拆分的处理,在单个线程中处理数据

流可以通过 sequential() 和 parallel()方法把流处理成顺序流或者是并行流。

  • 怎么生成流

集合生成流,Collection 接口提供了两个生成集合的方法stream(),parallelStream()

分别生成顺序流和并行流。

数组操作类 Arrays 类的 stream(T[] array), Arrays 类也可以操作 int / long / double 的数组,把数组转化成流。

Stream 类的静态方法 of(T… values) 生成指定元素流 ,静态方法generate(Supplier<T> s) 创建一个无限流,Stream 类的 iterate(final T seed, final UnaryOperator<T> f),在初始值 seed 的基础上,经过 f 的处理操作,源源不断的的生成流。

  • 流的中间操作

一个中间操作链,对数据源的数据进行处理。

中间操作常用的几个方法:

1. filter(Predicate <? super T> predicate) 筛选流中符合条件的元素

2. skip(long n) 返回一个扔掉了前 n 个元素的一个新流

3. limit(long maxSize) 限制流的最大元素的数量

4. distinct() 根据流中的元素定义的 hashCode 和equal 方法除去重复的元素

5. map(Function<? super T, ? extends R> mapper) 接收一个函数作为参数,该函数会应用到流中的每一个元素上,最终的结果是形成一个新的流,这个新流中元素的类型是函数的返回值类型 。返回整型数据的 mapToInt(ToIntFunction<? super T> mapper) : 同 map 的方法类似,返回整型数据的一个流。mapToLong 方法: 返回 long 类型的一个流。 mapToDouble 返回 double 类型的一个流

6. flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 分析一下该函数的参数,该函数接受一个 Function 函数作为参数,该函数会应用到流中的每一个元素上,每个元素经过操作形成一个个新的流,每个元素形成的一个个新流最终组成一个大的流。

7. sorted() 自然排序,流中的元素需要实现 Comparable 的接口,根据 compare 这个方法进行排序。

8. sorted(Comparator<? super T> comparator) 这个是自定义规则的排序,需要实现 compareTo 这个方法。

  • 流的终止操作

1. anyMatch(Predicate<? super T> predicate) 检查流中是否有符合条件的元素,只要有符合条件的就返回 true ,否则 false

2. allMatch(Predicate<? super T> predicate) 检查流中是否所有元素都符合条件,如果都符合,就返回 true, 否则 false

3. noneMatch(Predicate<? super T> predicate) 这个效果跟 allMatch 相反,检查流中所元素,如果都不符合条件,就会返回 true ,否则 false

4. count () 方法同集合中的 size () 方法的效果一样,统计流中元素的个数

5. findFirst() 返回流中的第一个元素,元素保存在 Optional (Optional 下文会提到)中

6. findAny() 返回流中的的任何一个元素,用法同上面的 findFirst 类似

7. min(Comparator<? super T> comparator) 根据自定义的排序规则,返回一个 Optional 容器,Option 中保存着最小的元素值

8. max() 根据自定义的排序规则,返回一个 Optional 容器,Option 中保存着最大的元素值

9. forEach(Consumer<? super T> action) 迭代流中的元素,同集合中的 forEach 方法一样

下面是关于 reduce 方法的一个介绍,可以参考文档理解[reduce 的三种重载]

reduce 的理解

10. reduce(T identity, BinaryOperator<T> accumulator) , 该 reduce 函数有两个参数 identity 是一个初始值会累积到 accumulator 计算的结果上 ,accumulator 是一个二元操作的函数式接口,可以定义一个运算规则去统计流中所有元素的一个总体情况,例如统计所有元素的总和。

11. reduce(BinaryOperator<T> accumulator) 同上面描述的效果类似,区别是该方法没有给定一个初始的累加值,而且返回值是 Optional ,最终值还需要从 Optional 中获取

12. reduce(U identity,BiFunction<U, ? super T, U> accumulator ,BinaryOperator<U> combiner) 操作流元素,流元素的数据是 T 类型,但是使用该方法方法的返回值类型可以是其他类型 U , 分析该方法后可以发现该方法总共有三个参数,第一个参数是该方法返回值类型的一个初始值,第二个参数 accumulator 是对流中元素的一个叠加操作,如果该流是一个顺序流那么第三个参数没有作用,同两个参数的 reduce 方法是使用效果类似,如果是并行流,那么第三个参数将会起作用,合并并行流产生一个总结果。

reduce 的方法经常用来统计计算一个总结果,尤其是 reduce 中带一个参数和两个参数的使用的频率会很高。

map - reduce 的组合在处理流数据的时候非常好用,通常的做法是通过 map 的方法处理成一个新流,然后通过 reduce 方法对新流做一个数据处理。

  • 流中的 collect 方法(流的终止操作)

调用流中的 collect(Collector<? super T, A, R> collector) 方法通常可以实现更多的需求,很强大,分析方法的的参数 collector 是一个接口,通常通过调用 Collectors 这个工具类的方法去生成不同的 Collector 实现不同的需求,现列举如下:

1. collect(Collectors.toList) 把流中的元素收集到 list 集合中,返回值是一个 List 集合

2. collect(Collectors.toSet) 把流中的元素收集到 set 集合中,返回值是一个 Set 结合

3. collect(Collectors.toCollection(ArrayList::new))可以生成一个指定类型的集合,该例子是生成 List 集合,也可以通过 Collectors.toCollection(HashSet::new) 生成一个 Set 集合

4. collect(Collectors.toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper) 该方法返回一个 HashMap ,keyMapper 是生成 key 的一个函数,valueMapper是生成 value 的一个函数,使用该方法需要注意 key 不可以有重复值

5. collect(Collectors.toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)该方法返回一个 HashMap ,前面两个参数同上面相同,第三个参数是在 key 出现重复值的情况下,对 value 的一个操作。

6. collect(Collectors.toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction,Supplier<M> mapSupplier)该方法返回一个指定的 map ,具体是什么类型的 map ,需要看第四个参数 mapSupplier 提供的是什么类型的一个 map 。

7. collect(Collectors.counting()) 返回流中的一个总数目

8. collect(Collectors. summingInt(ToIntFunction<? super T> mapper)) 处理流中的的元素并返回一个整型值,对比该方法还有处理 long 和 double 数据的方法

9. collect(Collectors.averagingInt(ToIntFunction<? super T> mapper) 处理流中的元素,并返回一个平均值,对比该方法还有处理 long 和 double 数据的方法

10. collect(Collectors.summarizingInt(ToIntFunction<? super T> mapper)) 处理流中的元素,返回值是一个 IntSummaryStatistics 类型的数据,通过该类可以获取流中的最大值,最小值,总数等,相应的还有 long 和 double 数据的方法

11. collect(Collectors.joining()) 该方法的返回值是一个 String 默认 返回所有的元素组成一个字符串。类似的还有collect(Collectors.joining(CharSequence delimiter) 指定一个连接的分割符, collect(Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) 指定分隔符,前缀和后缀

12. collect(Collectors.maxBy(Comparator<? super T> comparator))自定义一个比较器,根据自定义的规则返回一个 Optional 容器 , 通过返回 Optional ,可以得到一个最大值。 类似的还有获取做小值的一个方法。

13. collect(Collectors.reducing(BinaryOperator<T> op)) 统计元素的累加值,可以参考 Stream 类提供的一个参数的 reduce 方法使用相同

collect(Collectors.reducing(T identity, BinaryOperator<T> op)) 指定初始值的累加,参考 Stream 类提供的两个参数的 reduce 方法使用相同.

collect(Collectors.reducing(U identity,Function<? super T, ? extends U> mapper,BinaryOperator<U> op)) 前面两个返回值都是流中的元素的类型,这个方法可以返回一个其他的类型,参数的第一个值是要返回数据类型的一个初始值,第二个参数是通过流中元素获取需要的数据类型,第三个参数是针对返回的类型做一个二元操作,比如有一个 Person 类和 Person 类的集合 persons ,类中有属性 int age ,可以通过该方法获取所有人的年龄,和计算年龄的总和: int sum = persons.stream().collect(Collectors.reducing(0,p->p.getAge(),(x,y)->x+y));

14. collect(Collectors.collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)) 分析如下, 第一个参数是 Collector 类型,通过该参数可以对流中的元素进行操作,然后有一个返回值是 R 类型,操作完以后,第二个参数在第一个参数的返回值 R 上做进一步的函数操作:比如有一个 Person 类和 Person 类的集合 persons ,类中有属性 int age ,可以通过该方法获取所有人的年龄,和计算年龄的总和,计算出总和后,把结果转换类型成 String 类型: String ageSum = persons.stream().collect(Collectors.reducing(0,p->p.getAge(),(x,y)->x+y),sum ->String.valueOf(sum));

15. collect(Collectors.groupingBy(Function<? super T, ? extends K> classifier)) 这个一个分组的方法,把流中的元素进行一个分组操作,该方法返回的是一个 Map<K,List<T>> 该返回值的 map 的 key 是分组的条件,value 是一个 list 集合,集合中包含了该组的所有元素。

collect(Collectors.groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)) 把流中的元素进行一个分组操作,返回值是一个 Map<K, D> ,该 map 的 key 第一个参数 classifier 的返回值,value 是 downstream 操作流以后的一个返回值。

16. collect(Collectors.partitioningBy(Predicate<? super T> predicate)) 把流中元素分成两组,根据参数 predicate 的条件,true 的一组,false 的一组,该函数的返回值类型是 Map<Boolean, List<T>> ,key 是 true 或者 false , value 是该一个包含流中元素的一个 List 集合。

collect(Collectors.partitioningBy(Predicate<? super T> predicate,Collector<? super T, A, D> downstream)) 该方法返回值是一个 Map<Boolean, D>> 该返回值的 key 是符合第一个参数 predicate 条件的 true , 或者 false , value 该方法的第二个参数 downstream 操作流以后的一个返回值。


java 8 的时间分析(抛弃 java 之前的时间类,拥抱 java.time 包)

Java 8 提供的时间是非常还用的 ,Java 8 提供的时间 API 也指出在实体类中使用 java 8 的 LocalDateTime / LocalDate /LocalTime 更妥帖,例如表示生日可以使用 LocalDate 表示等。

java 提供的日期时间都类似,通过方法的一些前缀可以大致知道方法的功能,下面分析如下:

of : 通常用于实例化一个时间类,静态工厂方法

parse: 通常用来 String 到日期时间的一个转化

format: 通常用来日期时间到 String 的一个转化

get : 通常用来获取具体的某个时间值

is : 该前缀的方法通常返回 boolean 值,检查是否符合条件

with : 用来设置值,相当于 setter 方法

plus : 用来在原有的日期时间上添加一个数量

minus: 用来在原有的日期时间上减少一个数量

to : 用来把一个日期时间类转换成另一个日期时间类

at : 把当前对象组合另一对象形成一个新的对象

  • 日期时间类 LocalDateTime

Java 8 的中关于日期和时间很好掌握,各个日期时间类的初始化和方法都类似,因此掌握一个基本上就可以触类旁通。下面详细分析一下 LocalDateTime 这个类各个方法的使用:

1. now() : 获取当前的时间,例如:

LocalDateNow now =LocalDateTime.now(),

该方法还有俩个重载方法:

now(Clock clock): 根据时钟去获取 LocalDate 的实例

举例如下:

Clock clock = Clock.systemDefaultZone();
LocalDateTime dateTime = LocalDateTime.now(clock);

now(ZoneId zone): 获取指定时区的日期时间

举例如下,获取新加坡的日期时间:

ZoneId zone = ZoneId.of("Asia/Singapore");
LocalDateTime dateTime = LocalDateTime.now(zone));

2. of(int year, int month, int dayOfMonth, int hour, int minute)

of 方法用来指定具体的年月日时间来实例化 LocalDateTime , 有几个重载方法使用类似,现举例如下,指定日期是 19 年 9 月 20 号 3 点 17 分:

LocalDateTime dateTime = LocalDaTime.of(2019,9,20,3,17);

3. plusYears(long years) : 在原来 LocalDateTime 的基础上增加年数,还有增加月份,天数,小时,分钟,秒数等

4. withYear(int year): 设置 LocalDateTime 的年份,还有相应的月份,天数,小时,分钟,秒数相对应的 with 方法用法类似。

下面介绍一下另外的两个 with 方法:

with(TemporalAdjuster adjuster):该方法非静态方法,需要实体类调用,方法中的参数通常使用 TemporalAdjusters 类中的方法去获取 TemporalAdjuster 的实例。 TemporalAdjusters 提供了很多实用的方法,方便我们获取指定的日期时间现在举例如下:

LocalDateTime dateTime = LocalDateTime.now();

//下面是获取本月的第一个星期二是几号

LocalDateTime dateTime1 = dateTime.with(TemporalAdjusters.dayOfWeekInMonth(2,DayOfWeek.TUESDAY))

with(TemporalField field, long newValue): 该方法可以设置指定的年/天/小时/秒数等,TemporalField 通常用 ChronoField 去指定。

举例如下,在当前的日期上加三天后的日期:

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime1 = dateTime1.with(ChronoField.DAY_OF_MONTH, 3);


5. parse(CharSequence text, DateTimeFormatter formatter)

该方法的作用是将指定的字符串转换成 LocalDateTime

举例如下:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(06-09-2019 03:18:17, dateTimeFormatter);

6. format(DateTimeFormatter formatter) 把时间根据转换形式,转换成 String 类

7. isBefore(ChronoLocalDateTime<?> other) 检查当前日期时间是否在给定的时间之前

isAfter(ChronoLocalDateTime<?> other) 检查当前日期时间是否在给定的时间之后

isEqual(ChronoLocalDateTime<?> other) 检查当前日期时间是否等于当前的时间

isSupported(TemporalField field) :检查当前日期时间是否支持给定的时间字段

isSupported(TemporalUnit unit) : 检查当前的日期时间收否支持给定的时间单元

8. minus(long amountToSubtract, TemporalUnit unit)

在当亲的日期时间上减少相应的年/月/天/小时等 ,第一个参数是减少的具体数字,第二

个参数是通过 ChronoUnit 去实现指定减少的是年或者天数等例如 ChronoUnit.DAYS

就是减少天数。

类似的还有 minus(TemporalAmount amountToSubtract) 这个 TemoralAmount 是具体的时间间隔 ,实现类可以是 Period 或者 Duration

其它的关于 minus 做为前缀的方法是指明具体减少的是年或者天等等。

9. get前缀的方法可以获取具体的时间值例如获取 getMinute() 是获取当前时间的分钟数

10. to 前缀的方法是把当前的日期时间转换成其他的日期时间,例如 toLocalDate(),toLocalTime() 把日期和时间转换成 LocalDate 和 LocalTime

11. at 前缀转换成其他类型的日期时间:如 atOffset(ZoneOffset offset) 转换成 OffsetDateTime , atZone(ZoneId zone) 转换成 ZonedDateTime

分析一下四个时间类的区别:ChronoLocalDateTime/ OffsetDateTime / ZonedDateTime / LocalDateTime

ChronoLocalDateTime 是 LocalDateTime 的父类,是一个接口,里面定义了一些方法,LocalDateTime 里面有具体的实现

下面重点分析一下OffsetDateTime / ZonedDateTime / LocalDateTime

看一下 OffsetDateTime /ZonedDateTime/LocalDateTime 的 toString()的结果:

LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime);//2019-07-25T17:00:27.011
ZoneId zoneId = ZoneId.of("Asia/Singapore");
ZoneOffset zoneOffset = ZoneOffset.of("+2");
OffsetDateTime offsetDateTime = dateTime.atOffset(zoneOffset);
System.out.println(offsetDateTime);//2019-07-25T17:01:37.184+02:00
ZonedDateTime zonedDateTime = dateTime.atZone(zoneId);
System.out.println(zonedDateTime);
//2019-07-25T17:01:37.184+08:00[Asia/Singapore]

LocalDateTime 就是输出日期时间,OffsetDateTime 输出日期时间和时间偏移量

ZonedDateTime 输出日期时间,时间偏移量和具体的时区如:2019-07-25T17:01:37.184+08:00[Asia/Singapore]

LocalDateTime 是一个相当有代表性的一个类,学习时间类可以通过该类快速掌握其他的时间类,例如 LocalTime,LocalDate 等等,它们的使用基本类似。

  • 时间的转换类 DateTimeFormatter

这是一个时间转换类,常用来把指定格式的字符串转时间类和时间类转指定形式的字符串,该类最常用的就是这两个功能。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.now();
// LocalDateTime 和 DateTimeFormatter 都有 format 方法把时间转换成 String
String s = dateTime.format(formatter);
System.out.println(s);
String s1 =formatter.format(dateTime);
System.out.println(s1);
// 下面是转换成 String 的两中方式
String s2 = "2019-12-23 04:35:23";
TemporalAccessor dateTime1 = formatter.parse(s2);
LocalDateTime dateTime2 = LocalDateTime.from(dateTime1);
System.out.println(dateTime2);
LocalDateTime dateTime3 = LocalDateTime.parse(s2, formatter);
System.out.println(dateTime3);

Optional 类一个容器类

Optional 是用用来存放数据的的一个类,该类提供一些实用方法,详解如下:

of(T value) : 实例化一个 Optional 容器实例,value 不可以是空值, 如果 value == null, 该方法将会报错。

ofNullable(T value) : 实例化一个 Optional 容器实例,允许 value == null ,如果是空值将会返回一个空的容器。

empty(): 返回一个空的容器实例类 Optional

isPresent(): 返回一个 boolean 值,如果容器中有值就会返回 true ,如果返回 flase 容器中是空值

ifPresent(Consumer<? super T> consumer) : 如果容器中有值就会消费该值,如果容器中是空值,该方法什么也不做。

filter(Predicate<? super T> predicate) : 如果容器中没有值,返回一个空的 Optional 实例,如果容器中有值,判断是否符合 predicate 中的条件,符合返回该值,否则返回一个空的容器

orElse(T other) : 返回一个T 类型的实例,如果容器中已经有值,则返回容器中已经有的值,如果没有值就会返回参数 other .

orElseGet(Supplier<? extends T> other) : 使用同上类似, 只不过这里面的参数是一个函数式接口 Supplier ,可以自定义实现获取一个实例。

orElseThrow(Supplier<? extends X> exceptionSupplier) : 放回一个容器中的 T 类型数据,如果没有值组会抛出一个异常。

map(Function<? super T,? extends U> mapper) : 如果容器中有值,就会执行 mapper 这个函数式接口,可以返回一个新的类型的 Optional<U> 容器;如果容器中没有值,就返回一个空的容器。

flatMap(Function<? super T,Optional<U>> mapper) :用法同 map 的方法一样,如果容器中有值就会处理 mapper 函数。

get() : 从容器中获取值,如果有值返回容器中的值,如果没有值就抛出没有元素的一个异常。

以上是 Optional 类的一个常用方法说明,Optional 类可以很好的处理空指针异常问题,更方便我们去定位空指针的问题。

以上总结了 java 8 中提供的 lambda 表达式,Stream , LocalDateTime ,Optional 的常用操作。熟练使用 java 8 ,让代码好看一点,舒服一点 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值