Lambda表達(dá)式和函數(shù)式編程
點(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 = {1, 2, 3};
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ì)象)

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