亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

LambdaExpressionInJava

2019-11-14 15:26:05
字體:
來源:轉載
供稿:網友

 題記
在閱讀JDK源碼java.util.Collections的時候在UnmodifiableCollection類中看到了這么一段代碼:

    public void forEach(Consumer<? super E> action) {        c.forEach(action);    }

Consumer的源碼如下:

    @FunctionalInterface    public interface Consumer<T> {        void accept(T t);        default Consumer<T> andThen(Consumer<? super T> after) {            Objects.requireNonNull(after);            return (T t) -> { accept(t); after.accept(t); };        }    }

乍一看讓我費解了一下,但是回過神來發現這不就是Java8的新特性Lambda表達式嗎。原來對于這些新特性只是了解一下,沒注意到在JDK源碼中也使用到了,所以抽時間看了一下Java的Lambda表達式。

Lambda演算
Lambda演算在wiki上的非形式化表述如下:


    在λ演算中,每個表達式都代表一個函數,這個函數有一個參數,并且返回一個值。不論是參數和返回值,也都是一個單參的函數。可以這么說,λ演算中,只有一種“類型”,那就是這種單參函數。
    函數是通過λ表達式匿名地定義的,這個表達式說明了此函數將對其參數進行什么操作。例如,“加2”函數f(x)= x + 2可以用lambda演算表示為λx.x + 2 (或者λy.y + 2,參數的取名無關緊要)而f(3)的值可以寫作(λx.x + 2) 3。函數的應用(application)是左結合的:f x y =(f x) y。
    考慮這么一個函數:它把一個函數作為參數,這個函數將被作用在3上:λf.f 3。如果把這個(用函數作參數的)函數作用于我們先前的“加2”函數上:(λf.f 3)(λx.x+2),則明顯地,下述三個表達式:
        (λf.f 3)(λx.x+2) 與 (λx.x + 2) 3 與 3 + 2
    是等價的。有兩個參數的函數可以通過lambda演算這么表達:一個單一參數的函數的返回值又是一個單一參數的函數(參見Currying)。例如,函數f(x, y) = x - y可以寫作λx.λy.x - y。下述三個表達式:
        (λx.λy.x - y) 7 2 與 (λy.7 - y) 2 與 7 - 2
    也是等價的。然而這種lambda表達式之間的等價性無法找到一個通用的函數來判定。


詳細的形式化表述請跳轉Lambda演算

Java中的Lambda表達式
在Java中Lambda表達式可以有多個參數,在JSR335-FINAL(Java Specification Requests)中對Java中Lambda表達式的形式定義如下:

    LambdaExPRession:          LambdaParameters '->' LambdaBody    LambdaParameters:          Identifier          '(' FormalParameterListopt ')'          '(' InferredFormalParameterList ')'    InferredFormalParameterList:         Identifier          InferredFormalParameterList ',' Identifier    LambdaBody:          Expression          Block    The following definitions from 8.4.1 are repeated here for convenience:    FormalParameterList:          LastFormalParameter          FormalParameters ',' LastFormalParameter    FormalParameters:          FormalParameter          FormalParameters, FormalParameter    FormalParameter:          VariableModifiersopt Type VariableDeclaratorId    LastFormalParameter:          VariableModifiersopt Type '...' VariableDeclaratorId          FormalParameter

舉例如下 Examples of lambda expressions:

    () -> {}                     // No parameters; result is void    () -> 42                     // No parameters, expression body    () -> null                   // No parameters, expression body    () -> { return 42; }         // No parameters, block body with return    () -> { System.gc(); }       // No parameters, void block body    () -> {          if (true) return 12;          else {            int result = 15;            for (int i = 1; i < 10; i++)                  result *= i;            return result;          }    }                          // Complex block body with returns    (int x) -> x+1             // Single declared-type parameter    (int x) -> { return x+1; } // Single declared-type parameter    (x) -> x+1                 // Single inferred-type parameter    x -> x+1                   // Parens optional for single inferred-type case    (String s) -> s.length()   // Single declared-type parameter    (Thread t) -> { t.start(); } // Single declared-type parameter    s -> s.length()              // Single inferred-type parameter    t -> { t.start(); }          // Single inferred-type parameter    (int x, int y) -> x+y      // Multiple declared-type parameters    (x,y) -> x+y               // Multiple inferred-type parameters    (final int x) -> x+1       // Modified declared-type parameter    (x, final y) -> x+y        // Illegal: can't modify inferred-type parameters    (x, int y) -> x+y          // Illegal: can't mix inferred and declared types

注意,在形式參數中推導參數和聲明參數不能混用。(Inferred-type parameters的類型是編譯的時候從上下問中推斷出來的,比如說是借口定義時指定的參數)

Java SE 8: Lambda Quick Start
以下例子摘自 Oracle-Java SE 8: Lambda Quick Start

Runnable Lambda

    public class LambdaTest {        public static void main(String[] args) {            LambdaTest LT = new LambdaTest();            LT.runnableTest();            LT.comparatorTest();        }        public void runnableTest() {            System.out.println("=== RunnableTest ===");            // Anonymous Runnable            Runnable r1 = new Runnable() {                @Override                public void run() {                    System.out.println("Hello world one!");                }            };            // Lambda Runnable            Runnable r2 = () -> {                System.out.println("Hello world two!");                System.out.println("Hello world three!");            };            // Run em!            r1.run();            r2.run();        }    }

上面代碼用Lambda表達式代替了*New*操作和*run*方法的定義,使得代碼更為簡潔。

Comparator Lambda

    class Person {        public String surName;        public Person(String surName) {            super();            this.surName = surName;        }        public void printName() {            System.out.println(this.surName);        }    }    public void comparatorTest() {        List<Person> personList = new ArrayList<Person>();        personList.add(new Person("B"));        personList.add(new Person("A"));        //        // Sort with Inner Class        //        Collections.sort(personList, new Comparator<Person>() {        //            public int compare(Person p1, Person p2) {        //                return p1.surName.compareTo(p2.surName);        //            }        //        });        //        //        System.out.println("=== Sorted Asc SurName ===");        //        for (Person p : personList) {        //             p.printName();        //        }        // Use Lambda instead        // Print Asc        System.out.println("=== Sorted Asc SurName ===");        Collections.sort(personList, (p1, p2) -> p1.surName.compareTo(p2.surName));        for (Person p : personList) {            p.printName();        }        // Print Desc        System.out.println("=== Sorted Desc SurName ===");        Collections.sort(personList, (p1, p2) -> p2.surName.compareTo(p1.surName));        for (Person p : personList) {            p.printName();        }    }

這里則是用Lambda表達式代替了匿名的對象*Comparator*的作用。

Function
原先我以為Lambda表達式的加入只是一個簡單的語法糖,但是后面發現還有更多的語法糖。設想一下如果你需要對一個List的數據做判斷和篩選,通常我們會按照下面這種一般做法。

    public class Person {        public String givenName;        public String surName;        public int age;        public Gender gender;        public String eMail;        public String phone;        public String address;        //getters and setters        //...    }    public class RoboContactMethods2 {        public void callDrivers(List<Person> pl){               for(Person p:pl){                 if (isDriver(p)){                       roboCall(p);                 }           }        }           public boolean isDriver(Person p){               return p.getAge() >= 16;           }           public void roboCall(Person p){               System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());         }    }

這樣如果有多個過濾條件的需求,就需要實現更多的判斷函數,那么更文藝一些的做法是這樣的(還是用上面的Person對象舉例):

    public interface MyTest<T> {           public boolean test(T t);    }    public class RoboContactAnon {           public void phoneContacts(List<Person> pl, MyTest<Person> aTest){             for(Person p:pl){                   if (aTest.test(p)){                     roboCall(p);                   }             }           }           public void roboCall(Person p){             System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());           }           public static void main (String[] args) {            //get PersonList for testing.               List<Person> pl = getInitedPersonList();            RoboContactAnon rca = new RoboContactAnon();            rca.phoneContacts(pl, (p) -> p.getAge() > 16);           }    }

我們這里使用了一個自定義的*MyTest*接口,但是其實我們不需要自己定義這個接口,因為在Java SE 8中,JDK為我們提供了一系列的接口供我們使用,比如我們的*MyTest*接口就可以用系統提供的*Predicte*接口進行替代,它的定義跟MyTest類似:

    public interface Predicate<T> {        public boolean test(T t);    }

除了Predicte,JDK還提供了一系列的接口供我們在不同的場景使用。它們在java.util.function包中。下面是列舉的是JDK提供的一部分接口:

    - Predicate: A property of the object passed as argument    - Consumer: An action to be performed with the object passed as argument    - Function: Transform a T to a U    - Supplier: Provide an instance of a T (such as a factory)    - UnaryOperator: A unary operator from T -> T    - BinaryOperator: A binary operator from (T, T) -> T

Collections
除了上面提到的語法糖,和java.util.function包以外,Java SE 8還增加了java.util.stream,這是對Collections對象起到了一定的增強??紤]以下場景“你需要對一個List根據一定的條件對元素進行過濾,然后求過濾后元素某個屬性的平均值”。我們的做法一般是這樣:

    //仍舊以List<Person>舉例    double sum = 0;    int count = 0;    for (Person p : personList) {        if (p.getAge() > 0) {            sum += p.getAge();            cout++;        }    }    double average = count > 0 ? sum/average : 0;

如果我們使用stream的話,就可以用更文藝一點的寫法:

    // Get average of ages    OptionalDouble averageAge = pl             .stream()             .filter((p) -> p.getAge() > 16)             .mapToDouble(p -> p.getAge())             .average();

可以看到這樣寫的話代碼確實更簡潔了,它把List轉換成一個Stream,然后對元素進行操作。而且如果我們做的操作對元素的順序沒有要求那么我們可以將stream()方法換成parallelStream()方法,這樣可以得到一個可以并行處理的流,當我們對元素進行處理的時候,JVM會把這個流進行劃分,對每一個部分并行的進行處理,然后再進行歸并,這樣可以提高處理的效率,而這些對開發人員是透明的。

劃分,映射,歸并,這些聽起來有沒有覺得很熟悉,對,這就是MapReduce,只是跟Hadoop用機器作為處理節點不一樣的是這里對于劃分的處理是在一個JVM里面進行的。在java.util.stream中給我們提供了一個通用的reduce()方法:

     <U> U reduce(U identity,              BiFunction<U, ? super T, U> accumulator,              BinaryOperator<U> combiner);    int sumOfWeights = widgets.stream()                              .reduce(0,                              (sum, b) -> sum + b.getWeight()),                            Integer::sum);

其中identity是一個reduce的初始值,如果沒有元素進行reduce的話則返回identitiy值,如果有元素進行reduce則在identity上進行累加。accumulator是一個累加器,負責把部分結果與另一個元素進行累加,combiner則是一個合并器,負責把不同部分的子結果進行合并,取得最終的結果。這里如果所進行的運算對元素的元素沒有要求的話我們可以使用parallelStream(),取得一個并行的流,這樣才能對流進行劃分和并行處理,充分發揮這些新特性的性能。

將一個輸入的collection做轉換也是可以的比如下面的例子就返回了元素中某個屬性的List:

     <R> R collect(Supplier<R> supplier,               BiConsumer<R, ? super T> accumulator,               BiConsumer<R, R> combiner);     ArrayList<String> strings = stream.collect(() -> new ArrayList<>(),                                                (c, e) -> c.add(e.toString()),                                                (c1, c2) -> c1.addAll(c2));     List<String> strings = stream.map(Object::toString)                                  .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

有可能你覺得這不是KV對操作,不像MapReduce,那么你可以將結果映射成一個Map,這就是妥妥的KV對了,這個操作需要使用到groupingBy(Collection collection)方法:

     Collector<Employee, ?, Integer> summingSalaries         = Collectors.summingInt(Employee::getSalary);     Map<Department, Integer> salariesByDept         = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,                                                            summingSalaries));

以上只是對于stream的簡單舉例,詳情請閱讀[JSR335-FINAL]中關于java.util.stream的部分。

后記
起初我認為這些新特性只是一些語法糖,現在我也還認為這個特性是個語法糖。雖然在開發過程中很少用到這個新特性(甚至都不會用到),但是了解這些新特性總是沒有壞除的,恰當的使用這些新特性在某些場景下的確可以取得很好的效果(簡潔的代碼,優秀的性能)。這篇文章的初衷一是對自己所得的記錄,二是做一個分享。寫得不好的或者謬誤的地方還請大家批評指正,一起交流,共同進步。

參考文獻
Lambda演算-wikipedia
JSR335-FINAL(Java Specification Requests)
Oracle-Java SE 8: Lambda Quick Start

My Github


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人福利网站在线观看11| 久久在线免费视频| 欧美大片va欧美在线播放| 亚洲精品成人网| 亚洲乱码一区二区| 91亚洲国产成人久久精品网站| 亚洲国语精品自产拍在线观看| 国内偷自视频区视频综合| 国产成人激情小视频| 久久精品国产亚洲精品| 日韩在线观看免费av| 成人情趣片在线观看免费| 另类天堂视频在线观看| 欧美国产一区二区三区| 久久久国产91| 国产一区二区视频在线观看| 国产91av在线| 91精品国产亚洲| 日韩av影院在线观看| 国产精品日韩av| 欧美激情2020午夜免费观看| 亚洲深夜福利在线| 成人在线视频福利| 伊人伊人伊人久久| 2019亚洲日韩新视频| 国产精品免费一区豆花| 日韩在线视频观看正片免费网站| 亚洲iv一区二区三区| 日韩高清a**址| 国产精品99久久久久久久久久久久| 2018日韩中文字幕| 欧美交受高潮1| 97成人超碰免| 久久久久久久久综合| 国产精品美女呻吟| 亚洲色图欧美制服丝袜另类第一页| 萌白酱国产一区二区| 国内偷自视频区视频综合| 亚洲精品视频网上网址在线观看| 欧美激情奇米色| 欧美在线亚洲在线| 欧美极品xxxx| 国产精品视频一| 亚洲精品国产综合久久| 国产91在线播放精品91| 欧美丝袜第一区| 97在线视频一区| 精品久久久久久亚洲国产300| 欧美日韩午夜剧场| 国产精品男人爽免费视频1| 青草青草久热精品视频在线网站| 欧美一级淫片丝袜脚交| 国产精品久久综合av爱欲tv| 欧美激情第一页xxx| 日韩av电影手机在线观看| 久久青草福利网站| 国产丝袜高跟一区| 日本精品一区二区三区在线| 亚洲男人天堂九九视频| 中文国产成人精品久久一| 国产精品va在线| 国产欧美在线视频| 欧美日韩午夜激情| 欧美午夜精品久久久久久人妖| 在线看日韩av| 久久九九亚洲综合| 奇米成人av国产一区二区三区| zzijzzij亚洲日本成熟少妇| 96精品久久久久中文字幕| 欧美成人精品一区二区三区| 国产精品美乳一区二区免费| 国产成人精品免费久久久久| 亚洲日本中文字幕免费在线不卡| 久久九九免费视频| 欧美激情在线视频二区| 日韩动漫免费观看电视剧高清| 激情懂色av一区av二区av| 国产精品一区二区三区在线播放| 色综合久久久888| 国产视频观看一区| 国产亚洲精品美女| 久久人人爽人人爽爽久久| 亚洲男女自偷自拍图片另类| 日韩美女中文字幕| 欧美激情综合亚洲一二区| 日韩中文在线视频| 久99九色视频在线观看| 欧美午夜激情在线| 欧美激情xxxx性bbbb| 日本久久久久亚洲中字幕| 欧美影院成年免费版| 欧美日韩在线第一页| 成人免费高清完整版在线观看| 日韩精品www| 国产精品xxx视频| 欧美国产日韩一区二区在线观看| 国产精品夜色7777狼人| 九九热精品视频在线播放| 51色欧美片视频在线观看| 欧美成人全部免费| 日韩在线观看网址| 日韩美女av在线免费观看| 日韩毛片在线观看| 欧美国产日韩一区二区在线观看| 97激碰免费视频| 久久综合久久美利坚合众国| 亚洲伊人第一页| 国产成人精品一区二区| 在线免费观看羞羞视频一区二区| 日本国产一区二区三区| 欧美寡妇偷汉性猛交| 亚洲影院在线看| 亚洲女人天堂av| 2018日韩中文字幕| 国产精品视频在线播放| 亚洲性日韩精品一区二区| 欧美最猛性xxxxx亚洲精品| 亚洲成人激情小说| 国产精品久久9| 91在线国产电影| 国产精品一区二区3区| 色综合久久久888| 91精品啪aⅴ在线观看国产| 欧美日韩美女在线观看| 国产精品久久电影观看| 亚洲欧美日韩视频一区| www.色综合| 亚洲人成网7777777国产| 一区二区欧美亚洲| 2019av中文字幕| 国产999精品视频| 亚洲国产精品一区二区三区| 欧美激情亚洲综合一区| 草民午夜欧美限制a级福利片| 国产日本欧美在线观看| 久久精品男人天堂| 欧美激情在线观看| 国产精品成人一区二区三区吃奶| 国产精品久久久久免费a∨大胸| 亚洲欧洲国产精品| 91久久精品久久国产性色也91| 亚洲成人aaa| 成人春色激情网| 亚洲欧美国产一区二区三区| 久久久av一区| 欧美性xxxxx极品娇小| 欧美久久精品午夜青青大伊人| 日韩欧美国产黄色| 久久国产精品影片| 欧美亚洲伦理www| 成人激情视频小说免费下载| 久久久视频精品| 2019中文字幕全在线观看| 91福利视频网| 欧美一级淫片aaaaaaa视频| 欧美日韩国产激情| 91免费综合在线| 国产精品视频播放| 久久av中文字幕| 欧美猛男性生活免费| 欧美日产国产成人免费图片| 韩剧1988在线观看免费完整版| 日韩高清欧美高清| 欧美激情视频在线观看|