欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java中l(wèi)ambda表達式簡單用例

 更新時間:2016年09月11日 16:45:45   作者:robin  
讓我們從最簡單的例子開始,來學習如何對一個string列表進行排序。我們首先使用Java 8之前的方法來實現(xiàn)

我對java中l(wèi)ambda表達式的看法是相當糾結的:

一個我這么想:lambda表達式降低了java程序的閱讀體驗。java程序一直不以表現(xiàn)力出眾,正相反使Java流行的一個因素正是它的安全和保守——即使是初學者只要注意些也能寫出健壯且容易維護的代碼來。lambda表達式對開發(fā)人員的要求相對來說高了一層,因此也增加了一些維護難度。

另一個我這么想:作為一個碼代碼的,有必要學習并接受語言的新特性。如果只是因為它的閱讀體驗差就放棄它在表現(xiàn)力方面的長處,那么即使是三目表達式也有人覺得理解起來困難呢。語言也是在發(fā)展的,跟不上的就自愿被丟下好了。

我不愿意被丟下。不過非讓我做出選擇的話,我的決定還是比較保守的:沒必要一定在java語言中使用lambda——它讓目前Java圈子中的很多人不習慣,會造成人力成本的上升。如果非常喜歡的話,可以考慮使用scala。

不管怎樣,我還是開始試著掌握Lambda了,畢竟工作中維護的部分代碼使用了Lambda(相信我,我會逐步把它去掉的)。學習的教程是在Oracla – Java官網(wǎng)的相關教程。

——————————

假設目前正在創(chuàng)建一個社交網(wǎng)絡應用。其中的一個特性是管理員可以對符合指定條件的會員執(zhí)行某些操作,如發(fā)送消息。下面的表格詳細描述了這個用例:

Field 描述
名稱 要執(zhí)行的操作
主要參與者 管理員
前提條件 管理員登錄系統(tǒng)
后置條件 只對符合指定標準的會員執(zhí)行操作
主成功場景 1. 管理員對要執(zhí)行操作的目標會員設置過濾標準;
2. 管理員選擇要執(zhí)行的操作;
3. 管理員點擊提交按鈕;
4. 系統(tǒng)找到符合指定標準的會員;
5. 系統(tǒng)對符合指定標準的會員執(zhí)行預先選擇的操作。
擴展 在選擇執(zhí)行操作前或者點擊提交按鈕前,管理員可以選擇是否預覽符合過濾標準的會員信息。
發(fā)生頻率 一天中會發(fā)生許多次。

使用下面的Person類來表示社交網(wǎng)絡中的會員信息:

public class Person {
 
  public enum Sex {
    MALE, FEMALE
  }
 
  String name;
  LocalDate birthday;
  Sex gender;
  String emailAddress;
 
  public int getAge() {
    // ...
  }
 
  public void printPerson() {
    // ...
  }
}

假設所有的會員都保存在一個List<Person>實例中。

這一節(jié)我們從一個非常簡單的方法開始,然后嘗試使用局部類和匿名類進行實現(xiàn),到最后會逐步深入體驗Lambda表達式的強大和高效??梢栽谶@里找到完整的代碼。

方案一:一個個地創(chuàng)建查找符合指定標準的會員的方法

這是實現(xiàn)前面提到的案例最簡單粗糙的方案了:就是創(chuàng)建幾個方法、每個方法校驗一項標準(比如年齡或是性別)。下面的一段代碼校驗了年齡大于一個指定值的情況:

   public static void printPersonsOlderThan(List<person> roster, int age) {
     for (Person p : roster) {
       if (p.getAge() >= age) {
         p.printPerson();
       }
     }
   }

這是一種很脆弱的方案,極有可能因為一點更新就導致應用無法運行。假如我們?yōu)镻erson類添加了新的成員變量或者更改了標準中衡量年齡的算法,就需要重寫大量的代碼來適應這種變化。再者,這里的限制也太過僵化了,比方說我們要打印年齡小于某個指定值的成員又該怎么做呢?再添加一個新方法printPersonsYoungerThan?這顯然是一個笨方法。

方案二:創(chuàng)建更通用的方法

下面的方法較之printPersonsOlderThan適應性更好一些;這個方法打印了在指定年齡段內的會員信息:

    public static void printPersonsWithinAgeRange(
        List<person> roster, int low, int high) {
      for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
          p.printPerson();
        }
      }
    }

此時又有新的想法了:如果我們要打印指定性別的會員信息,或者同時符合指定性別又在指定年齡段內的會員信息該怎么辦呢?如果我們調整了Person類,添加了諸如友好度和地理位置這樣的屬性又該怎么辦呢。盡管這樣寫方法要比printPersonsYoungerThan通用性更強一些,但是如果為每一種可能的查詢都寫一個方法也會導致代碼的脆弱。倒不如把標準檢查這一塊代碼給獨立到一個新的類中。

方案三:在一個局部類中實現(xiàn)標準檢查

下面的方法打印了符合檢索標準的會員信息:

  public static void printPersons(List<person> roster, CheckPerson tester) {
    for (Person p : roster) {
      if (tester.test(p)) {
        p.printPerson();
      }
    }
  }

在程序里使用了一個CheckPerso對象tester對List參數(shù)roster中的每個實例進行校驗。如果tester.test()返回true,就會執(zhí)行printPerson()方法。為了設置檢索標準,需要實現(xiàn)CheckPerson接口。

下面的這個類實現(xiàn)了CheckPerson并提供了test方法的具體實現(xiàn)。這個類中的test方法過濾了滿足在美國服兵役條件的會員信息:即性別為男、且年齡在18~25歲之間。

  class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
      return p.gender == Person.Sex.MALE &&
          p.getAge() >= 18 &&
          p.getAge() <= 25;
    }
  }

要使用這個類,需要創(chuàng)建一個實例并觸發(fā)printPersons方法:

  printPersons(
      roster, new CheckPersonEligibleForSelectiveService());

現(xiàn)在的代碼看起來不那么脆弱了——我們不需要因為Person類結構的變化而重寫代碼。不過這里仍有額外的代碼:一個新定義的接口、為應用中每個搜索標準定義了一個內部類。

因為CheckPersonEligibleForSelectiveService實現(xiàn)了一個接口,所以可以使用匿名類,而不需要再為每種標準分別定義一個內部類。

方案四:使用匿名類實現(xiàn)標準檢查

下面調用的printPersons方法中的一個參數(shù)是匿名類。這個匿名類的作用和方案三中的CheckPersonEligibleForSelectiveService類一樣:都是過濾性別為男且年齡在18和25歲之間的會員。

    printPersons(
        roster,
        new CheckPerson() {
          public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
          }
        }
    );

這個方案減少了編碼量,因為不再需要為每個要執(zhí)行的檢索方案創(chuàng)建新類。不過這樣做仍讓人有些不舒服:盡管CheckPerson接口只有一個方法,實現(xiàn)的匿名類仍是有些冗長笨重。此時可以使用Lambda表達式替換匿名類,下面會說下如何使用Lambda表達式替換匿名類。

方案五:使用Lambda表達式實現(xiàn)標準檢查

CheckPerson接口是一個函數(shù)式接口。所謂的函數(shù)式接口就是指任何只包含一個抽象方法的接口。(一個函數(shù)式接口中也可以有多個default方法或靜態(tài)方法)。既然函數(shù)式接口中只有一個抽象方法,那么在實現(xiàn)這個函數(shù)式接口的方法的時候可以省略掉方法的方法名。為了實現(xiàn)這個想法,可以使用Lambda表達式替換匿名類表達式。在下面重寫的printPersons方法中,相關的代碼做了高亮處理:

    printPersons(
        roster,
        (Person p) -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25
    );

在這里還可以使用一個標準的函數(shù)接口來替換CheckPerson接口,從而進一步簡化代碼。

方案六:在Lambda表達式中使用標準函數(shù)式接口

再來看一下CheckPerson接口:

  interface CheckPerson {
    boolean test(Person p);
  }

這是一個非常簡單的接口。因為只有一個抽象方法,所以它也是一個函數(shù)式接口。這個抽象方法只接受一個參數(shù)并返回一個boolean值。這個抽象接口太過簡單了,以至于我們會考慮是否有必要在應用中定義一個這樣的接口。此時可以考慮使用JDK定義的標準函數(shù)式接口,可以在java.util.function包下找到這些接口。

在這個例子中,我們就可以使用Predicate<T>接口來替換CheckPerson。在這個接口中有一個boolean test(T t)方法:

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

Predicate<T>接口是一個泛型接口。泛型類(或者是泛型接口)使用一對尖括號(<>)指定了一個或多個類型參數(shù)。在這個接口中只有一個類型參數(shù)。在使用具體類聲明或實例化一個泛型類時,就會獲得一個參數(shù)化類。比如說參數(shù)化類Predicate<Person>就是這樣的:

  interface Predicate<person> {
    boolean test(Person t);
  }

在這個參數(shù)化類中有一個與CheckPerson.boolean test(Person p)方法的參數(shù)和返回值都一致的方法。因此就可以同如下方法所演示的一樣使用Predicate<T>接口來替換CheckPerson接口:

  public static void printPersonsWithPredicate(
      List<person> roster, Predicate<person> tester) {
    for (Person p : roster) {
      if (tester.test(p)) {
        p.printPerson();
      }
    }
  }

然后使用下面的代碼就可以像方案三中一樣篩選適齡服兵役的會員了:

    printPersonsWithPredicate(
        roster,
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25
    );

有沒有注意到,這里使用Predicate<Person>作為參數(shù)類型時并沒有顯式指定參數(shù)類型。這里并不是唯一適用lambda表達式的地方,下面的方案會介紹更多l(xiāng)ambda表達式的用法。

方案七:在整個應用中使用lambda表達式

再來看一下printPersonsWithPredicate 方法,考慮是否可以在這里使用lambda表達式:

  public static void printPersonsWithPredicate(
      List<person> roster, Predicate<person> tester) {
    for (Person p : roster) {
      if (tester.test(p)) {
        p.printPerson();
      }
    }
  }

在這個方法中使用Predicate實例tester檢查了roster中的每個Person實例。如果Person實例符合tester中定義的檢查標準,將會觸發(fā)Person實例的printPerson方法。

除了觸發(fā)printPerson方法,滿足tester標準的Person實例還可以執(zhí)行其他的方法??梢钥紤]使用lambda表達式指定要執(zhí)行的方法(私以為這個特性很好,解決了java中方法不能作為對象傳遞的問題)。現(xiàn)在需要一個類似printPerson方法的lambda表達式——一個只需要一個參數(shù)且返回為void的lambda表達式。記住一點:要使用lambda表達式,需要先實現(xiàn)一個函數(shù)式接口。在這個例子中需要一個函數(shù)式接口,其中只包含一個抽象方法,這個抽象方法有個類型為Person的參數(shù),且返回為void。可以看一下JDK提供的標準函數(shù)式接口Consumer<T>,它有一個抽象方法void accept(T t)正好滿足這個要求。在下面的代碼中使用一個Consumer<T>的實例調用accept方法替換了p.printPerson():

  public static void processPersons(
      List<person> roster,
      Predicate<person> tester,
      Consumer<person> block) {
    for (Person p : roster) {
      if (tester.test(p)) {
        block.accept(p);
      }
    }
  }

對應這里,可以使用如下代碼篩選適齡服兵役的會員:

    processPersons(
        roster,
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25,
        p -> p.printPerson()
    );

如果我們想做的事情不只是打印會員信息,而是更多的事情,比如驗證會員身份、獲取會員聯(lián)系方式等等。此時,我們需要一個有返回值方法的函數(shù)式接口。JDK的標準函數(shù)式接口Function<T,R>就有一個這樣的方法R apply(T t)。下面的方法從參數(shù)mapper中獲取數(shù)據(jù),并在這些數(shù)據(jù)上執(zhí)行參數(shù)block指定的行為:

  public static void processPersonsWithFunction(
      List<person> roster,
      Predicate<person> tester,
      Function<person  , string> mapper,
      Consumer<string> block) {
    for (Person p : roster) {
      if (tester.test(p)) {
        String data = mapper.apply(p);
        block.accept(data);
      }
    }
  }

下面的代碼獲取了roster中適齡服兵役的所有會員的郵箱信息并打印出來:

    processPersonsWithFunction(
        roster,
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25,
        p -> p.getEmailAddress(),
        email -> System.out.println(email)
    );

方案八:多使用泛型

再來回顧一下processPersonsWithFunction方法。下面是這個方法的泛型版本,新方法在參數(shù)類型上要求更為寬容:

  public static <x  , Y> void processElements(
      Iterable<x> source,
      Predicate<x> tester,
      Function<x  , Y> mapper,
      Consumer<y> block) {
    for (X p : source) {
      if (tester.test(p)) {
        Y data = mapper.apply(p);
        block.accept(data);
      }
    }
  }

要打印適齡服兵役的會員信息可以像下面這樣調用processElements方法:

    processElements(
        roster,
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25,
        p -> p.getEmailAddress(),
        email -> System.out.println(email)
    );

在方法的調用過程中執(zhí)行了如下行為:

從一個集合中獲取對象信息,在這個例子里是從集合實例roster中獲取Person對象信息。
過濾能夠匹配Predicate實例tester的對象。在這個例子里,Predicate對象是一個lambda表達式,它指定了過濾適齡服兵役的條件。

將過濾后的對象交給一個Function對象mapper處理,mapper會為這個對象匹配一個值。在這個例子中Function對象mapper是一個lambda表達式,它返回了每個會員的郵箱地址。

由Consumer對象block為mapper匹配的值指定一個行為。在這個例子里,Consumer對象是一個lambda表達式,它的作用是打印一個字符串,也就是Function實例mapper返回的會員郵件地址。

方案九:使用將lambda表達式作為參數(shù)的聚集操作

下面的代碼中使用了聚集操作來打印roster集合中適齡服兵役會員的郵件地址:

    roster.stream()
        .filter(
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25)
        .map(p -> p.getEmailAddress())
        .forEach(email -> System.out.println(email));

分析下如上代碼的執(zhí)行過程,整理如下表:

行為

聚集操作

獲取對象

Stream<E> stream()

過濾匹配Predicate實例指定標準的對象

Stream<T> filter(Predicate<? super T> predicate)

通過一個Function實例獲取對象匹配的值

<R> Stream<R> map(Function<? super T,? extends R> mapper)

執(zhí)行Consumer實例指定的行為

void forEach(Consumer<? super T> action)

表中的filter、map和forEach操作都是聚集操作。聚集操作處理的元素來自Stream,而非是直接從集合中獲?。ň褪且驗檫@示例程序中調用的第一個方法是stream())。Stream是一個數(shù)據(jù)序列。和集合不同,Stream并沒有用特定的結構存儲數(shù)據(jù)。相反的,Stream從一個特定的源獲取數(shù)據(jù),比如從集合獲取數(shù)據(jù),通過一個pipeline。pipeline是一個Stream操作序列,在這個例子中就是filter-map-forEach。此外,聚集操作通常采用lambda表達式作為參數(shù),這也給了我們許多自定義的空間。

相關文章

  • java?guava主要功能介紹及使用心得總結

    java?guava主要功能介紹及使用心得總結

    這篇文章主要為大家介紹了java?guava主要功能介紹及使用心得總結,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Spring Boot 集成MyBatis 教程詳解

    Spring Boot 集成MyBatis 教程詳解

    這篇文章主要介紹了Spring Boot 集成MyBatis 教程詳解,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-04-04
  • JVM 心得 OOM時的堆信息獲取方法與分析

    JVM 心得 OOM時的堆信息獲取方法與分析

    下面小編就為大家?guī)硪黄狫VM 心得 OOM時的堆信息獲取方法與分析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Spring中IOC和AOP的核心組成架構詳解

    Spring中IOC和AOP的核心組成架構詳解

    這篇文章主要介紹了Spring中IOC和AOP的核心組成架構詳解,本文是對Spring的2大核心功能——IoC和AOP 的總結提煉,并增加了環(huán)境profile和條件化bean的內容,篇幅較短,更像是一個大綱,或者思維導圖,需要的朋友可以參考下
    2023-08-08
  • feign的ribbon超時配置和hystrix的超時配置說明

    feign的ribbon超時配置和hystrix的超時配置說明

    這篇文章主要介紹了feign的ribbon超時配置和hystrix的超時配置說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • java中Lamda表達式講解

    java中Lamda表達式講解

    本文詳細講解了java中的Lamda表達式,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12
  • 如何利用JConsole觀察分析Java程序的運行并進行排錯調優(yōu)

    如何利用JConsole觀察分析Java程序的運行并進行排錯調優(yōu)

    從Java 5開始 引入了 JConsole。JConsole 是一個內置 Java 性能分析器,可以從命令行或在 GUI shell 中運行。您可以輕松地使用 JConsole(或者,它更高端的 “近親” VisualVM )來監(jiān)控 Java 應用程序性能和跟蹤 Java 中的代碼
    2015-12-12
  • JavaWeb 簡單分頁實現(xiàn)代碼

    JavaWeb 簡單分頁實現(xiàn)代碼

    這篇文章主要介紹了JavaWeb 簡單分頁實現(xiàn)代碼的相關資料,需要的朋友可以參考下
    2016-11-11
  • Java進程間通信之消息隊列

    Java進程間通信之消息隊列

    這篇文章主要為大家詳細介紹了Java進程間通信之消息隊列,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 使用CXF和Jersey框架來進行Java的WebService編程

    使用CXF和Jersey框架來進行Java的WebService編程

    這篇文章主要介紹了使用CXF和Jersey框架來進行Java的WebService編程,Web service是一個平臺獨立的低耦合的自包含的基于可編程的web的應用程序,需要的朋友可以參考下
    2015-12-12

最新評論