java8 lambda map<list>排序_Java 8新特性介绍:lambda表达式,流类库等

de70ad13b5f41a0464fa16522c70dc85.png

今天向大家介绍一下Java语言最近几年的更新,相较于前些年“老顽固”的形象,现在的Java可以说是活力四射,版本号飙得飞快。而在版本号升级的背后,也包含着大量语言、底层、JVM上的更新。所以我准备先从Java 8开始,逐渐向大家介绍一下最近几年Java语言的变化。

不得不提的自然是开创了历史的Java 8,在当时可谓是历史上更新幅度最大的Java,引入了大量新内容。其中最重要的就是本文要介绍的lambda表达式和流类库,作为Java程序员当然必须要了解这些知识。其实这些东西说实话也不难,而且这些概念在其他语言中也存在,可以说是只要学会就可以融会贯通了。

lambda表达式

好了,废话不多说。首先来介绍的就是lambda表达式。Java是一门面向对象的语言,所有方法都必须包含在类或者接口里面,但是实际开发的时候,很多时候都只需要传递一个方法,也就是所谓的匿名方法。而lambda表达式就可以看作是匿名方法的另外一种形式。

举个例子,下面的代码创建了几个学生,然后分别按名字和年龄对学生进行排序。这个sort排序方法需要接受一个Comparator接口,其中包含了一个compare方法,它是实际对学生进行排序的。我们这里实现了两个方法分别是按名字和年龄进行排序。但是很容易就可以发现,这两个排序方法真的非常啰嗦,除了return那一行,剩下的完全可以说是多余的,用lambda表达式就可以简化这部分代码。

// 原方法public int compare(Student o1, Student o2) {  return o1.age - o2.age;}// 去掉参数列表前面所有东西,去掉方法参数的类型,加上箭头(o1, o2) -> {  return o1.age - o2.age;}// 如果方法体只有一行,方法体大括号和return语句也可以去掉(o1, o2) -> o1.age- o2.age

在Java语言中,所有只包含一个方法的接口,都可以转换成相应的lambda表达式。转换方法很简单,以sortByAge这个排序器为例,可以进行下面的简化。首先去掉方法名字、方法修饰符、方法参数列表上面的类型,然后在参数列表和方法体之间添加箭头。这样就转化为了lambda表达式。

如果方法体只有一行语句,还可以去掉方法体的大括号和return语句。当然,假如方法只有一个参数的话,参数左右的圆括号也可以去掉。第一次看到这种形式可能有点奇怪,但是熟悉之后,就会觉得lambda表达式不仅简洁,而且很自然。

public static void main(String[] args) {  List students = new ArrayList<>();  students.add(new Student("zhang3", 18));  students.add(new Student("li4", 20));  students.add(new Student("wang5", 22));    // lambda表达式  students.sort(((o1, o2) -> o1.name.compareTo(o2.name)));  System.out.println(students);  students.sort(((o1, o2) -> o1.age - o2.age));  System.out.println(students);    // 实际上还有更好的写法   students.sort(Comparator.comparing(o -> o.getName()));     students.sort((Comparator.comparingInt(Student::getAge)));}

经过简化之后, 上面的代码就变得非常短了。当然实际上还有更好的写法,Java 8中,基础类库也添加了一些新方法来适应lambda表达式。这就是最下面的两个写法,第一个利用了Java 8中新添加的Compator.comparing方法,这个方法接受一个lambda表达式,指定要比较的字段。而第二个写法则是上面的变种,如果lambda表达式是o -> o.function这种形式,就可以转换成“类名::方法名”这种形式,而这种形式叫做方法引用。方法引用和对应的lambda表达式没有区别, 只是写法上的不同。

public static void main(String[] args) {  List students = new ArrayList<>();  students.add(new Student("zhang3", 18));  students.add(new Student("li4", 20));  students.add(new Student("wang5", 22));  students.sort(((o1, o2) -> o1.name.compareTo(o2.name)));  System.out.println(students);  students.sort(((o1, o2) -> o1.age - o2.age));  System.out.println(students);    // 实际上还有更好的写法   students.sort(Comparator.comparing(o -> o.getName()));     // 方法引用   students.sort((Comparator.comparingInt(Student::getAge)));}

流类库

如果只有lambda表达式这么一个新特性的话,只是稍微减少了一些代码输入量。而lambda表达式和这里要介绍的流类库一起组合起来,才能发挥最大的作用。

先来看个例子,有一组学生,现在要选出所有年龄在22岁以下的,如果用一般的做法,我们要写成类似下面这样的代码。假如要添加其他查询条件,还要在if判断里新增条件,非常不方便。而使用流类库,就可以极大地简化代码。

public static void main(String[] args) {  List students = Student.students();  List young = new ArrayList<>();  for (Student s : students) {    if (s.getAge() < 22) {      young.add(s);    }  }  System.out.println(young);}

对应的使用流类库的代码如下。几乎所有的Java集合类型,都添加了一个名为stream的方法,用于将集合转化为流,而后,我们就可以在流上调用各种方法了。最常用的是filter方法,类似于SQL中的查询语句,可以从流中挑选符合查询条件的。如果需要查询多次,链式调用filter方法即可。流类库会对其做优化,不会出现反复查询多次的情况。查询完毕的结果仍然是一个流,为了获取最终结果,我们需要使用收集器,它会将流转化成集合类型。收集器方法一般作为流类库的最后一个方法而调用。

  public static void main(String[] args) {    List students = Student.students();    List young = students.stream()            .filter(o -> o.getAge() < 22)            .collect(Collectors.toList());    System.out.println(young);  }}

流类库和C井里的LINQ,以及SQL有着类似的功能,查询、组合、映射等等操作都支持。下面简单来看一下这些功能。第一个例子,使用max方法来查询年龄最大的学生,这个方法的返回值是个Optional,所以需要用get方法获取实际值。第二个例子,使用mapToInt将学生流转换成学生年龄的int流,然后用sum方法计算所有年龄的和。第三个例子,计算所有学生年龄的乘积,这个结果说实话没什么用,但是用到的reduce方法需要关注一下,reduce方法接受一个计算lambda表达式,然后会将这个计算应用到整个流上,最后的结果就是所有年龄的乘积。第四个例子用到了一个特殊的收集器groupingBy,它接受一个计算,将结果分组,这里就是简单地将学生按年龄分为两组。

  public static void main(String[] args) {    List students = Student.students();    // 查询年龄最大的学生    Student maxAgeStudent = students.stream()            .max(Comparator.comparing(Student::getAge))            .get();    System.out.println(maxAgeStudent);    //计算所有学生的年龄总和    int totalAge = students.stream()            .mapToInt(Student::getAge)            .sum();    System.out.println(totalAge);    // 所有学生年龄的乘积    int product = students.stream()            .map(Student::getAge)            .reduce((acc, e) -> acc * e)            .get();    System.out.println(product);    // 按学生年龄是否大于20分组    Map> groups = students.stream()            .collect(Collectors.groupingBy(o -> o.getAge() > 20));    System.out.println(groups);  }

lambda表达式和流类库功能强大,需要一段时间来掌握,这里就不多做介绍了。

默认接口方法和接口静态方法

如果你仔细注意Java 8的源代码的话,可以发现Java 8代码做了大量改动,上面的流类库也是新加进来的。集合类基本上都实现了Stream接口。但是因为Java 8之前的集合类并没有这个接口,这就导致了一个问题,那就是之前的代码放到Java 8里面不能正常工作了。为了解决这个问题,Java 8引入了一个新特性,也就是默认接口实现。

Java的接口里面声明的方法是没有实现的,但是现在,接口里面可以编写一个默认的实现。假如继承了这个接口的类没有做出实现,就会使用接口里面默认的实现。如果继承类编写了新的实现,就会覆盖这个默认实现。这样一来,之前的代码和现在的代码就可以做到兼容了。当然我们编写代码的时候,也可以利用这个特性。

接口默认实现需要添加default关键字。

interface Greeting {  default void greet() {    System.out.println("Default Greeting");  }}

除此以外,现在接口中还可以实现静态方法。

新的日期时间类

原来在Java中处理日期其实是一件很麻烦的事情,java.util.Date类设计的并不好,有以下几个问题:线程不安全、API设计的很不好,而且没有时区支持。现在Date类的大部分方法都已经被标记成了过时,提醒我们不要再使用它了。

f09028adc7371626a36580750ffbbed8.png

可能之前有朋友用过JodaTime这个类库,相对于原生的Java日期类,JodaTime使用方便了很多。新的Java日期时间类库也有JodaTime作者参与,经过重新设计的类库使用起来更加方便。新的类库主要使用LocalDate、LocalTime、LocalDateTime三个类,分别用于处理日期、时间、日期和时间,新的类库更易于使用。下边的例子简单演示了日期类的使用方法,时间类和日期时间类的用法都差不多。

public static void main(String[] args) {  // 当前日期,年月日,一年的第几天,星期几  LocalDate date = LocalDate.now();  System.out.printf("%s年%s月%s日,今年第%s天,星期:%s", date.getYear()          , date.getMonthValue()          , date.getDayOfMonth()          , date.getDayOfYear()          , date.getDayOfWeek());  System.out.println(date);  // 昨天  LocalDate yesterday = date.minus(1, ChronoUnit.DAYS);  System.out.println(yesterday);  //下个月这天  LocalDate theDayAfterAMonth = date.plus(1, ChronoUnit.MONTHS);  System.out.println(theDayAfterAMonth);  // 是否闰年  System.out.println(date.isLeapYear());  // 日期间隔  Period period=Period.between(yesterday,date.plus(5,ChronoUnit.DAYS));  System.out.println(period.getDays());}

Optional可空类型

这个和C井里面的Nullable类型差不多。Java中空指针错误经常会出现,很多时候不得不手动加一个if来判断对象的值是否为空,不为空的话取得它的值。这时候就可以利用Optional来改写,它的几个方法也可以很方便的实现几个判断模式。

  public static void main(String[] args) {    Optional integer = Optional.of(20);    // 原来的非空判断    if (integer == null) {      System.out.println(integer.get());    } else {      System.out.println("数据为空");    }    // 现在用Optional的方法来简化    integer.ifPresentOrElse(System.out::println, () -> System.out.println("数据为空"));    // 获取数据的时候还可以调用orElse方法设置默认值,防止出现空指针错误    System.out.println(integer.orElse(21));  }}

好了,以上就是Java 8一些重要更新的介绍了,当然其实作为Java历史上最大的更新,Java 8还包括了其他大量更新,例如各类类库的更新、JVM底层更新等等。后面还会介绍新版Java的其他特性。如果大家觉得本文有帮助的话,欢迎收藏,同时也别忘了转发哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值