java8 新特性函数式编程lambda和stream详解

一、初识lambda表达式

         lambda表达式是一个匿名函数,我们可以把lamdba表达式理解为是一段可以传递的代码。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,是java的语言表达能力得到了提升。

        先不说语法,我们先来做一个需求:

        获取当前公司中员工年龄大于35的员工信息

List<Person> emps = Arrays.asList(
                new Person("张三",23,5000),
                new Person("李四",36,4000),
                new Person("王五",22,6000),
                new Person("周六",48,6000),
                new Person("田七",22,6000)
        );


//需求:获取当前公司员工年龄大于35的员工信息
    public List<Person> filterEmployees(List<Person> list){
        List<Person> emps = new ArrayList<>();

        for (Person person : list) {
            if(person.getAge() >= 35){
                emps.add(person);
            }
        }

        return emps;
    }

             再加一个需求 获取当前公司中员工工资大于5000的员工信息

public List<Person> filterEmployees(List<Person> list){
        List<Person> emps = new ArrayList<>();

        for (Person person : list) {
            if(person.getMoney() >= 5000){
                emps.add(person);
            }
        }

        return emps;
    }

           有上可以看出只是改变了条件,却要附带这么多重复代码,

           优化方式一:使用策略模式解耦

           

public interface MyPredicate<T> {

    boolean test(T t);
}



public class FilterEmployeeByAge implements MyPredicate<Person> {
    @Override
    public boolean test(Person p) {

        return p.getAge() > 35;
    }
}


public List<Person> filterEmployee(List<Person> list, MyPredicate<Person> myPredicate){
        List<Person> emps = new ArrayList<>();

        for (Person person : list) {
            if(myPredicate.test(person)){
                emps.add(person);
            }
        }
        return emps;
        
 }

List<Person> list = filterEmployee(emps,new FilterEmployeeByAge());
list.forEach(System.out::println);


       显而易见现在我们加需求只需要添加接口实现就好了,已于维护少了很多冗余代码,但是有了新的问题,这会导致有很多的实现类,那么就有了优化方式二:匿名内部类

List<Person> list2 = filterEmployee(emps, new MyPredicate<Person>() {
            @Override
            public boolean test(Person person) {
                return person.getAge()>=35;
            }
        });
list2.forEach(System.out::println);

          仔细想一下发现有用的代码只有 person.getAge()>=35  那么其他的代码能不能优化掉呢?这时使用lambda表达式就大大减少了代码量,优化方式三:lambda表达式

List<Person> list3 = filterEmployee(emps, (person) -> person.getAge() >35 );
list3.forEach(System.out::println);

         回首一盼,lambda竟可以如此清新脱俗,那么现在开始了解一下他的具体语法把。

二、lambda基础语法

  1.   Lambda表达式的基础语法;

               java8引入了一个新的操作符 “  -> ”  该操作符将Lambda拆成两部分

               左侧:Lambda表达式的参数列表

               右侧:Lambda表达式中所需执行

               语法格式一:无参数,无返回值

               

()->  System.out.println("Hello Lambda");

               语法格式二:有一个参数,并且无返回值

               

   (x)  ->  System.out.println(x);

                语法格式三:若只有一个参数,小括号可以省略不写

                   

 x   ->   System.out.println(x);

                 语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句:                   

Comparator<Integer> com = (x,y) -> {

                        System.out.println("函数式接口");
                        
                        return Integer.compare(x,y);

                     }

                  语法格式五:若Lambda体中只有一条语句,return 和大写括号都可以省略不写

                  

Comparator<Integer> com = (x,y) -> Integer.compare

                   语法格式六:Lamdba表达式的参数列表的数据类型可以省略不写,因为Jvm编译器通过上下文推断出,数据类型,即"类型推断"。

(Integer x,Integer y) -> Integer.compare(x,y);

     2、Lambda 表达式需要" 函数式接口"的支持

      函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解@FunctionalInterface修饰可以检测是否是函数式接口。

     3、内置的四大核心函数式接口

      Consumer<T>  : 消费型接口     void accept(T t);

public void happy(String str, Consumer<String> c){
        c.accept(str);
    }

happy("你好",(str)->System.out.println(str+"lambda"));

       Supplier<T> :供给型接口        T get();

//将整数放置集合中
public List<Integer> getNumList(int num, Supplier<Integer> s){
        List<Integer> nList = new ArrayList<>();

        for (int i = 0 ; i<num;i++){
            nList.add(s.get());
        }

        return nList;
    }

List<Integer> list = getNumList(10,() -> (int)(Math.random()*100));
        for(Integer num : list){
            System.out.println(num);
        }

        Function<T,R>: 函数型接口     R   apply(T t);

 //用于处理字符串
 public String strHander(String str , Function<String,String> f){
        return f.apply(str);
 }

 String emp = strHander("asdfx",(str)->str.toUpperCase());
 System.out.println(emp);

        Predicate<T> :断言型接口       boolean test(T t);

//将满足字符串的加入集合中
public List<String> filterStr(List<String> strlist, Predicate<String> p){
        List<String> nList = new ArrayList<>();
        for(String str : strlist){
            boolean b = p.test(str);
            if(b)  {nList.add(str);}
        }
        return nList;
    }

List<String> strlist = Arrays.asList("hello","lamdba","tianyu");
        List<String> nList = filterStr(strlist,(str)->{
            if(str.length()>5){
                return true;
            }else return false;
        });
        for (String str:nList) {
            System.out.println(str);
        }

三、方法引用和构造器引用

  •  方法引用

         主要有三种方式 :

         对象::实例方法名

PrintStream ps1 = System.out;
Consumer<String> con = (x) -> ps1.println(x);

PrintStream ps = System.out;
Consumer<String> con1 = ps::println;

         类::静态方法名

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com1 = Integer::compare;

         类::实例方法名

BiPredicate<String, String> bp = (x,y) ->x.equals(y);
BiPredicate<String, String> bp2 = String::equals;

         注意:①Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致

                    ②若Lambda参数列表中的第一参数实例方法的调用者,而第二个参数是实例的参数时,可以使用ClassName::method

  • 构造器引用

     格式   ClassName :: new

Supplier<Person> supplier = () -> new Person();
Supplier<Person> supplier2 = Person::new;

     注意:需要调用的构造器的参数列表与函数式接口中抽象方法的参数列表保持一致!

  • 数组引用

    格式 Type[] :: new

Function<Integer,String[]>  fun = (x) -> new String[x];
String[] strs = fun.apply(10);
Function<Integer,String[]> fun2 = String[]::new;

 四、Stream详解

Stream是java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。简而言之Stream API提供了一种高效且易于使用的处理数据的方式。Stream主要是三个操作步骤:

  • 创建Stream

     创建Stream的方式

      1)、可以通过Collertion系列集合提供的stream()或parallelStream()

 List<String>  list = new ArrayList<>();
 Stream<String> stream = list.stream();

       2)、通过Arrays中的静态方法stream()获取数组流

person[] pson = new person[10];
Stream<person> stream = Arrays.stream(pson);

       3)、通过stream类中的静态方法of()

stream<String> stream = Stream.of("aa","bb","cc");

      4)、创建无限流

      a、迭代

Stream<Integer> stream = Stream.itreate(0,(x)->x+2);
stream.limit(10).forEach(System.out::println);

     b、生成

Stream.generate( () -> Math.random() )
      .limit(5)
      .forEach(System.out::println);
  • 中间操作

          a、筛选和切片

          filter - 接收Lambda,从滚中排除某些元素 。

List<Person> emps = Arrays.asList(
                new Person("张三",23,5000),
                new Person("李四",36,4000),
                new Person("王五",22,6000),
                new Person("周六",48,6000),
                new Person("田七",22,6000)
        );
        Stream<Person> stream  = emps.stream()
                                     .filter( (e) -> {
                                        System.out.println("Stream中间操作");
                                        return e.getAge() >= 35;
                                     });
        //终止操作
        stream.forEach(System.out::println);

         注意:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”

         limit - 截断流,使其元素不超过给的数量。

            emps.stream()
                .filter( (e) -> {
                    System.out.println("Stream中间操作");
                    return e.getAge() >= 35;
                })
                .limit(2)
                .forEach(System.out::println);

           skip(n) - 跳过元素,返回一个扔掉了前几个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

            emps.stream()
                .filter( (e) -> {
                    System.out.println("Stream中间操作");
                    return e.getAge() >= 35;
                })
                .skip(2)
                .forEach(System.out::println);

           distincture - 筛选,通过流所生成元素的hashCode()和equals()去除重复元素。

            emps.stream()
                .filter( (e) -> {
                    System.out.println("Stream中间操作");
                    return e.getAge() >= 35;
                })
                .skip(2)
                .distinct()
                .forEach(System.out::println);

             b、映射

            map - 接受Lambda,将元素转化成其他形式或提取信息。接收一个函数作位参数,该函数会被引用到每个元素上,并将其映射成了一个新的元素。

List<String> list =  Arrays.asList("aa","bb","cc","dd","ee");
         list.stream()
             .map((str)->str.toUpperCase())
             .forEach(System.out::println);

              flatMap - 接受一个函数作位参数,将该中的每个值都换成另一个流,然后把所有流简介成一个流

        public static Stream<Character> filterCharacter(String str){
            List<Character> list = new ArrayList<>();

            for(Character ch : str.toCharArray()){
                list.add(ch);
            }
            return list.stream();
        }

        List<String> list =  Arrays.asList("aa","bb","cc","dd","ee");
         
        Stream<Character> sm = list.stream()
                                   .flatMap(TestStreamApi::filterCharacter);
        sm.forEach(System.out::println);

 

输出

a
a
b
b
c
c
d
d

             c、排序

              sorted() - 自然排序(Comparable)

              按照Comparable内置的自然排序

            List<String> list =  Arrays.asList("aa","bb","cc","dd","ee");
            list.stream()
            .sorted()
            .forEach(System.out::println);

                sorted(Comparator com)-定制排序(Comparator)

       

        List<Person> emps = Arrays.asList(
                new Person("张三",23,5000),
                new Person("李四",36,4000),
                new Person("王五",22,6000),
                new Person("周六",48,6000),
                new Person("田七",22,6000)
        );
         emps.stream()
            .sorted((e1,e2) -> {
                if(e1.getAge().equals(e2.getAge())){
                    return e1.getUsername().compareTo(e2.getUsername());
                }else {
                    return e1.getAge().compareTo(e2.getAge());
                }
            }).forEach(System.out::println);
  • Stream的终止操作

         a、查找与匹配

           allMatch - 检查是否匹配所有元素

           anyMatch - 检查是否至少匹配一个元素

           noneMatch - 检查是否没有匹配所有元素

           findAny - 返回当前流中的任意元素

           count - 返回流中的元素个数

           max - 返回流中的最大值

           min - 返回流中的最小值

        b、归约和收集

          归纳:reduce(T  identity , BinaryOperator)  /  reduce(BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8);
Integer sum =  list.stream()
                   .reduce(0,(x,y) -> x+y);
System.out.println(sum);


//输出
36

         备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索出名

List<Person> emps = Arrays.asList(
                new Person("张三",23,5000),
                new Person("李四",36,4000),
                new Person("王五",22,6000),
                new Person("周六",48,6000),
                new Person("田七",22,6000)
        );

        Optional<Double> op = emps.stream()
            .map(Person::getMoney)
            .reduce(Double::sum);
        System.out.println(op.get());


//输出
27000

           收集:collect --将流转化为其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总的方法

           Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常用收集器实例。

Set<String> set = emps.stream()
                      .map(Person::getUsername)
                      .collect(Collectors.toSet());
set.forEach(System.out::println);
Set<String> set = emps.stream()
                      .map(Person::getUsername)
                      .collect(Collectors.toSet());
set.forEach(System.out::println);

//输出
李四
张三
王五
田七
周六

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章