1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 10439字,需瀏覽 21分鐘

         ·

        2021-12-18 16:07

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

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

        在 Java 8之前,一個(gè)實(shí)現(xiàn)了只有一個(gè)抽象方法的接口的匿名類看起來更像Lambda 表達(dá)式。下面的代碼中,anonymousClass方法調(diào)用waitFor方法,參數(shù)是一個(gè)實(shí)現(xiàn)接口的Condition類,實(shí)現(xiàn)的功能為,當(dāng)滿足某些條件,Server 就會(huì)關(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)行時(shí)的環(huán)境。在 Java 8 之前,這就需要把匿名類所需要的一切復(fù)制給它。在上面的例子中,就需要把 server 屬性復(fù)制給匿名類。

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

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

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

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

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

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

        獲取語義 this 關(guān)鍵字是其中的一個(gè)語義上的區(qū)別。在匿名類中,this 指的是匿名類的實(shí)例,例如有了內(nèi)部類為 Foo$InnerClass,當(dāng)你引用內(nèi)部類閉包的作用域時(shí),像Foo.this.x的代碼看起來就有些奇怪。在 Lambda 表達(dá)式中,this 指的就是閉包作用域,事實(shí)上,Lambda 表達(dá)式就是一個(gè)作用域,這就意味著你不需要從超類那里繼承任何名字,或是引入作用域的層級(jí)。你可以在作用域里直接訪問屬性,方法和局部變量。例如,下面的代碼中,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的簡(jiǎn)寫。但是在匿名類中,你必須顯式地調(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ǔ),對(duì)理解Lambda表達(dá)式有很大幫助,因?yàn)镴ava正在將自己變的更高(Sha)級(jí)(Gua),更人性化。--------可以這么說lambda表達(dá)式其實(shí)就是實(shí)現(xiàn)SAM接口的語法糖。

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

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

        • 創(chuàng)建線程


        • 排序


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

        例如:


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

        1.1lambda表達(dá)式語法

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

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

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

        1.1.2單參數(shù)語法

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

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

        例如:將列表中的字符串轉(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á)式只包含一條語句時(shí),可以省略大括號(hào)、return和語句結(jié)尾的分號(hào)

        例如:將列表中的字符串轉(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新語言特性,后面會(huì)講到)

        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)部定義的變量,同時(shí)也能訪問它外部的變量。

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

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

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

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

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

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

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

        例如:

        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)一步簡(jiǎ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á)式的第一個(gè)參數(shù)當(dāng)成instanceMethod的目標(biāo)對(duì)象,其他剩余參數(shù)當(dāng)成該方法的參數(shù)。比如String::toLowerCase等同于x->x.toLowerCase()。

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

        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.可以支持順序和并行的對(duì)原Stream進(jìn)行匯聚的操作;

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

        例子:

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

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


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


        使用Stream的基本步驟:

        1.創(chuàng)建Stream;2.轉(zhuǎn)換Stream,每次轉(zhuǎn)換原有Stream對(duì)象不改變,返回一個(gè)新的Stream對(duì)象(可以有多次轉(zhuǎn)換);3.對(duì)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中的一個(gè)新特性,就是接口中的一個(gè)帶有實(shí)現(xiàn)的方法)–stream(),把一個(gè)Collection對(duì)象轉(zhuǎn)換成Stream

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

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

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

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

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

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

        3. iterate方法:也是生成無限長(zhǎng)度的Stream,和generator不同的是,其元素的生成是重復(fù)對(duì)給定的種子值(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); 這段代碼就是先獲取一個(gè)無限長(zhǎng)度的正整數(shù)集合的Stream,然后取出前10個(gè)打印。千萬記住使用limit方法,不然會(huì)無限打印下去。

        3.1.2通過Collection子類獲取Stream

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

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

        3.2轉(zhuǎn)換Stream

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


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


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


        4. flatMap:和map類似,不同的是其每個(gè)元素轉(zhuǎn)換得到的是Stream對(duì)象,會(huì)把子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 中的層級(jí)結(jié)構(gòu)扁平化,就是將最底層元素抽出來放到一起,最終 output 的新 Stream 里面已經(jīng)沒有 List 了,都是直接的數(shù)字。

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


        6. limit: 對(duì)一個(gè)Stream進(jìn)行截?cái)嗖僮?,獲取其前N個(gè)元素,如果原Stream中包含的元素個(gè)數(shù)小于N,那就獲取其所有的元素;


        7. skip: 返回一個(gè)丟棄原Stream的前N個(gè)元素后剩下元素組成的新Stream,如果原Stream中包含的元素個(gè)數(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),簡(jiǎn)單解釋一下這段代碼的含義:給定一個(gè)Integer類型的List,獲取其對(duì)應(yīng)的Stream對(duì)象,然后進(jìn)行過濾掉null,再去重,再每個(gè)元素乘以2,再每個(gè)元素被消費(fèi)的時(shí)候打印自身,在跳過前兩個(gè)元素,最后去前四個(gè)元素進(jìn)行加和運(yùn)算(解釋一大堆,很像廢話,因?yàn)榛究戳朔椒椭酪鍪裁戳?。這個(gè)就是聲明式編程的一大好處!)。大家可以參考上面對(duì)于每個(gè)方法的解釋,看看最終的輸出是什么。

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

        3.3匯聚(Reduce)Stream

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

        下面會(huì)分兩部分來介紹匯聚操作:

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

        3.3.1可變匯聚

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

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

        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)); 上面這段代碼就是對(duì)一個(gè)元素是Integer類型的List,先過濾掉全部的null,然后把剩下的元素收集到一個(gè)新的List中。進(jìn)一步看一下collect方法的三個(gè)參數(shù),都是lambda形式的函數(shù)。

        第一個(gè)函數(shù)生成一個(gè)新的ArrayList實(shí)例;第二個(gè)函數(shù)接受兩個(gè)參數(shù),第一個(gè)是前面生成的ArrayList對(duì)象,二個(gè)是stream中包含的元素,函數(shù)體就是把stream中的元素加入ArrayList對(duì)象中。第二個(gè)函數(shù)被反復(fù)調(diào)用直到原stream的元素被消費(fèi)完畢;第三個(gè)函數(shù)也是接受兩個(gè)參數(shù),這兩個(gè)都是ArrayList類型的,函數(shù)體就是把第二個(gè)ArrayList全部加入到第一個(gè)中;但是上面的collect方法調(diào)用也有點(diǎn)太復(fù)雜了,沒關(guān)系!我們來看一下collect方法另外一個(gè)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對(duì)于代碼的簡(jiǎn)化:

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

        3.3.2其他匯聚

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

        Optionalreduce(BinaryOperatoraccumulator); 接受一個(gè)BinaryOperator類型的參數(shù),在使用的時(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方法接受一個(gè)函數(shù),這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是上次函數(shù)執(zhí)行的返回值(也稱為中間結(jié)果),第二個(gè)參數(shù)是stream中的元素,這個(gè)函數(shù)把這兩個(gè)值相加,得到的和會(huì)被賦值給下次執(zhí)行這個(gè)函數(shù)的第一個(gè)參數(shù)。要注意的是:第一次執(zhí)行的時(shí)候第一個(gè)參數(shù)的值是Stream的第一個(gè)元素,第二個(gè)參數(shù)是Stream的第二個(gè)元素。這個(gè)方法返回值類型是Optional,這是Java8防止出現(xiàn)NPE的一種可行方法,后面的文章會(huì)詳細(xì)介紹,這里就簡(jiǎn)單的認(rèn)為是一個(gè)容器,其中可能會(huì)包含0個(gè)或者1個(gè)對(duì)象。這個(gè)過程可視化的結(jié)果如圖:


        reduce方法還有一個(gè)很常用的變種:

        T reduce(T identity, BinaryOperatoraccumulator); 這個(gè)定義上上面已經(jīng)介紹過的基本一致,不同的是:它允許用戶提供一個(gè)循環(huán)計(jì)算的初始值,如果Stream為空,就直接返回該值。而且這個(gè)方法不會(huì)返回Optional,因?yàn)槠洳粫?huì)出現(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中元素的個(gè)數(shù)。比較簡(jiǎn)單,這里就直接給出例子,不做解釋了。

        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中是否存在任何一個(gè)元素滿足匹配條件 – findFirst: 返回Stream中的第一個(gè)元素,如果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 - 博客園

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

        —?【 THE END 】—
        公眾號(hào)[程序員黃小斜]全部博文已整理成一個(gè)目錄,請(qǐng)?jiān)诠娞?hào)里回復(fù)「m」獲取!

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

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

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

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

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品98| 日屄免费视频| 亚洲综合伊人| 91中文字幕在线| 中国字幕在线观看韩国电影| x88AV吊钟奶熟女| 熟女天堂| 国产Av影视| 亚洲日韩黄色| 国产精品国产三级国产AⅤ| 中文字幕人成人乱| 正在播放ADN156松下纱荣子| 国产高清秘成人久久| 91精品久久久久久| 水果派解说在线观看| 欧美日韩99| 中文字幕不卡一区| 激情精品| 99精品色| 伊人综合大香蕉| 国产午夜精品一区二区三区牛牛| 北条麻妃91视频| 中文字幕免费毛片| 亚洲免费av在线| 国产精品欧美性爱| 丰满少妇一级片| 深爱激情综合| 特黄AAAAAAAA片视频| 综合+++夜夜| 日日撸| 亭亭五月天| 人人爽人人爽人人爽| 久久婷五月| 抽插网| 抽插视频免费| 国产日韩在线视频| 日韩精品久久久久久久酒店| 麻豆传媒在线播放| 久久国产高清| 人人看人人爱| 人人插人人射| h片网站在线观看| 国产乱子伦一区二区三区在线观看 | 特写毛茸茸BBwBBwBBw| 91视频在线观看免费大全| 在线乱视频| 起碰视频| 特级西西444www| 老太色HD色老太HD.| 999热这里只有精品| 3D动漫精选啪啪一期二期三期| 久久无码影院| 成人做爰100部片视频| 偷拍92| brazzers疯狂作爱| 中文字幕免费看高清| 蜜桃av秘一区二区三区| 6969电视影片最新更新| 天天操免费| 中文字幕日韩无码电影| 中文字幕午夜福利| 国产換妻4P视频| 米奇7777狠狠狠狠| 国产婷婷色一区二区| 瘦精品无码一区二区三区四区五区六区七区八区 | a视频在线| av毛片| 一区二区无码视频| 黄色成人在线观看| 国产成人秘一区二区三区东京热| 大香蕉黄色电影| 牛牛AV| 国产清纯可爱美女自卫裸贷偷情| chip少妇性| 特级丰满少妇免费观看| 欧美性爱永久| 国产成人片色情AAAA片| 任你爽在线视频| 无码日逼视频| 北条麻妃精品视频| 亚洲成人视频在线播放| 亚洲精品日韩综合观看成人91 | 外国一级片| 国产黄色影院| 国产欧美熟妇另类久久久| 亚洲性爱电影| 欧美狠狠干| 中文字幕激情精品| 开心色色五月天| 日韩精品你懂的| 国产欧美在线观看不卡| 91视频熟女| 国产极品无码| 影音先锋黄色资源| 亚洲国产视频一区| 欧美成人一级片| 国产乱子伦一区二区三区在线观看 | 欧美成人午夜视频| 日韩人妻无码中文字幕| 黄页网站在线免费观看| 女人的天堂AV| 波多野结衣av在线| 最近中文字幕高清2019中文字幕 | 天天操狠狠操| 人妻av在线| 三级片在线观看视频| 少妇做爱视频| 狼友初视频在线观看| 欧美日韩在线观看一区| 久久午夜无码鲁丝午夜精品| 亚洲日韩色色| 免费的黄色视频在线观看| 日韩中文字幕视频在线观看| 无码欧美成人AAAA三区在线| 熟女AV888| 无码看片| 亚洲欧美性爱视频| 亚洲黄色精品| 日韩中文在线观看| 久草黄色电影在线观看| 97这里只有精品| 日日夜夜草| 天天舔九色婷婷| 97干视频| 玖玖av| 亚洲狼人天堂| 色五月婷婷五月| 中文黄片| 操骚逼视频| 日韩中文字| 欧美后门菊门交4| 91中文无码| 中文字幕免费观看视频| 日韩乱伦中文字幕| 伊人中文在线| 夜夜狠狠擅视频| 久草资源在线| 国产探花视频在线免费观看| 日本无码片| 熟女视频网| 久久大香蕉网| 亚洲天堂日本| 水蜜桃视频免费观看| 五月激情丁香| 欧美一区二区| AV无码不卡| 91欧美精品成人AAA片| 性欧美欧美巨大69| 色屁屁草草影院ccyycom| 中文乱伦视频| 亚洲福利视频在线| 在线观看无码| A级片毛片| 日韩欧美天堂| 国产精品V亚洲精品V日韩精品| 九九精品视频在线观看| 天天操b| 麻豆91麻豆国产传媒| 中文字幕一区二区蜜桃| 91麻豆国产在线| 高清无码视频免费| 欧美一级AA大片免费看视频| 久久久久伊人| 成人午夜免费视频| 亚洲一区二区黄色电影视频网站| 91大熟女91大腚女人| 久久久国产一区| 人妻精品久久久久中文字幕69| 黄片在线免费观看视频| 国产成人AV片| 欧美性爱永久| 操逼手机视频| 骚逼逼影院| 国产三级国产三级国产| 亚洲无码在线电影| 92午夜福利天堂视频2019| 激情青青草| 亚洲专区区免费| 久久一卡二卡| 色色视频网站| 日本不卡视频| 亚洲素人无码| 少妇精品久久久久久久久久 | 日韩在线高清视频| 日韩一区二区三区免费视频| 大香蕉欧美在线| 欧美成人在线观看视频| av资源在线| 小早川怜子精品一区二区| 色播欧美| 国产色视频一区二区三区QQ号| 一本一道无码| 亚洲视频在线播放| 亚洲一区自拍| 日韩一级免费在线观看| 日韩欧美123| 成人做爰A片AAA毛真人| 综合影院| 在线国产激情视频| 一道本无码在线观看| 日韩人妻无码中文字幕| 91视频网| 久久黄色成人视频| 自拍亚洲欧美| 亚洲精品一区二区三| 最好看的2019中文在线大全电影| 午夜三级视频| 色呦呦视频在线观看| 风流少妇一区二区三区91| 国产在线第一页| 亚洲成人免费福利| 成人在线黄片| Japanese在线观看| 午夜无码福利| 人人看人人搞人人摸| 国产AV剧情| 天天草天天干| 韩日毛片| 久久亚洲Aⅴ成人无码国产丝袜| www.xxx| 国产伦精一品二品三品app| 内射免费看| 在线观看免费欧美操逼视频| 伊人久久无码| 无码窝在线观看| 自拍偷拍网站| 色五月视频在线| 99精品视频16在线免费观看 | 蜜桃91在线| 伊人影院在线观看| 99热这里只有精品99| 久久人人做| 久草视频这里只有精品| 毛片毛片毛片毛片毛片毛片| 乱伦精品| 日本一区二区三区视频在线观看 | 激情av在线观看| 九九热视频99| AV在线资源网| 俺去俺来也在线www色官网| 伊人五月丁香| 肏逼网址| 豆花成人社区,视频| 尤物综合网| 69视频在线播放| 搡BBBB| 风情万种AV| 少妇做爱特级AAA| 亚洲日本高清| 一区二区三区在线播放| 亚洲AV永久无码精品国产精| 人人爽久久涩噜噜噜网站| 大香蕉69| www.伊人大香蕉| 亚洲色婷婷五月天| 日韩在线视频网站| 日本高清视频网站| 久草电影在线观看| 一区高清无码| 性欧美成人播放77777| 自拍偷拍亚洲无码| 四川女人毛多水多A片| 中文字幕乱码免费综合久久| 91久久久久久久久久久久18| 国产91探花秘入口| 成人福利视频| 美女91小视频| 91探花视频在线观看| 国产特級黃色大片| 亚洲国产精品精JIZZ老师| 成人网站毛片| 欧美三级视频| 好男人一区二区三区在线观看| 亚洲无码在线免费| 99热免费| 国产精品九九九九九九| 天天日天天射天天操| 深爱婷婷网| H网站在线观看| 成人av无码| 人人操夜夜| 大香蕉网伊人在线| 91伊人网| 国产在线色视频| 超碰人人爱国产视| 99在线免费观看视频| 伊人久久大香蕉国产| 新妺妺窝窝777777野外| 黄片视频在线观看| 国产日韩二区| 亚洲高清av| 青操AV| 色婷婷在线影院| 亚洲中文字幕网| 国产AV综合网| 天天想夜夜操| 最近中文字幕高清2019中文字幕| 中文字幕成人影片| 国产一区二区三区免费播放| 伊人综合色|