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>

        Lambda表達(dá)式和函數(shù)式編程

        共 17622字,需瀏覽 36分鐘

         ·

        2021-03-23 21:19

        點擊關(guān)注,與你共同成長!


        Lambda表達(dá)式和函數(shù)式編程

        一.函數(shù)式編程概念

        (a,b) -> {xxx} 

        參數(shù) -> 方法體 左側(cè)一個參數(shù)時()可以省略,右側(cè)就一句方法體時{}可以省略

        二.JDK8引入的函數(shù)是編程接口類

        要想學(xué)習(xí)函數(shù)式編程一定要知道jdk提供的四種類型的函數(shù)式編程接口

        1.Function<T, R>  該類型的方法接收一個T類型的參數(shù),返回一個R類型的結(jié)果
        2.Consumer<T>  該類型方法接收一個T類型的參數(shù),無返回值
        3.Supplier<T>  該類型方法不接收任何參數(shù),返回一個T類型參數(shù)
        4.Predicate<T>  該類型方法接收一個T類型的參數(shù),返回一個Boolean類型返回值
        5.Optional<T> 該類型方法既主要在lambda函數(shù)式編程中處理空值情況

        三.流的創(chuàng)建方式

        1.數(shù)組轉(zhuǎn)流(Arrays.stream())

        int[] a = {123};
        Arrays.stream(a).peek(e -> log.info(e)).collect(Collectors.toList());

        2.集合類型轉(zhuǎn)流(Collection.stream())

        List<User> userList = new ArrayList<>();
        userList.stream().peek(user -> log.info(user)).collect(Collectors.toList());

        3.任意類型對象轉(zhuǎn)流(Stream.Of())

        添加不同類型的對象會變成一個Object類型的流

        Stream.of(1,2,3).peek(e -> log.info(e)).collect(Collectors.toList());

        4.迭代器添加元素轉(zhuǎn)流(Stream.iterate())

        迭代器內(nèi)第一個參數(shù)為初始值,第二個參數(shù)為一個lambda表達(dá)式,因為這個循環(huán)是個死循環(huán)所以這邊limit了前10個元素

        Stream.iterate(0,n -> n+1).limit(10).peek(e -> log.info(e)).collect(Collectors.toList());

        5.generate生成流(Stream.generate())

        迭代器內(nèi)第一個參數(shù)為一個lambda表達(dá)式,因為這個循環(huán)是個死循環(huán)所以這邊limit了前10個元素

        Stream.generate(() -> Math.random()).limit(10).peek(e -> log.info(e)).collect(Collectors.toList());

        6.StreamSupport生成流(Stream.generate())

        其實集合轉(zhuǎn)流的方式底層就是用的StreamSupport生成流的方式,大家可以去源碼看下

        Stream.generate()生成流的方式不常用,因為一般我們操作集合數(shù)組時直接用JDK封裝好的轉(zhuǎn)流方式就可以了,這邊不做演示。

        該方法有兩個參數(shù),第一個參數(shù)是迭代器Spliterator對象,第二個參數(shù)是是否啟用并行流的true/false值

        7.IntStream整形流

        IntStream繼承了BaseStream,有基礎(chǔ)的流操作功能等

        IntStream.range(0,5).boxed().peek(e -> log.info(e)).collect(Collectors.toList());

        range()左閉右開,rangeClosed()左右都是閉區(qū)間,boxed()方法將基本類型轉(zhuǎn)包裝類型

        8.Stream.builder()構(gòu)造器生成流

        Stream.builder()這種方式需要最后調(diào)用build()方法,本質(zhì)上和Stream.Of()相同

        Stream.Builder<User> userBuilder= Stream.builder();
        userBuilder.add(new User()).add(new User()).add(new User()).build().collect(Collectors.toList());

        四.常見操作符

        1.中間操作符

        User對象,假設(shè)User對象具有id,name,age三個屬性,下面的例子以操作User對象為例

        User user = new User();
        List<User> userList = new ArrayList<>();

        filter():過濾符合條件的數(shù)據(jù)流傳給下層操作符處理

        Optional<User> user1 = userList.stream().filter(user -> user.getAge() > 20).findFirst();

        map():遍歷數(shù)據(jù),可在map中將原始對象轉(zhuǎn)換為其他類型對象后返回新對象數(shù)據(jù)流傳給下層操作符操作

        peek():一般用于打印流操作中間狀態(tài)的元素詳情等,一般并無實際意義

        findAny():返回流中的第一個元素,在串行流中和findFirst()功能一樣,在并行流中返回的是最快處理完的那個線程的數(shù)據(jù),所以說在并行流操作中,對數(shù)據(jù)沒有順序上的要求,那么findAny的效率會比findFirst要快的

        Optional<User> user2 = userList.stream().filter(user -> user.getAge() > 20).findAny();

        findFirst(): 返回流中的第一個元素

        Optional<User> user1 = userList.stream().filter(user -> user.getAge() > 20).findFirst();

        sort():排序,數(shù)字類型默認(rèn)升序,中文和英文等按字典序排序,可以傳入自定義的比較器(第一個參數(shù)compareTo()第二個參數(shù)就是升序,第二個參數(shù)compareTo()第一個參數(shù)就是降序)

        userList.stream().sort().collect(Collectors.toList());
        userList.stream().sort(Compartor.reverseOrder()).collect(Collectors.toList());

        flatMap():參數(shù)是流,主要使用場景是處理高階嵌套的流,將高階流扁平化。例如:父子對象常見的集合屬性

        第一個應(yīng)用場景:一個用戶可能有多重角色,典型一對多父子類型

        userList.stream().flatMap(user -> user.getRoles().stream()).peek(role -> log.info(role)).collect(Collectors.toList());

        第二個應(yīng)用場景:在流中產(chǎn)生了Optional元素,想要取出Optional中的具體類型對象來操作

        此時假設(shè)角色集合返回的是 Optional<List<Role>>類型

        userList.stream().map(user->user.getRoles().stream()).flatMap(Optional::stream).peek(role->log.info(role)).count());

        2.終端操作符(count、min、max具有統(tǒng)計意義)

        User對象,假設(shè)User對象具有id,name,age三個屬性,下面的例子以操作User對象為例

        User user = new User();
        List<User> userList = new ArrayList<>();

        forEach():遍歷集合,非并行流時流中的元素是順序性的,并行流時流中的元素不能保證是順序性的

        將所有用戶的年齡都改為20歲

        userList.stream().forEach(user -> user.setAge(20));

        forEachOrder():遍歷集合,主要用于在并行流中想按排序的順序操作流中元素,如果不是并行流那么forEachOrder和forEach沒有任何區(qū)別

        按年齡大小輸出用戶名稱

        userList.parallelStream().sorted(Comparator.comparing(User::getAge)).forEachOrdered(user -> log.info("用戶名稱" + user.getName()));

        anyMatch():集合中有任何一個滿足條件的數(shù)據(jù)就會返回true,反之返回false(存在短路的優(yōu)點有結(jié)果了就會返回,不會繼續(xù)遍歷下去)

        boolean result = userList.stream().anyMatch(user -> user.getAge() > 20);

        如果anyMatch()方法中的函數(shù)體很長也可以這樣操作

        Predicate<User> predicate = user -> user.getAge() > 20;
        boolean result = userList.stream().anyMatch(predicate);

        allMatch():集合中所有數(shù)據(jù)都滿足條件的數(shù)據(jù)才會返回true,任意一條數(shù)據(jù)不滿足條件都會返回false

        集合中有任何一個滿足條件的數(shù)據(jù)就會返回true,反之返回false

        boolean result = userList.stream().anyMatch(user -> user.getAge() > 20);

        如果allMatch()方法中的函數(shù)體很長也可以這樣操作

        Predicate<User> predicate = user -> user.getAge() > 20;
        boolean result = userList.stream().allMatch(predicate);

        noneMatch():集合中沒有任何一個匹配條件的元素時返回true,反之返回false

        boolean result = userList.stream().noneMatch(user -> user.getAge() > 20);

        如果noneMatch()方法中的函數(shù)體很長也可以這樣操作

        Predicate<User> predicate = user -> user.getAge() > 20;
        boolean result = userList.stream().noneMatch(predicate);

        count():統(tǒng)計滿足條件的用戶數(shù)

        long count = userList.stream().filter(user -> user.getAge() > 20).count();

        min():排序后取出最小的用戶數(shù)據(jù)

        Optional<User> min = userList.stream().min(Comparator.comparing(User::getAge));

        max():排序后取出最大的用戶數(shù)據(jù)

        Optional<User> max = userList.stream().max(Comparator.comparing(User::getAge));

        reduce():執(zhí)行歸集操作,某種程度上和Collect作用類似,設(shè)計上reduce應(yīng)該和不可變得對象一起工作,如果對象是可變的,也可以得到結(jié)果,但是不是線程安全的,性能要弱于Collect,但是很靈活

        第一個參數(shù)是初始值(可以不設(shè)置,不設(shè)置默認(rèn)流中的第一個元素為初始值),第二個參數(shù)是個函數(shù),函數(shù)的第一個參數(shù)是累加器,第二個參數(shù)是當(dāng)前值,第三個參數(shù)是在并行流時會每個分片處理的線程會有一個臨時的值,這個參數(shù)為合并策略。

        累加器什么意思呢?就是第一次進(jìn)來累加器取初始值,然后每次循環(huán)用當(dāng)前的值加在累加器上,累加器相當(dāng)于值得總和,reduce也是循環(huán)處理對象的

        userList.stream().map(User::getAge).reduce(0,(Integer acc, Integer curr)-> Integer.sum(acc,curr));

        userList.parallelStream().reduce(
            Collections.EMPTY_LIST,

            (acc,curr) ->{
                List<User> newAcc = new ArrayList<>();
                newAcc.addAll(acc);
                newAcc.addAll(curr);
                return newAcc;
            },

            (left,right)->{
                List<User> merged = new ArrayList<>();
                merged.addAll(left);
                merged.addAll(right);
                return merged;
            }
          );

        3.常見集合對象收集器

        toList():將結(jié)果收集為一個List集合

        Stream.iterate(0,n -> n+1).collect(Collectors.toList());

        toSet():將結(jié)果收集為一個Set集合,Set集合元素不會重復(fù)

        Stream.iterate(0,n -> n+1).collect(Collectors.toSet());

        toMap():將結(jié)果收集為一個Map集合,鍵值對的形式,收集為Map集合時,有3種參數(shù)類型的重載方法可選

        2個參數(shù)的情況,toMap()中的第一個參數(shù)代表要收集Map結(jié)構(gòu)的key,第二個參數(shù)代表要收集Map結(jié)構(gòu)的value 其中Function.identity()和user -> user是等價的它代表輸入和輸出相同是Function中的一個靜態(tài)方法(大家可以看下源碼)

        userList.stream().collect(Collectors.toMap(User::getId, user -> user));
        userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

        3個參數(shù)的情況,toMap()中的第三個參數(shù)代表當(dāng)Key產(chǎn)生了重復(fù)值,那么在第三個參數(shù)方法中處理(用于合并的方法參數(shù))

        userList.stream().collect(Collectors.toMap(User::getId, user -> user, (oldValue,newValue) -> oldValue));

        4個參數(shù)的情況,toMap()中的第四個參數(shù)代表構(gòu)建Map的工廠,一般用于不想返回系統(tǒng)默認(rèn)的HashMap結(jié)構(gòu),比如返回TreeMap等

        userList.stream().collect(Collectors.toMap(User::getId, user -> user, (oldValue,newValue) -> oldValue, TreeMap::new));

        toCollection():將結(jié)果收集為一個自定義的集合類型

        TreeSet treeSet = userList.stream().collect(Collectors.toCollection(() -> new TreeSet(Comparator.comparing(User::getAge))));

        4.收集器中的聚合計算,分組統(tǒng)計和收集器

        首先我們來說下收集器中的聚合函數(shù)哈,雖然在數(shù)據(jù)庫層面提供了分組,求平均值,計算數(shù)量,最大值,最小值等功能,但不代表我們沒有在Lambda中完成上述操作的需求,因為畢竟是在內(nèi)存中完成的聚合計算,有的時候性能會比數(shù)據(jù)庫層面要提升很多

        averagingXXX():求平均值,可以轉(zhuǎn)為3中數(shù)字類型(Double,Integer,Long)

        所有用戶年齡的平均值

        Integer aveAge = userList.stream().collect(Collectors.averagingInt(User::getAge));
        Double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));
        Long aveAge = userList.stream().collect(Collectors.averagingLong(User::getAge));

        summingXXX():求和,可以轉(zhuǎn)為3中數(shù)字類型(Double,Integer,Long)

        Integer aveAge = userList.stream().collect(Collectors.summingInt(User::getAge));
        Double aveAge = userList.stream().collect(Collectors.summingDouble(User::getAge));
        Long aveAge = userList.stream().collect(Collectors.summingLong(User::getAge));

        counting():計數(shù),計算滿足條件的對象或值的數(shù)量

        Map<String, Long> stringLongMap = userList.stream().collect(Collectors.groupingBy(User::getName, Collectors.counting()));

        maxBy():取最大值,方法中需要傳進(jìn)去一個比較器,不然它不知道按哪一個值比較大小,返回一個Optional對象

        Optional<User> user = userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge)));

        minBy():取最小值,方法中需要傳進(jìn)去一個比較器,不然它不知道按哪一個值比較大小,返回一個Optional對象

        Optional<User> user = userList.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge)));

        summarizingXXX():為了方便我們操作,這個方法里統(tǒng)計了上面所有的值,返回一個XXXSummaryStatistics對象,我們可以按需取值

        IntSummaryStatistics intSummaryStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge));
        LongSummaryStatistics longSummaryStatistics = userList.stream().collect(Collectors.summarizingLong(User::getAge));
        DoubleSummaryStatistics doubleSummaryStatistics = userList.stream().collect(Collectors.summarizingDouble(User::getAge));

        groupingBy():以某一個值分組,默認(rèn)返回一個Map,groupingBy方法中可以繼續(xù)下一步的流操作(downstream),一般在業(yè)務(wù)中和mapping連用比較多

        User對象轉(zhuǎn)為UserDto對象:

        Map<Long, List<UserDto>> map = userList.stream().collect(Collectors.groupingBy(
            User::getId,
            Collectors.mapping(
                user -> new UserDto(user.getId(), user.getName(), user.getAge()),
                Collectors.toList())
        ));

        區(qū)域倉地址信息按省份id分組后計算市區(qū)的數(shù)量:

        Map<Long, Long> cityCountMap = Optional.ofNullable(warehouseInfo.getAddr()).orElse(new ArrayList<>()).stream().collect(Collectors.groupingBy(WarehouseAddr::getAddrId1, Collectors.counting()));

        區(qū)域倉地址信息按省份id分組后將市區(qū)組裝為一個List集合

        Map<Long, List<Long>> haveStockAreaMap = Optional.ofNullable(warehouseInfo.getAddr()).orElse(new ArrayList<>()).stream().collect(Collectors.groupingBy(WarehouseAddr::getAddrId1, Collectors.mapping(
        WarehouseAddr::getAddrId2, Collectors.toList())));

        partitioningBy():partitioningBy和groupingBy都是用于將數(shù)據(jù)進(jìn)行分組的函數(shù)。

        兩者的區(qū)別主要是參數(shù)返回值不同,partitioningBy又被稱為分區(qū)函數(shù),重載的分區(qū)函數(shù)可以傳遞下游流操作,比如繼續(xù)分組等

        看源碼可以看出函數(shù)的參數(shù)一個Predicate接口,那么這個接口的返回值是boolean類型的,也只能是boolean類型,然后他的返回值是Map的key是boolean類型,也就是這個函數(shù)的返回值只能將數(shù)據(jù)分為兩組也就是ture和false兩組數(shù)據(jù)。

        public static <T>
            Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
                return partitioningBy(predicate, toList());
            }
            
        public static <T, D, A>
            Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                            Collector<? super T, A, D> downstream){
                                                            }
                                                            
        public static <T, K> Collector<T, ?, Map<K, List<T>>>
            groupingBy(Function<? super T, ? extends K> classifier) {
                return groupingBy(classifier, toList());
            }

        joining():主要用于字符串的連接,一般用3個參數(shù)的重載方法,第一個參數(shù)是以"符號”連接每個對象,第二個參數(shù)是整體返回對象的前綴,第三個參數(shù)是整體返回對象的后綴

        以一個拼接訪問參數(shù)為例:

        Map<String,String> paramMap = new HashMap();
        paramMap.put("name","張三");
        paramMap.put("age","20");
        paramMap.put("email","[email protected]");
        paramMap.keySet().stream().map(key - > key + "=" paramMap.get(key)).sorted().collect(Collectors.joining("&","http://localhost:8080/api?",""))

        mapping():和常用中間操作符map()功能類似,第二個參數(shù)為下游流操作函數(shù),主要處理中間類型轉(zhuǎn)換等,可以一直用流操作串下去

        List<String> list = Lists.newArrayList("bb""ddd""cc""a");
        Map<Integer,TreeSet<String>> result = list.stream().collect(
            Collectors.groupingBy(
                    String::length,
                    Collectors.mapping(
                            String::toUpperCase,
                            Collectors.filtering(
                                    s -> s.length() > 1,
                                    Collectors.toCollection(TreeSet::new)
        ))));

        collectingAndThen():一般用于先收集一個集合后,再對收集后的集合做一些操作

        Map<String, Double> collect1 = userList.stream().collect(
            Collectors.groupingBy(
                    User::getName,
                    Collectors.collectingAndThen(
                            Collectors.toList(),
                            e -> {
                                return e.stream().collect(Collectors.averagingDouble(User::getAge));
                            }
                    )
        ));

        reducing():和reduce操作類似

        五.Optional流操作

        Optional是Java8新增的在java.util包下,主要用來輔助處理Java流式操作中的null值,它在返回結(jié)果之上又封裝了一層,封裝的這層永遠(yuǎn)不會出現(xiàn)null值,來確保我們在用lambda流操作時不會中斷

        1.生成Optional對象的方式

        因為其構(gòu)造方法是私有的,所以只能通過靜態(tài)的構(gòu)造器來創(chuàng)建Optional對象

        Optional.<>of():生成Optional對象,接收的對象不能為null否則拋出NullPointerException異常

        Optional.of(userList).ifPresent(users -> users.stream().filter(user -> user.getAge() > 20).count());

        Optional.<>ofNullable():生成Optional對象,接收的對象可以為null,如果為null則內(nèi)部返回Optional.<>empty()對象

        Optional.ofNullable(userList).orElse(new ArrayList<>());

        Optional.<>empty():生成一個空Optional對象,源碼注釋上寫著不能保證這個對象是單例的,具體原因還沒搞清,各位有懂得可以評論區(qū)為筆者解答下(笑出鵝叫)

        2.Optional常見操作符

        isPresent:判斷Optional值是否存在,存在返回true,不存在返回false

        boolean present = Optional.ofNullable(userList).isPresent();
         if (present){
             //TODO 業(yè)務(wù)邏輯
         }
         else {
             //TODO 業(yè)務(wù)邏輯
         }

        orElse:Optional的值為null時,要返回的常量值或?qū)ο?/p>

        Optional.ofNullable(userList).orElse(new ArrayList<>());

        orElseGet:Optional的值為null時,要執(zhí)行一個方法并返回一個值

        Optional.ofNullable(userList).orElseGet(()->new ArrayList<User>());

        orElseThrow:Optional的值為null時,要拋出的異常

        Optional.of(userList).orElseThrow(()->new Exception());

        or:Optional的值為空時,我們既不拋異常也不返回一個默認(rèn)值下面的操作還要返回一個Optional對象時用or

        ifPresent:如果Optional的值存在,下面的操作不需要返回一個流而要對值做一些處理時調(diào)用,傳入一個方法

        Optional.<List<User>>of(userList).ifPresent(users -> users.stream().filter(user -> user.getAge() > 20).count());

        ifPresentOrElse:對于下一步的操作不需要返回一個流而要對值做一些處理時調(diào)用,有值情況用一個方法處理,無值情況也用一個方法處理(要傳入兩個方法操作Optional中的對象)


        Java集合-Iterator

        Java集合-List

        超經(jīng)典的 25 道 MyBatis 面試題!


        以上,便是今天的分享,希望大家喜歡,覺得內(nèi)容不錯的,歡迎「分享」「」或者點擊「在看」支持,謝謝各位。

        瀏覽 77
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        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>
            人人艹人人 | 囗交姿势26图片 | 天天爽天天爽夜夜爽毛片资源 | 色戒完整版2小时38分片集网 | 综合激情五月丁香婷婷 | 性欧美孕交 | 欧美午夜AAAAAA免费视频 | 啊灬好用力灬嗯灬h视频 | 动漫做床爱大尺度电影 | 女警被跳蛋蹂躏的小说 |