<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>

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

          共 17622字,需瀏覽 36分鐘

           ·

          2021-03-23 21:19

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


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

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

          (a,b) -> {xxx} 

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

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

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

          1.Function<T, R>  該類型的方法接收一個(gè)T類型的參數(shù),返回一個(gè)R類型的結(jié)果
          2.Consumer<T>  該類型方法接收一個(gè)T類型的參數(shù),無返回值
          3.Supplier<T>  該類型方法不接收任何參數(shù),返回一個(gè)T類型參數(shù)
          4.Predicate<T>  該類型方法接收一個(gè)T類型的參數(shù),返回一個(gè)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.任意類型對(duì)象轉(zhuǎn)流(Stream.Of())

          添加不同類型的對(duì)象會(huì)變成一個(gè)Object類型的流

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

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

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

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

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

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

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

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

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

          Stream.generate()生成流的方式不常用,因?yàn)橐话阄覀儾僮骷蠑?shù)組時(shí)直接用JDK封裝好的轉(zhuǎn)流方式就可以了,這邊不做演示。

          該方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是迭代器Spliterator對(duì)象,第二個(gè)參數(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對(duì)象,假設(shè)User對(duì)象具有id,name,age三個(gè)屬性,下面的例子以操作User對(duì)象為例

          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中將原始對(duì)象轉(zhuǎn)換為其他類型對(duì)象后返回新對(duì)象數(shù)據(jù)流傳給下層操作符操作

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

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

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

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

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

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

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

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

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

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

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

          此時(shí)假設(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)計(jì)意義)

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

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

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

          將所有用戶的年齡都改為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():集合中有任何一個(gè)滿足條件的數(shù)據(jù)就會(huì)返回true,反之返回false(存在短路的優(yōu)點(diǎn)有結(jié)果了就會(huì)返回,不會(huì)繼續(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ù)才會(huì)返回true,任意一條數(shù)據(jù)不滿足條件都會(huì)返回false

          集合中有任何一個(gè)滿足條件的數(shù)據(jù)就會(huì)返回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():集合中沒有任何一個(gè)匹配條件的元素時(shí)返回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)計(jì)滿足條件的用戶數(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è)計(jì)上reduce應(yīng)該和不可變得對(duì)象一起工作,如果對(duì)象是可變的,也可以得到結(jié)果,但是不是線程安全的,性能要弱于Collect,但是很靈活

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

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

          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.常見集合對(duì)象收集器

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

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

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

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

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

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

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

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

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

          4個(gè)參數(shù)的情況,toMap()中的第四個(gè)參數(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é)果收集為一個(gè)自定義的集合類型

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

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

          首先我們來說下收集器中的聚合函數(shù)哈,雖然在數(shù)據(jù)庫層面提供了分組,求平均值,計(jì)算數(shù)量,最大值,最小值等功能,但不代表我們沒有在Lambda中完成上述操作的需求,因?yàn)楫吘故窃趦?nèi)存中完成的聚合計(jì)算,有的時(shí)候性能會(huì)比數(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():計(jì)數(shù),計(jì)算滿足條件的對(duì)象或值的數(shù)量

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

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

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

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

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

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

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

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

          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分組后計(jì)算市區(qū)的數(shù)量:

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

          區(qū)域倉地址信息按省份id分組后將市區(qū)組裝為一個(gè)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ù)一個(gè)Predicate接口,那么這個(gè)接口的返回值是boolean類型的,也只能是boolean類型,然后他的返回值是Map的key是boolean類型,也就是這個(gè)函數(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個(gè)參數(shù)的重載方法,第一個(gè)參數(shù)是以"符號(hào)”連接每個(gè)對(duì)象,第二個(gè)參數(shù)是整體返回對(duì)象的前綴,第三個(gè)參數(shù)是整體返回對(duì)象的后綴

          以一個(gè)拼接訪問參數(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()功能類似,第二個(gè)參數(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():一般用于先收集一個(gè)集合后,再對(duì)收集后的集合做一些操作

          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)不會(huì)出現(xiàn)null值,來確保我們?cè)谟胠ambda流操作時(shí)不會(huì)中斷

          1.生成Optional對(duì)象的方式

          因?yàn)槠錁?gòu)造方法是私有的,所以只能通過靜態(tài)的構(gòu)造器來創(chuàng)建Optional對(duì)象

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

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

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

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

          Optional.<>empty():生成一個(gè)空Optional對(duì)象,源碼注釋上寫著不能保證這個(gè)對(duì)象是單例的,具體原因還沒搞清,各位有懂得可以評(píng)論區(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時(shí),要返回的常量值或?qū)ο?/p>

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

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

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

          orElseThrow:Optional的值為null時(shí),要拋出的異常

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

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

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

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

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


          Java集合-Iterator

          Java集合-List

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


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

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  日韩精品不卡 | 超碰大鸡巴 | 久久久久久久久久久高清毛片一级 | 中文字幕AV电影 | 青青98伊人 |