最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

我對java中l(wèi)ambda表達(dá)式的看法是相當(dāng)糾結(jié)的:

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

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

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

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

——————————

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

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

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

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

假設(shè)所有的會(huì)員都保存在一個(gè)List<Person>實(shí)例中。

這一節(jié)我們從一個(gè)非常簡單的方法開始,然后嘗試使用局部類和匿名類進(jìn)行實(shí)現(xiàn),到最后會(huì)逐步深入體驗(yàn)Lambda表達(dá)式的強(qiáng)大和高效。可以在這里找到完整的代碼。

方案一:一個(gè)個(gè)地創(chuàng)建查找符合指定標(biāo)準(zhǔn)的會(huì)員的方法

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

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

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

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

下面的方法較之printPersonsOlderThan適應(yīng)性更好一些;這個(gè)方法打印了在指定年齡段內(nèi)的會(huì)員信息:

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

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

方案三:在一個(gè)局部類中實(shí)現(xiàn)標(biāo)準(zhǔn)檢查

下面的方法打印了符合檢索標(biāo)準(zhǔn)的會(huì)員信息:

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

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

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

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

要使用這個(gè)類,需要?jiǎng)?chuàng)建一個(gè)實(shí)例并觸發(fā)printPersons方法:

  printPersons(
      roster, new CheckPersonEligibleForSelectiveService());

現(xiàn)在的代碼看起來不那么脆弱了——我們不需要因?yàn)镻erson類結(jié)構(gòu)的變化而重寫代碼。不過這里仍有額外的代碼:一個(gè)新定義的接口、為應(yīng)用中每個(gè)搜索標(biāo)準(zhǔn)定義了一個(gè)內(nèi)部類。

因?yàn)镃heckPersonEligibleForSelectiveService實(shí)現(xiàn)了一個(gè)接口,所以可以使用匿名類,而不需要再為每種標(biāo)準(zhǔn)分別定義一個(gè)內(nèi)部類。

方案四:使用匿名類實(shí)現(xiàn)標(biāo)準(zhǔn)檢查

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

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

這個(gè)方案減少了編碼量,因?yàn)椴辉傩枰獮槊總€(gè)要執(zhí)行的檢索方案創(chuàng)建新類。不過這樣做仍讓人有些不舒服:盡管CheckPerson接口只有一個(gè)方法,實(shí)現(xiàn)的匿名類仍是有些冗長笨重。此時(shí)可以使用Lambda表達(dá)式替換匿名類,下面會(huì)說下如何使用Lambda表達(dá)式替換匿名類。

方案五:使用Lambda表達(dá)式實(shí)現(xiàn)標(biāo)準(zhǔn)檢查

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

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

在這里還可以使用一個(gè)標(biāo)準(zhǔn)的函數(shù)接口來替換CheckPerson接口,從而進(jìn)一步簡化代碼。

方案六:在Lambda表達(dá)式中使用標(biāo)準(zhǔn)函數(shù)式接口

再來看一下CheckPerson接口:

  interface CheckPerson {
    boolean test(Person p);
  }

這是一個(gè)非常簡單的接口。因?yàn)橹挥幸粋€(gè)抽象方法,所以它也是一個(gè)函數(shù)式接口。這個(gè)抽象方法只接受一個(gè)參數(shù)并返回一個(gè)boolean值。這個(gè)抽象接口太過簡單了,以至于我們會(huì)考慮是否有必要在應(yīng)用中定義一個(gè)這樣的接口。此時(shí)可以考慮使用JDK定義的標(biāo)準(zhǔn)函數(shù)式接口,可以在java.util.function包下找到這些接口。

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

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

Predicate<T>接口是一個(gè)泛型接口。泛型類(或者是泛型接口)使用一對尖括號(<>)指定了一個(gè)或多個(gè)類型參數(shù)。在這個(gè)接口中只有一個(gè)類型參數(shù)。在使用具體類聲明或?qū)嵗粋€(gè)泛型類時(shí),就會(huì)獲得一個(gè)參數(shù)化類。比如說參數(shù)化類Predicate<Person>就是這樣的:

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

在這個(gè)參數(shù)化類中有一個(gè)與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();
      }
    }
  }

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

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

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

方案七:在整個(gè)應(yīng)用中使用lambda表達(dá)式

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

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

在這個(gè)方法中使用Predicate實(shí)例tester檢查了roster中的每個(gè)Person實(shí)例。如果Person實(shí)例符合tester中定義的檢查標(biāo)準(zhǔn),將會(huì)觸發(fā)Person實(shí)例的printPerson方法。

除了觸發(fā)printPerson方法,滿足tester標(biāo)準(zhǔn)的Person實(shí)例還可以執(zhí)行其他的方法。可以考慮使用lambda表達(dá)式指定要執(zhí)行的方法(私以為這個(gè)特性很好,解決了java中方法不能作為對象傳遞的問題)。現(xiàn)在需要一個(gè)類似printPerson方法的lambda表達(dá)式——一個(gè)只需要一個(gè)參數(shù)且返回為void的lambda表達(dá)式。記住一點(diǎn):要使用lambda表達(dá)式,需要先實(shí)現(xiàn)一個(gè)函數(shù)式接口。在這個(gè)例子中需要一個(gè)函數(shù)式接口,其中只包含一個(gè)抽象方法,這個(gè)抽象方法有個(gè)類型為Person的參數(shù),且返回為void??梢钥匆幌翵DK提供的標(biāo)準(zhǔn)函數(shù)式接口Consumer<T>,它有一個(gè)抽象方法void accept(T t)正好滿足這個(gè)要求。在下面的代碼中使用一個(gè)Consumer<T>的實(shí)例調(diào)用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);
      }
    }
  }

對應(yīng)這里,可以使用如下代碼篩選適齡服兵役的會(huì)員:

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

如果我們想做的事情不只是打印會(huì)員信息,而是更多的事情,比如驗(yàn)證會(huì)員身份、獲取會(huì)員聯(lián)系方式等等。此時(shí),我們需要一個(gè)有返回值方法的函數(shù)式接口。JDK的標(biāo)準(zhǔn)函數(shù)式接口Function<T,R>就有一個(gè)這樣的方法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中適齡服兵役的所有會(huì)員的郵箱信息并打印出來:

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

方案八:多使用泛型

再來回顧一下processPersonsWithFunction方法。下面是這個(gè)方法的泛型版本,新方法在參數(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);
      }
    }
  }

要打印適齡服兵役的會(huì)員信息可以像下面這樣調(diào)用processElements方法:

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

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

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

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

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

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

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

    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實(shí)例指定標(biāo)準(zhǔn)的對象

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

通過一個(gè)Function實(shí)例獲取對象匹配的值

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

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

void forEach(Consumer<? super T> action)

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

相關(guān)文章

  • Java實(shí)現(xiàn)將txt文件轉(zhuǎn)成xls文件的方法

    Java實(shí)現(xiàn)將txt文件轉(zhuǎn)成xls文件的方法

    今天小編就為大家分享一篇Java實(shí)現(xiàn)將txt文件轉(zhuǎn)成xls文件的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • Mybatis-plus多條件篩選分頁的實(shí)現(xiàn)

    Mybatis-plus多條件篩選分頁的實(shí)現(xiàn)

    本文主要介紹了Mybatis-plus多條件篩選分頁,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 利用Java連接Hadoop進(jìn)行編程

    利用Java連接Hadoop進(jìn)行編程

    這篇文章主要介紹了利用Java連接Hadoop進(jìn)行編程,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06
  • java 線程池的實(shí)現(xiàn)方法

    java 線程池的實(shí)現(xiàn)方法

    在本篇文章里小編給大家整理了關(guān)于java 線程池的實(shí)現(xiàn)方法,有興趣的朋友們可以學(xué)習(xí)參考下。
    2020-02-02
  • MyBatisPlus?大數(shù)據(jù)量查詢慢的問題解決

    MyBatisPlus?大數(shù)據(jù)量查詢慢的問題解決

    本文主要介紹了MyBatis?Plus?解決大數(shù)據(jù)量查詢慢問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • JavaWeb評論功能實(shí)現(xiàn)步驟以及代碼實(shí)例

    JavaWeb評論功能實(shí)現(xiàn)步驟以及代碼實(shí)例

    項(xiàng)目初始版本上線,有時(shí)間寫點(diǎn)東西記錄一下項(xiàng)目中的心得體會(huì),通過這個(gè)項(xiàng)目學(xué)習(xí)了很多,要寫下來的有很多,先從評論功能開始吧,下面這篇文章主要給大家介紹了關(guān)于JavaWeb評論功能實(shí)現(xiàn)步驟以及代碼的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Maven 多profile及指定編譯問題的解決

    Maven 多profile及指定編譯問題的解決

    這篇文章主要介紹了Maven 多profile及指定編譯問題的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis Generator去掉生成的注解

    MyBatis Generator去掉生成的注解

    這篇文章主要介紹了MyBatis Generator去掉生成的注解的相關(guān)資料,非常不錯(cuò)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • 詳解Spring FactoryBean靈活創(chuàng)建復(fù)雜對象的秘密武器

    詳解Spring FactoryBean靈活創(chuàng)建復(fù)雜對象的秘密武器

    FactoryBean是Spring框架中用于創(chuàng)建復(fù)雜Bean的接口,通過編程方式控制Bean的創(chuàng)建過程,它允許開發(fā)者自定義Bean的創(chuàng)建邏輯,適用于集成第三方庫、延遲初始化、動(dòng)態(tài)代理和統(tǒng)一管理資源等場景,本文介紹Spring FactoryBean創(chuàng)建復(fù)雜對象的相關(guān)操作,感興趣的朋友一起看看吧
    2025-02-02
  • 淺談在eclipse中如何修改svn的用戶名和密碼

    淺談在eclipse中如何修改svn的用戶名和密碼

    這篇文章主要介紹了在eclipse中如何修改svn的用戶名和密碼的方法,在eclipse中經(jīng)常用svn進(jìn)行代碼版本控制,提交或更新代碼的時(shí)候需要我們輸入用戶名和密碼。對此感興趣的話可以來了解一下
    2020-07-07

最新評論

集贤县| 陕西省| 西峡县| 遵化市| 若尔盖县| 莱芜市| 晋城| 高雄市| 宽城| 通海县| 谢通门县| 遵义县| 迁西县| 中江县| 武安市| 久治县| 新闻| 濮阳市| 梓潼县| 九台市| 绥德县| 岑溪市| 漳平市| 龙门县| 辽阳县| 汾西县| 霍城县| 苍山县| 新田县| 霍林郭勒市| 成安县| 黄龙县| 宁安市| 霍城县| 邓州市| 治多县| 集安市| 财经| 平利县| 嘉鱼县| 蓬溪县|