Lambda表示式和函數語言程式設計

一。函數語言程式設計概念

(a,b) -> {xxx}

引數 -> 方法體 左側一個引數時()可以省略,右側就一句方法體時{}可以省略

二。JDK8引入的函式是程式設計介面類

要想學習函數語言程式設計一定要知道jdk提供的四種類型的函數語言程式設計介面

1。Function 該型別的方法接收一個T型別的引數,返回一個R型別的結果2。Consumer 該型別方法接收一個T型別的引數,無返回值3。Supplier 該型別方法不接收任何引數,返回一個T型別引數4。Predicate 該型別方法接收一個T型別的引數,返回一個Boolean型別返回值5。Optional 該型別方法既主要在lambda函數語言程式設計中處理空值情況

三。流的建立方式

1。陣列轉流(Arrays。stream())

int[] a = {1, 2, 3};Arrays。stream(a)。peek(e -> log。info(e))。collect(Collectors。toList());

2。集合型別轉流(Collection。stream())

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

3。任意型別物件轉流(Stream。Of())

新增不同型別的物件會變成一個Object型別的流

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

4。迭代器新增元素轉流(Stream。iterate())

迭代器內第一個引數為初始值,第二個引數為一個lambda表示式,因為這個迴圈是個死迴圈所以這邊limit了前10個元素

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

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

迭代器內第一個引數為一個lambda表示式,因為這個迴圈是個死迴圈所以這邊limit了前10個元素

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

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

其實集合轉流的方式底層就是用的StreamSupport生成流的方式,大家可以去原始碼看下

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

該方法有兩個引數,第一個引數是迭代器Spliterator物件,第二個引數是是否啟用並行流的true/false值

7。IntStream整形流

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

IntStream。range(0,5)。boxed()。peek(e -> log。info(e))。collect(Collectors。toList());range()左閉右開,rangeClosed()左右都是閉區間,boxed()方法將基本型別轉包裝型別

8。Stream。builder()構造器生成流

Stream。builder()這種方式需要最後呼叫build()方法,本質上和Stream。Of()相同

Stream。Builder userBuilder= Stream。builder();userBuilder。add(new User())。add(new User())。add(new User())。build()。collect(Collectors。toList());

四。常見運算子

1。中間運算子

User物件,假設User物件具有id,name,age三個屬性,下面的例子以操作User物件為例

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

filter():過濾符合條件的資料流傳給下層運算子處理

Optional user1 = userList。stream()。filter(user -> user。getAge() > 20)。findFirst();

map():遍歷資料,可在map中將原始物件轉換為其他型別物件後返回新物件資料流傳給下層運算子操作

peek():一般用於列印流操作中間狀態的元素詳情等,一般並無實際意義

findAny():返回流中的第一個元素,在序列流中和findFirst()功能一樣,在並行流中返回的是最快處理完的那個執行緒的資料,所以說在並行流操作中,對資料沒有順序上的要求,那麼findAny的效率會比findFirst要快的

Optional user2 = userList。stream()。filter(user -> user。getAge() > 20)。findAny();

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

Optional user1 = userList。stream()。filter(user -> user。getAge() > 20)。findFirst();

sort():排序,數字型別預設升序,中文和英文等按字典序排序,可以傳入自定義的比較器(第一個引數compareTo()第二個引數就是升序,第二個引數compareTo()第一個引數就是降序)

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

flatMap():引數是流,主要使用場景是處理高階巢狀的流,將高階流扁平化。例如:父子物件常見的集合屬性

第一個應用場景:一個使用者可能有多重角色,典型一對多父子型別

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

第二個應用場景:在流中產生了Optional元素,想要取出Optional中的具體型別物件來操作

此時假設角色集合返回的是 Optional>型別

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

2。終端運算子(count、min、max具有統計意義)

User物件,假設User物件具有id,name,age三個屬性,下面的例子以操作User物件為例

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

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

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

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

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

按年齡大小輸出使用者名稱稱

userList。parallelStream()。sorted(Comparator。comparing(User::getAge))。forEachOrdered(user -> log。info(“使用者名稱稱” + user。getName()));

anyMatch():集合中有任何一個滿足條件的資料就會返回true,反之返回false(存在短路的優點有結果了就會返回,不會繼續遍歷下去)

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

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

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

allMatch():集合中所有資料都滿足條件的資料才會返回true,任意一條資料不滿足條件都會返回false

集合中有任何一個滿足條件的資料就會返回true,反之返回false

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

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

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

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

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

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

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

count():統計滿足條件的使用者數

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

min():排序後取出最小的使用者資料

Optional min = userList。stream()。min(Comparator。comparing(User::getAge));

max():排序後取出最大的使用者資料

Optional max = userList。stream()。max(Comparator。comparing(User::getAge));

reduce():執行歸集操作,某種程度上和Collect作用類似,設計上reduce應該和不可變得物件一起工作,如果物件是可變的,也可以得到結果,但是不是執行緒安全的,效能要弱於Collect,但是很靈活

第一個引數是初始值(可以不設定,不設定預設流中的第一個元素為初始值),第二個引數是個函式,函式的第一個引數是累加器,第二個引數是當前值,第三個引數是在並行流時會每個分片處理的執行緒會有一個臨時的值,這個引數為合併策略。

累加器什麼意思呢?就是第一次進來累加器取初始值,然後每次迴圈用當前的值加在累加器上,累加器相當於值得總和,reduce也是迴圈處理物件的

userList。stream()。map(User::getAge)。reduce(0,(Integer acc, Integer curr)-> Integer。sum(acc,curr));userList。parallelStream()。reduce( Collections。EMPTY_LIST, (acc,curr) ->{ List newAcc = new ArrayList<>(); newAcc。addAll(acc); newAcc。addAll(curr); return newAcc; }, (left,right)->{ List merged = new ArrayList<>(); merged。addAll(left); merged。addAll(right); return merged; } );

3。常見集合物件收集器

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

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

toSet():將結果收集為一個Set集合,Set集合元素不會重複

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

toMap():將結果收集為一個Map集合,鍵值對的形式,收集為Map集合時,有3種引數型別的過載方法可選

2個引數的情況,toMap()中的第一個引數代表要收集Map結構的key,第二個引數代表要收集Map結構的value 其中Function。identity()和user -> user是等價的它代表輸入和輸出相同是Function中的一個靜態方法(大家可以看下原始碼)

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

3個引數的情況,toMap()中的第三個引數代表當Key產生了重複值,那麼在第三個引數方法中處理(用於合併的方法引數)

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

4個引數的情況,toMap()中的第四個引數代表構建Map的工廠,一般用於不想返回系統預設的HashMap結構,比如返回TreeMap等

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

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

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

4。收集器中的聚合計算,分組統計和收集器

首先我們來說下收集器中的聚合函式哈,雖然在資料庫層面提供了分組,求平均值,計算數量,最大值,最小值等功能,但不代表我們沒有在Lambda中完成上述操作的需求,因為畢竟是在記憶體中完成的聚合計算,有的時候效能會比資料庫層面要提升很多

averagingXXX():求平均值,可以轉為3中數字型別(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():求和,可以轉為3中數字型別(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():計數,計算滿足條件的物件或值的數量

Map stringLongMap = userList。stream()。collect(Collectors。groupingBy(User::getName, Collectors。counting()));

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

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

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

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

summarizingXXX():為了方便我們操作,這個方法裡統計了上面所有的值,返回一個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():以某一個值分組,預設返回一個Map,groupingBy方法中可以繼續下一步的流操作(downstream),一般在業務中和mapping連用比較多

User物件轉為UserDto物件:

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

區域倉地址資訊按省份id分組後計算市區的數量:

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

區域倉地址資訊按省份id分組後將市區組裝為一個List集合

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

partitioningBy():partitioningBy和groupingBy都是用於將資料進行分組的函式。

兩者的區別主要是引數返回值不同,partitioningBy又被稱為分割槽函式,過載的分割槽函式可以傳遞下游流操作,比如繼續分組等

看原始碼可以看出函式的引數一個Predicate介面,那麼這個介面的返回值是boolean型別的,也只能是boolean型別,然後他的返回值是Map的key是boolean型別,也就是這個函式的返回值只能將資料分為兩組也就是ture和false兩組資料。

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

joining():主要用於字串的連線,一般用3個引數的過載方法,第一個引數是以“符號”連線每個物件,第二個引數是整體返回物件的字首,第三個引數是整體返回物件的字尾

以一個拼接訪問引數為例:

Map paramMap = new HashMap();paramMap。put(”name“,”張三“);paramMap。put(”age“,”20“);paramMap。put(”email“,”123@qq。com“);paramMap。keySet()。stream()。map(key - > key + ”=“ paramMap。get(key))。sorted()。collect(Collectors。joining(”&“,”http://localhost:8080/api?“,”“))

mapping():和常用中間運算子map()功能類似,第二個引數為下游流操作函式,主要處理中間型別轉換等,可以一直用流操作串下去

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

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

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

reducing():和reduce操作類似

點選檢視原文,獲取更多福利!

https://developer。aliyun。com/article/1103285#slide-0?utm_content=g_1000365915

版權宣告:本文內容由阿里雲實名註冊使用者自發貢獻,版權歸原作者所有,阿里雲開發者社群不擁有其著作權,亦不承擔相應法律責任。具體規則請檢視《阿里雲開發者社群使用者服務協議》和《阿里雲開發者社群智慧財產權保護指引》。如果您發現本社群中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社群將立刻刪除涉嫌侵權內容。