来聊聊java8的新特性

先看一个例子,以案例带出来java的特性。

一:Lambda表达式与方法引用

1.1Lambda表达式

先聊一个不断变化的需求:

第一次需求:筛选出绿苹果

public static List<Apple> filterGreenApples(List<Apple> apples){
      List<Apple> result=new ArraryList<>();//累积苹果的列表
      for(Apple apple:apples){
          if("green".equals(apple.getColor()){
             result.add(apple);
           }
      }
      return result;
}

第二次需求:筛选出多种颜色

重构为颜色作为参数

public static List<Apple> filterGreenApples(List<Apple> apples,String color){
      List<Apple> result=new ArraryList<>();//累积苹果的列表
      for(Apple apple:apples){
          if(color.equals(apple.getColor()){
             result.add(apple);
           }
      }
      return result;
}

第三次需求:既能筛选颜色,也能筛重量

public static List<Apple> filterGreenApples(List<Apple> apples,String color,int weight,boolean flag){
      List<Apple> result=new ArraryList<>();//累积苹果的列表
      for(Apple apple:apples){
          if((flag && color.equals(apple.getColor()) ||(!flag && apple.getWeight()>weight)){
             result.add(apple);
           }
      }
      return result;
}

第四次策略模式重构

public interface ApplePredicate{//策略接口
   boolean test(Apple apple);
}
public class AppleGreenColorPredicate implements ApplePredicate{//第一种策略
   public boolean test(Apple apple){
       return "green".equals(apple.getColor());
   }
}
public class AppleRedAndHeavyPredicate implements ApplePredicate{//第二种策略
  public boolean test(Apple apple){
       return "red".equals(apple.getColor())&&apple.getWeight()>150;
   }
}
public static List<Apple> filterApples(List<Apple> apples,ApplePredicate p){
   List<Apple> result=new ArrayList<>();
   for(Apple apple:apples){
      if(p.test(apple)){
           result.add(apple);
      }
   }
   return result;
}

 

使用策略的代码

List<Apple> redAndHeavyApples=filterApples(apples,new AppleRedAndHeavyPredicate());

这里有个重要的知识点是第二个参数,传递策略,也就是行为参数化。

第五次需求:匿名类重构

List<Apple> redApples=filterApples(apples,new ApplePredicate(){
    public boolean test(Apple apple){
          return "red",equals(apple.getColor());
    }
});

第六次:使用java8Lambda表达式

List<Apple> result=filterApples(apples,(Apple apple)->"red".equals(apple.getColor()));

我们可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但有参数列表、函数主体、返回类型。它可以作为参数传递给方法或存储在变量中。

(Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight());

Lambda表达式有参数、箭头和主体组成,(Apple a1,Apple a2)为参数,->为箭头,a1.getWeight().compareTo(a2.getWeight());是函数主体

用Lambda表达式之前的写法是:

Comparator<Apple> byWeight=new Comparator<Apple>(){
    public int compare(Apple a1,Apple a2){
        return a1.getWeight().compareTo(a2.getWeight());
    }
};

Lambda表达式让代码更简洁,并且更高效,降低多线程编程,后边会介绍流。

哪些是有效的Lambda表达式呢?

(String s)->s.length();//第一个是一个String类型参数并返回一个int。Lambda没有return语句,因为已经隐含了return
(Apple a)->a.getWeight()>150//也属于第一种类型,返回一个boolean类型
(int x,int y)->{//第二种类型,多个参数没有返回值。
System.out.println("Result:");
System.out.println(x+y);//注意Lambda表达式可以包含多行语句,这里是两行
}

测验Lambda语法:

(1)()->{}正确,没有参数并返回void

  (2)  ()->"JDWL" 正确,没有参数,并返回String做表达式

 (3) ()->{return "JDWL"} 正确,没有参数,返回显示返回String类型数据

(4)(Integer i)->return "JDWL"+i;错误,return是一个表达式语句,因此需要使用花括号(Integer i)->{return "JDWL"+i;}

(5)(String s)->{"JDWL";}错误,"JDWL"是一个表达式,不是一个语句。

1.2方法引用

方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下,比使用Lambda表达式,更易读。

先前Lambda表达式

apples.sort((Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight()));

使用方法引用之后

apples.sort(comparing(Apple::getWeight());

方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。他是让你用已有的方法实现来创建Lambda表达式。

目标引用放在分隔符::前,方法的名称放在后面。例如,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住这里不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a)->a.getWeight()的快捷写法。

 Supplier<Apple> c1=Apple::new;

Apple a1=c1.get();

等价于:Supplier<Apple> c1=()->new Apple();

Apple a1=c1.get();

看一个方法引用的实例:

按照重量排序的最原始写法:

public class AppleComparator implements Comparator<Apple>{
    public int compare(Apple a1,Apple a2){
         return a1.getWeight().compareTo(a2.getWeight());
    }
}

apples.sort(new AppleComparator());

使用使用Lambda表达式:

apples.sort((Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight());

使用方法引用:

apples.sort(comparing(Apple::getWeight));

二:流

流是java8的新的api,它允许你以声明性方式处理数据集合(通过查询 语句来表达,而不是临时编写一个实现)。

如果把for循环看成外部迭代器,那么流可以看做内部迭代器。并且流还可以并行

java8之前想要对卡路里小于400的菜肴排序并获取菜肴名称的写法:

 

 1 List<Dish> lowCaloricDishes=new ArrayList<>();
 2 for(Dish d:menus){//选择卡路里低于400卡的菜肴
 3       if(d.getCalories()<400){
 4           lowCaloricDishes.add(d);
 5        }
 6 }
 7 Collections.sort(lowCaloricDishes,new Comparator<Dish>(){//使用匿名类对菜肴排序
 8       public int compare(Dish d1,Dish d2){
 9           return Integer.compare(d1.getCalories(),d2.getCalories());
10       }
11 });
12 List<String> lowCaloricDishesName=new ArrayList<>();
13 for(Dish d:lowCaloricDishes){//获取菜名
14      lowCaloricDishesName.add(d.getName());
15 }

 

使用java8流之后

1 List<String> lowCaloricDishesName=menus.stream().filter(d->d.getCalories()<400).sorted(comparing(Dish::getCalories)).map(Dish::getName).collect(toList());

如果使用多线程做排序只用把stream()换成parallelStream(),还可以取出前3名

List<String> lowCaloricDishesName=menus.parallelStream().filter(d->d.getCalories()<400).sorted(comparing(Dish::getCalories)).map(Dish::getName).limit(3).collect(toList());

 

 

15行代码压缩到1行,曾经有人因为面试官让写一个java8筛选、排序、获取名称的题没做出来,薪资就止步20k以下了。

java8流的好处:

声明性:说明想要完成什么,而不是说明如何实现一个操作。更简洁,更易读

可复合:更灵活的组合

可并行:性能更好

流操作分为两种:

一种是filter、sorted、map、limit、distinct中间操作,连成一条流水线

另外是终端操作,包含collect、count、forEach,会触发流水线执行并关闭它。

 

posted @ 2020-12-27 18:03  姚春辉  阅读(115)  评论(0编辑  收藏  举报