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);

//輸出
李四
張三
王五
田七
週六

 

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