<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java8 特性詳解 lambda表達(dá)式(一):使用篇

          共 10439字,需瀏覽 21分鐘

           ·

          2021-12-18 16:07

          點(diǎn)擊藍(lán)色“程序員黃小斜”關(guān)注我喲

          加個“星標(biāo)”,每天和你一起多進(jìn)步一點(diǎn)點(diǎn)

          在 Java 8之前,一個實(shí)現(xiàn)了只有一個抽象方法的接口的匿名類看起來更像Lambda 表達(dá)式。下面的代碼中,anonymousClass方法調(diào)用waitFor方法,參數(shù)是一個實(shí)現(xiàn)接口的Condition類,實(shí)現(xiàn)的功能為,當(dāng)滿足某些條件,Server 就會關(guān)閉。 下面的代碼是典型的匿名類的使用。

          void anonymousClass() {
          final Server server = new HttpServer();
          waitFor(new Condition() {
          @Override
          public Boolean isSatisfied() {
          return !server.isRunning();
          }
          }
          復(fù)制代碼


          下面的代碼用 Lambda 表達(dá)式實(shí)現(xiàn)相同的功能:

          void closure() { 
          Server server = new HttpServer();
          waitFor(() -> !server.isRunning());
          }
          復(fù)制代碼


          其實(shí),上面的waitFor方法,更接近于下面的代碼的描述:

          class WaitFor {
          static void waitFor(Condition condition) throws
          InterruptedException {
          while (!condition.isSatisfied())
          Thread.sleep(250);
          }
          }
          復(fù)制代碼


          一些理論上的區(qū)別 實(shí)際上,上面的兩種方法的實(shí)現(xiàn)都是閉包,后者的實(shí)現(xiàn)就是Lambda 表示式。這就意味著兩者都需要持有運(yùn)行時的環(huán)境。在 Java 8 之前,這就需要把匿名類所需要的一切復(fù)制給它。在上面的例子中,就需要把 server 屬性復(fù)制給匿名類。

          因?yàn)槭菑?fù)制,變量必須聲明為 final 類型,以保證在獲取和使用時不會被改變。Java 使用了優(yōu)雅的方式保證了變量不會被更新,所以我們不用顯式地把變量加上 final 修飾。

          Lambda 表達(dá)式則不需要拷貝變量到它的運(yùn)行環(huán)境中,從而 Lambda 表達(dá)式被當(dāng)做是一個真正的方法來對待,而不是一個類的實(shí)例。

          Lambda 表達(dá)式不需要每次都要被實(shí)例化,對于 Java 來說,帶來巨大的好處。不像實(shí)例化匿名類,對內(nèi)存的影響可以降到最小。

          總體來說,匿名方法和匿名類存在以下區(qū)別:

          類必須實(shí)例化,而方法不必;當(dāng)一個類被新建時,需要給對象分配內(nèi)存;方法只需要分配一次內(nèi)存,它被存儲在堆的永久區(qū)內(nèi);對象作用于它自己的數(shù)據(jù),而方法不會;靜態(tài)類里的方法類似于匿名方法的功能。

          一些具體的區(qū)別 匿名方法和匿名類有一些具體的區(qū)別,主要包括獲取語義和覆蓋變量。

          獲取語義 this 關(guān)鍵字是其中的一個語義上的區(qū)別。在匿名類中,this 指的是匿名類的實(shí)例,例如有了內(nèi)部類為 Foo$InnerClass,當(dāng)你引用內(nèi)部類閉包的作用域時,像Foo.this.x的代碼看起來就有些奇怪。在 Lambda 表達(dá)式中,this 指的就是閉包作用域,事實(shí)上,Lambda 表達(dá)式就是一個作用域,這就意味著你不需要從超類那里繼承任何名字,或是引入作用域的層級。你可以在作用域里直接訪問屬性,方法和局部變量。例如,下面的代碼中,Lambda 表達(dá)式可以直接訪問firstName變量。

          public class Example {
          private String firstName = "Tom";

          public void example() {
          Function addSurname = surname -> {
          // equivalent to this.firstName
          return firstName + " " + surname; // or even,
          };
          }
          }
          復(fù)制代碼


          這里的firstName就是this.firstName的簡寫。但是在匿名類中,你必須顯式地調(diào)用firstName,

          public class Example {
          private String firstName = "Jerry";

          public void anotherExample() {
          Function addSurname = new Function String>() {
          @Override
          public String apply(String surname) {
          return Example.this.firstName + " " + surname;
          }
          };
          }
          }


          1.lambda表達(dá)式

          Java8最值得學(xué)習(xí)的特性就是Lambda表達(dá)式和Stream API,如果有python或者javascript的語言基礎(chǔ),對理解Lambda表達(dá)式有很大幫助,因?yàn)镴ava正在將自己變的更高(Sha)級(Gua),更人性化。--------可以這么說lambda表達(dá)式其實(shí)就是實(shí)現(xiàn)SAM接口的語法糖。

          lambda寫的好可以極大的減少代碼冗余,同時可讀性也好過冗長的內(nèi)部類,匿名類。

          先列舉兩個常見的簡化(簡單的代碼同樣好理解)

          • 創(chuàng)建線程


          • 排序


          lambda表達(dá)式配合Java8新特性Stream API可以將業(yè)務(wù)功能通過函數(shù)式編程簡潔的實(shí)現(xiàn)。(為后面的例子做鋪墊)

          例如:


          這段代碼就是對一個字符串的列表,把其中包含的每個字符串都轉(zhuǎn)換成全小寫的字符串。注意代碼第四行的map方法調(diào)用,這里map方法就是接受了一個lambda表達(dá)式。

          1.1lambda表達(dá)式語法

          1.1.1lambda表達(dá)式的一般語法

          (Type1 param1, Type2 param2, ..., TypeN paramN) -> {
          statment1;
          statment2;
          //.............
          return statmentM;
          }

          這是lambda表達(dá)式的完全式語法,后面幾種語法是對它的簡化。

          1.1.2單參數(shù)語法

          param1 -> {
          statment1;
          statment2;
          //.............
          return statmentM;
          }

          當(dāng)lambda表達(dá)式的參數(shù)個數(shù)只有一個,可以省略小括號

          例如:將列表中的字符串轉(zhuǎn)換為全小寫

          ListproNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"}); ListlowercaseNames1 = proNames.stream().map(name -> {return name.toLowerCase();}).collect(Collectors.toList());

          1.1.3單語句寫法

          param1 -> statment

          當(dāng)lambda表達(dá)式只包含一條語句時,可以省略大括號、return和語句結(jié)尾的分號

          例如:將列表中的字符串轉(zhuǎn)換為全小寫

          ListproNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});

          ListlowercaseNames2 = proNames.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());

          1.1.4方法引用寫法

          (方法引用和lambda一樣是Java8新語言特性,后面會講到)

          Class or instance :: method

          例如:將列表中的字符串轉(zhuǎn)換為全小寫

          ListproNames = Arrays.asList(new String[]{"Ni","Hao","Lambda"});

          ListlowercaseNames3 = proNames.stream().map(String::toLowerCase).collect(Collectors.toList());

          1.2lambda表達(dá)式可使用的變量

          先舉例:

          //將為列表中的字符串添加前綴字符串 String waibu = "lambda :"; ListproStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"}); ListexecStrs = proStrs.stream().map(chuandi -> { Long zidingyi = System.currentTimeMillis(); return waibu + chuandi + " -----:" + zidingyi; }).collect(Collectors.toList()); execStrs.forEach(System.out::println);

          輸出:

          lambda :Ni -----:1474622341604 lambda :Hao -----:1474622341604 lambda :Lambda -----:1474622341604

          變量waibu :外部變量

          變量chuandi :傳遞變量

          變量zidingyi :內(nèi)部自定義變量

          lambda表達(dá)式可以訪問給它傳遞的變量,訪問自己內(nèi)部定義的變量,同時也能訪問它外部的變量。

          不過lambda表達(dá)式訪問外部變量有一個非常重要的限制:變量不可變(只是引用不可變,而不是真正的不可變)。

          當(dāng)在表達(dá)式內(nèi)部修改waibu = waibu + " ";時,IDE就會提示你:

          Local variable waibu defined in an enclosing scope must be final or effectively final

          編譯時會報錯。因?yàn)樽兞縲aibu被lambda表達(dá)式引用,所以編譯器會隱式的把其當(dāng)成final來處理。

          以前Java的匿名內(nèi)部類在訪問外部變量的時候,外部變量必須用final修飾?,F(xiàn)在java8對這個限制做了優(yōu)化,可以不用顯示使用final修飾,但是編譯器隱式當(dāng)成final來處理。

          1.3lambda表達(dá)式中的this概念

          在lambda中,this不是指向lambda表達(dá)式產(chǎn)生的那個SAM對象,而是聲明它的外部對象。

          例如:

          public class WhatThis {

           public void whatThis(){
          //轉(zhuǎn)全小寫
          List proStrs = Arrays.asList(new String[]{"Ni","Hao","Lambda"});
          List execStrs = proStrs.stream().map(str -> {
          System.out.println(this.getClass().getName());
          return str.toLowerCase();
          }).collect(Collectors.toList());
          execStrs.forEach(System.out::println);
          }

          public static void main(String[] args) {
          WhatThis wt = new WhatThis();
          wt.whatThis();
          }

          }

          輸出:

          com.wzg.test.WhatThis com.wzg.test.WhatThis com.wzg.test.WhatThis ni hao lambda

          2.方法引用和構(gòu)造器引用

          本人認(rèn)為是進(jìn)一步簡化lambda表達(dá)式的聲明的一種語法糖。

          前面的例子中已有使用到:execStrs.forEach(System.out::println);

          2.1方法引用

          objectName::instanceMethod

          ClassName::staticMethod

          ClassName::instanceMethod

          前兩種方式類似,等同于把lambda表達(dá)式的參數(shù)直接當(dāng)成instanceMethod|staticMethod的參數(shù)來調(diào)用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。

          最后一種方式,等同于把lambda表達(dá)式的第一個參數(shù)當(dāng)成instanceMethod的目標(biāo)對象,其他剩余參數(shù)當(dāng)成該方法的參數(shù)。比如String::toLowerCase等同于x->x.toLowerCase()。

          可以這么理解,前兩種是將傳入對象當(dāng)參數(shù)執(zhí)行方法,后一種是調(diào)用傳入對象的方法。

          2.2構(gòu)造器引用

          構(gòu)造器引用語法如下:ClassName::new,把lambda表達(dá)式的參數(shù)當(dāng)成ClassName構(gòu)造器的參數(shù) 。例如BigDecimal::new等同于x->new BigDecimal(x)。

          3.Stream語法

          兩句話理解Stream:

          1.Stream是元素的集合,這點(diǎn)讓Stream看起來用些類似Iterator;2.可以支持順序和并行的對原Stream進(jìn)行匯聚的操作;

          大家可以把Stream當(dāng)成一個裝飾后的Iterator。原始版本的Iterator,用戶只能逐個遍歷元素并對其執(zhí)行某些操作;包裝后的Stream,用戶只要給出需要對其包含的元素執(zhí)行什么操作,比如“過濾掉長度大于10的字符串”、“獲取每個字符串的首字母”等,具體這些操作如何應(yīng)用到每個元素上,就給Stream就好了!原先是人告訴計算機(jī)一步一步怎么做,現(xiàn)在是告訴計算機(jī)做什么,計算機(jī)自己決定怎么做。當(dāng)然這個“怎么做”還是比較弱的。

          例子:

          //Lists是Guava中的一個工具類 Listnums = Lists.newArrayList(1,null,3,4,null,6); nums.stream().filter(num -> num != null).count();

          上面這段代碼是獲取一個List中,元素不為null的個數(shù)。這段代碼雖然很簡短,但是卻是一個很好的入門級別的例子來體現(xiàn)如何使用Stream,正所謂“麻雀雖小五臟俱全”。我們現(xiàn)在開始深入解刨這個例子,完成以后你可能可以基本掌握Stream的用法!


          圖片就是對于Stream例子的一個解析,可以很清楚的看見:原本一條語句被三種顏色的框分割成了三個部分。紅色框中的語句是一個Stream的生命開始的地方,負(fù)責(zé)創(chuàng)建一個Stream實(shí)例;綠色框中的語句是賦予Stream靈魂的地方,把一個Stream轉(zhuǎn)換成另外一個Stream,紅框的語句生成的是一個包含所有nums變量的Stream,進(jìn)過綠框的filter方法以后,重新生成了一個過濾掉原nums列表所有null以后的Stream;藍(lán)色框中的語句是豐收的地方,把Stream的里面包含的內(nèi)容按照某種算法來匯聚成一個值,例子中是獲取Stream中包含的元素個數(shù)。如果這樣解析以后,還不理解,那就只能動用“核武器”–圖形化,一圖抵千言!


          使用Stream的基本步驟:

          1.創(chuàng)建Stream;2.轉(zhuǎn)換Stream,每次轉(zhuǎn)換原有Stream對象不改變,返回一個新的Stream對象(可以有多次轉(zhuǎn)換);3.對Stream進(jìn)行聚合(Reduce)操作,獲取想要的結(jié)果;

          3.1怎么得到Stream

          最常用的創(chuàng)建Stream有兩種途徑:

          1.通過Stream接口的靜態(tài)工廠方法(注意:Java8里接口可以帶靜態(tài)方法);2.通過Collection接口的默認(rèn)方法(默認(rèn)方法:Default method,也是Java8中的一個新特性,就是接口中的一個帶有實(shí)現(xiàn)的方法)–stream(),把一個Collection對象轉(zhuǎn)換成Stream

          3.1.1 使用Stream靜態(tài)方法來創(chuàng)建Stream

          1. of方法:有兩個overload方法,一個接受變長參數(shù),一個接口單一值

          StreamintegerStream = Stream.of(1, 2, 3, 5); StreamstringStream = Stream.of("taobao");

          2. generator方法:生成一個無限長度的Stream,其元素的生成是通過給定的Supplier(這個接口可以看成一個對象的工廠,每次調(diào)用返回一個給定類型的對象)

          Stream.generate(new Supplier() { @Override public Double get() { return Math.random(); } });

          Stream.generate(() -> Math.random()); Stream.generate(Math::random); 三條語句的作用都是一樣的,只是使用了lambda表達(dá)式和方法引用的語法來簡化代碼。每條語句其實(shí)都是生成一個無限長度的Stream,其中值是隨機(jī)的。這個無限長度Stream是懶加載,一般這種無限長度的Stream都會配合Stream的limit()方法來用。

          3. iterate方法:也是生成無限長度的Stream,和generator不同的是,其元素的生成是重復(fù)對給定的種子值(seed)調(diào)用用戶指定函數(shù)來生成的。其中包含的元素可以認(rèn)為是:seed,f(seed),f(f(seed))無限循環(huán)

          Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println); 這段代碼就是先獲取一個無限長度的正整數(shù)集合的Stream,然后取出前10個打印。千萬記住使用limit方法,不然會無限打印下去。

          3.1.2通過Collection子類獲取Stream

          Collection接口有一個stream方法,所以其所有子類都都可以獲取對應(yīng)的Stream對象。

          public interface Collectionextends Iterable{ //其他方法省略 default Streamstream() { return StreamSupport.stream(spliterator(), false); } }

          3.2轉(zhuǎn)換Stream

          轉(zhuǎn)換Stream其實(shí)就是把一個Stream通過某些行為轉(zhuǎn)換成一個新的Stream。Stream接口中定義了幾個常用的轉(zhuǎn)換方法,下面我們挑選幾個常用的轉(zhuǎn)換方法來解釋。1. distinct: 對于Stream中包含的元素進(jìn)行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重復(fù)的元素;


          2. filter: 對于Stream中包含的元素使用給定的過濾函數(shù)進(jìn)行過濾操作,新生成的Stream只包含符合條件的元素;


          3. map: 對于Stream中包含的元素使用給定的轉(zhuǎn)換函數(shù)進(jìn)行轉(zhuǎn)換操作,新生成的Stream只包含轉(zhuǎn)換生成的元素。這個方法有三個對于原始類型的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉(zhuǎn)換成一個新的Stream,這個新生成的Stream中的元素都是int類型。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;


          4. flatMap:和map類似,不同的是其每個元素轉(zhuǎn)換得到的是Stream對象,會把子Stream中的元素壓縮到父集合中;


          flatMap給一段代碼理解:

          Stream> inputStream = Stream.of(
          Arrays.asList(1),
          Arrays.asList(2, 3),
          Arrays.asList(4, 5, 6)
          );
          StreamoutputStream = inputStream.
          flatMap((childList) -> childList.stream());

          flatMap 把 input Stream 中的層級結(jié)構(gòu)扁平化,就是將最底層元素抽出來放到一起,最終 output 的新 Stream 里面已經(jīng)沒有 List 了,都是直接的數(shù)字。

          5. peek: 生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費(fèi)函數(shù)(Consumer實(shí)例),新Stream每個元素被消費(fèi)的時候都會執(zhí)行給定的消費(fèi)函數(shù);


          6. limit: 對一個Stream進(jìn)行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數(shù)小于N,那就獲取其所有的元素;


          7. skip: 返回一個丟棄原Stream的前N個元素后剩下元素組成的新Stream,如果原Stream中包含的元素個數(shù)小于N,那么返回空Stream;


          整體調(diào)用例子:

          Listnums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10); System.out.println(“sum is:”+nums.stream().filter(num -> num != null).distinct().mapToInt(num -> num * 2).peek(System.out::println).skip(2).limit(4).sum());

          這段代碼演示了上面介紹的所有轉(zhuǎn)換方法(除了flatMap),簡單解釋一下這段代碼的含義:給定一個Integer類型的List,獲取其對應(yīng)的Stream對象,然后進(jìn)行過濾掉null,再去重,再每個元素乘以2,再每個元素被消費(fèi)的時候打印自身,在跳過前兩個元素,最后去前四個元素進(jìn)行加和運(yùn)算(解釋一大堆,很像廢話,因?yàn)榛究戳朔椒椭酪鍪裁戳恕_@個就是聲明式編程的一大好處!)。大家可以參考上面對于每個方法的解釋,看看最終的輸出是什么。

          可能會有這樣的疑問:在對于一個Stream進(jìn)行多次轉(zhuǎn)換操作,每次都對Stream的每個元素進(jìn)行轉(zhuǎn)換,而且是執(zhí)行多次,這樣時間復(fù)雜度就是一個for循環(huán)里把所有操作都做掉的N(轉(zhuǎn)換的次數(shù))倍啊。其實(shí)不是這樣的,轉(zhuǎn)換操作都是lazy的,多個轉(zhuǎn)換操作只會在匯聚操作(見下節(jié))的時候融合起來,一次循環(huán)完成。我們可以這樣簡單的理解,Stream里有個操作函數(shù)的集合,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個集合中,在匯聚操作的時候循環(huán)Stream對應(yīng)的集合,然后對每個元素執(zhí)行所有的函數(shù)。

          3.3匯聚(Reduce)Stream

          匯聚操作(也稱為折疊)接受一個元素序列為輸入,反復(fù)使用某個合并操作,把序列中的元素合并成一個匯總的結(jié)果。比如查找一個數(shù)字列表的總和或者最大值,或者把這些數(shù)字累積成一個List對象。Stream接口有一些通用的匯聚操作,比如reduce()和collect();也有一些特定用途的匯聚操作,比如sum(),max()和count()。注意:sum方法不是所有的Stream對象都有的,只有IntStream、LongStream和DoubleStream是實(shí)例才有。

          下面會分兩部分來介紹匯聚操作:

          可變匯聚:把輸入的元素們累積到一個可變的容器中,比如Collection或者StringBuilder;其他匯聚:除去可變匯聚剩下的,一般都不是通過反復(fù)修改某個可變對象,而是通過把前一次的匯聚結(jié)果當(dāng)成下一次的入?yún)?,反?fù)如此。比如reduce,count,allMatch;

          3.3.1可變匯聚

          可變匯聚對應(yīng)的只有一個方法:collect,正如其名字顯示的,它可以把Stream中的要有元素收集到一個結(jié)果容器中(比如Collection)。先看一下最通用的collect方法的定義(還有其他override方法):

          R collect(Suppliersupplier, BiConsumeraccumulator, BiConsumercombiner); 先來看看這三個參數(shù)的含義:Supplier supplier是一個工廠函數(shù),用來生成一個新的容器;BiConsumer accumulator也是一個函數(shù),用來把Stream中的元素添加到結(jié)果容器中;BiConsumer combiner還是一個函數(shù),用來把中間狀態(tài)的多個結(jié)果容器合并成為一個(并發(fā)的時候會用到)。看暈了?來段代碼!

          Listnums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10); ListnumsWithoutNull = nums.stream().filter(num -> num != null). collect(() -> new ArrayList(), (list, item) -> list.add(item), (list1, list2) -> list1.addAll(list2)); 上面這段代碼就是對一個元素是Integer類型的List,先過濾掉全部的null,然后把剩下的元素收集到一個新的List中。進(jìn)一步看一下collect方法的三個參數(shù),都是lambda形式的函數(shù)。

          第一個函數(shù)生成一個新的ArrayList實(shí)例;第二個函數(shù)接受兩個參數(shù),第一個是前面生成的ArrayList對象,二個是stream中包含的元素,函數(shù)體就是把stream中的元素加入ArrayList對象中。第二個函數(shù)被反復(fù)調(diào)用直到原stream的元素被消費(fèi)完畢;第三個函數(shù)也是接受兩個參數(shù),這兩個都是ArrayList類型的,函數(shù)體就是把第二個ArrayList全部加入到第一個中;但是上面的collect方法調(diào)用也有點(diǎn)太復(fù)雜了,沒關(guān)系!我們來看一下collect方法另外一個override的版本,其依賴Collector](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html "Collector (Java Platform SE 8 )"))。

          R collect(Collector collector); 這樣清爽多了!Java8還給我們提供了Collector的工具類–Collectors](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html "Collectors (Java Platform SE 8 )")),其中已經(jīng)定義了一些靜態(tài)工廠方法,比如:Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。這樣的靜態(tài)方法還有很多,這里就不一一介紹了,大家可以直接去看JavaDoc。下面看看使用Collectors對于代碼的簡化:

          ListnumsWithoutNull = nums.stream().filter(num -> num != null). collect(Collectors.toList());

          3.3.2其他匯聚

          – reduce方法:reduce方法非常的通用,后面介紹的count,sum等都可以使用其實(shí)現(xiàn)。reduce方法有三個override的方法,本文介紹兩個最常用的。先來看reduce方法的第一種形式,其方法定義如下:

          Optionalreduce(BinaryOperatoraccumulator); 接受一個BinaryOperator類型的參數(shù),在使用的時候我們可以用lambda表達(dá)式來。

          Listints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println("ints sum is:" + ints.stream().reduce((sum, item) -> sum + item).get()); 可以看到reduce方法接受一個函數(shù),這個函數(shù)有兩個參數(shù),第一個參數(shù)是上次函數(shù)執(zhí)行的返回值(也稱為中間結(jié)果),第二個參數(shù)是stream中的元素,這個函數(shù)把這兩個值相加,得到的和會被賦值給下次執(zhí)行這個函數(shù)的第一個參數(shù)。要注意的是:第一次執(zhí)行的時候第一個參數(shù)的值是Stream的第一個元素,第二個參數(shù)是Stream的第二個元素。這個方法返回值類型是Optional,這是Java8防止出現(xiàn)NPE的一種可行方法,后面的文章會詳細(xì)介紹,這里就簡單的認(rèn)為是一個容器,其中可能會包含0個或者1個對象。這個過程可視化的結(jié)果如圖:


          reduce方法還有一個很常用的變種:

          T reduce(T identity, BinaryOperatoraccumulator); 這個定義上上面已經(jīng)介紹過的基本一致,不同的是:它允許用戶提供一個循環(huán)計算的初始值,如果Stream為空,就直接返回該值。而且這個方法不會返回Optional,因?yàn)槠洳粫霈F(xiàn)null值。下面直接給出例子,就不再做說明了。

          Listints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println("ints sum is:" + ints.stream().reduce(0, (sum, item) -> sum + item)); – count方法:獲取Stream中元素的個數(shù)。比較簡單,這里就直接給出例子,不做解釋了。

          Listints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println("ints sum is:" + ints.stream().count());

          – 搜索相關(guān) – allMatch:是不是Stream中的所有元素都滿足給定的匹配條件 – anyMatch:Stream中是否存在任何一個元素滿足匹配條件 – findFirst: 返回Stream中的第一個元素,如果Stream為空,返回空Optional – noneMatch:是不是Stream中的所有元素都不滿足給定的匹配條件 – max和min:使用給定的比較器(Operator),返回Stream中的最大|最小值 下面給出allMatch和max的例子,剩下的方法讀者當(dāng)成練習(xí)。

          查看源代碼打印幫助 List ints="Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);" item="">item < 100)); ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);>

          參考文章

          Java 中的 Lambda 表達(dá)式 - 掘金

          Java8特性詳解 lambda表達(dá)式 Stream - aoeiuv - 博客園

          微信公眾號【程序員黃小斜】作者是前螞蟻金服Java工程師,專注分享Java技術(shù)干貨和求職成長心得,不限于BAT面試,算法、計算機(jī)基礎(chǔ)、數(shù)據(jù)庫、分布式、spring全家桶、微服務(wù)、高并發(fā)、JVM、Docker容器,ELK、大數(shù)據(jù)等。關(guān)注后回復(fù)【book】領(lǐng)取精選20本Java面試必備精品電子書。

          —?【 THE END 】—
          公眾號[程序員黃小斜]全部博文已整理成一個目錄,請在公眾號里回復(fù)「m」獲取!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號并回復(fù) PDF?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 64
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰自拍99 | 黄色五月天婷婷 | 淫色777| 老熟妇乱伦视频 | 欧美黄A片视频 |