欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你徹底了解Java8中的Lambda,函數(shù)式接口和Stream

 更新時間:2023年08月07日 14:58:45   作者:古時的風箏  
這篇文章主要為大家詳細介紹了解Java8中的Lambda,函數(shù)式接口和Stream的用法和原理,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學習一下

就在今年 Java 25周歲了,可能比在座的各位中的一些少年年齡還大,但令人遺憾的是,竟然沒有我大,不禁感嘆,Java 還是太小了。(難道我會說是因為我老了?)

而就在上個月,Java 15 的試驗版悄悄發(fā)布了,但是在 Java 界一直有個神秘現(xiàn)象,那就是「你發(fā)你發(fā)任你發(fā),我的最愛 Java 8」.

據(jù) Snyk 和 The Java Magazine 聯(lián)合推出發(fā)布的 2020 JVM 生態(tài)調(diào)查報告顯示,在所有的 Java 版本中,仍然有 64% 的開發(fā)者使用 Java 8。另外一些開發(fā)者可能已經(jīng)開始用 Java 9、Java 11、Java 13 了,當然還有一些神仙開發(fā)者還在堅持使用 JDK 1.6 和 1.7。

盡管 Java 8 發(fā)布多年,使用者眾多,可神奇的是竟然有很多同學沒有用過 Java 8 的新特性,比如 Lambda表達式、比如方法引用,再比如今天要說的 Stream。其實 Stream 就是以 Lambda 和方法引用為基礎(chǔ),封裝的簡單易用、函數(shù)式風格的 API。

Java 8 是在 2014 年發(fā)布的,實話說,風箏我也是在 Java 8 發(fā)布后很長一段時間才用的 Stream,因為 Java 8 發(fā)布的時候我還在 C# 的世界中掙扎,而使用 Lambda 表達式卻很早了,因為 Python 中用 Lambda 很方便,沒錯,我寫 Python 的時間要比 Java 的時間還長。

要講 Stream ,那就不得不先說一下它的左膀右臂 Lambda 和方法引用,你用的 Stream API 其實就是函數(shù)式的編程風格,其中的「函數(shù)」就是方法引用,「式」就是 Lambda 表達式。

Lambda 表達式

Lambda 表達式是一個匿名函數(shù),Lambda表達式基于數(shù)學中的λ演算得名,直接對應(yīng)于其中的lambda抽象,是一個匿名函數(shù),即沒有函數(shù)名的函數(shù)。Lambda表達式可以表示閉包。

在 Java 中,Lambda 表達式的格式是像下面這樣

//?無參數(shù),無返回值
()?->?log.info("Lambda")
?//?有參數(shù),有返回值
(int?a,?int?b)?->?{?a+b?}

其等價于

log.info("Lambda");
private?int?plus(int?a,?int?b){
???return?a+b;
}

最常見的一個例子就是新建線程,有時候為了省事,會用下面的方法創(chuàng)建并啟動一個線程,這是匿名內(nèi)部類的寫法,new Thread需要一個 implements 自Runnable類型的對象實例作為參數(shù),比較好的方式是創(chuàng)建一個新類,這個類 implements Runnable,然后 new 出這個新類的實例作為參數(shù)傳給 Thread。而匿名內(nèi)部類不用找對象接收,直接當做參數(shù)。

new?Thread(new?Runnable()?{
????@Override
????public?void?run()?{
????????System.out.println("快速新建并啟動一個線程");
????}
}).run();

但是這樣寫是不是感覺看上去很亂、很土,而這時候,換上 Lambda 表達式就是另外一種感覺了。

new?Thread(()->{
????System.out.println("快速新建并啟動一個線程");
}).run();

怎么樣,這樣一改,瞬間感覺清新脫俗了不少,簡潔優(yōu)雅了不少。

Lambda 表達式簡化了匿名內(nèi)部類的形式,可以達到同樣的效果,但是 Lambda 要優(yōu)雅的多。雖然最終達到的目的是一樣的,但其實內(nèi)部的實現(xiàn)原理卻不相同。

匿名內(nèi)部類在編譯之后會創(chuàng)建一個新的匿名內(nèi)部類出來,而 Lambda 是調(diào)用 JVM invokedynamic指令實現(xiàn)的,并不會產(chǎn)生新類。

方法引用

方法引用的出現(xiàn),使得我們可以將一個方法賦給一個變量或者作為參數(shù)傳遞給另外一個方法。::雙冒號作為方法引用的符號,比如下面這兩行語句,引用 Integer類的 parseInt方法。

Function<String,?Integer>?s?=?Integer::parseInt;
Integer?i?=?s.apply("10");

或者下面這兩行,引用 Integer類的 compare方法。

Comparator<Integer>?comparator?=?Integer::compare;
int?result?=?comparator.compare(100,10);

再比如,下面這兩行代碼,同樣是引用 Integer類的 compare方法,但是返回類型卻不一樣,但卻都能正常執(zhí)行,并正確返回。

IntBinaryOperator?intBinaryOperator?=?Integer::compare;
int?result?=?intBinaryOperator.applyAsInt(10,100);

相信有的同學看到這里恐怕是下面這個狀態(tài),完全不可理喻嗎,也太隨便了吧,返回給誰都能接盤。

先別激動,來來來,現(xiàn)在咱們就來解惑,解除蒙圈臉。

Q:什么樣的方法可以被引用?

A:這么說吧,任何你有辦法訪問到的方法都可以被引用。

Q:返回值到底是什么類型?

A:這就問到點兒上了,上面又是 Function、又是Comparator、又是 IntBinaryOperator的,看上去好像沒有規(guī)律,其實不然。

返回的類型是 Java 8 專門定義的函數(shù)式接口,這類接口用 @FunctionalInterface 注解。

比如 Function這個函數(shù)式接口的定義如下:

@FunctionalInterface
public?interface?Function<T,?R>?{
????R?apply(T?t);
}

還有很關(guān)鍵的一點,你的引用方法的參數(shù)個數(shù)、類型,返回值類型要和函數(shù)式接口中的方法聲明一一對應(yīng)才行。

比如 Integer.parseInt方法定義如下:

public?static?int?parseInt(String?s)?throws?NumberFormatException?{
????return?parseInt(s,10);
}

首先parseInt方法的參數(shù)個數(shù)是 1 個,而 Function中的 apply方法參數(shù)個數(shù)也是 1 個,參數(shù)個數(shù)對應(yīng)上了,再來,apply方法的參數(shù)類型和返回類型是泛型類型,所以肯定能和 parseInt方法對應(yīng)上。

這樣一來,就可以正確的接收Integer::parseInt的方法引用,并可以調(diào)用Funcitonapply方法,這時候,調(diào)用到的其實就是對應(yīng)的 Integer.parseInt方法了。

用這套標準套到 Integer::compare方法上,就不難理解為什么即可以用 Comparator<Integer>接收,又可以用 IntBinaryOperator接收了,而且調(diào)用它們各自的方法都能正確的返回結(jié)果。

Integer.compare方法定義如下:

public?static?int?compare(int?x,?int?y)?{
????return?(x?<?y)???-1?:?((x?==?y)???0?:?1);
}

返回值類型 int,兩個參數(shù),并且參數(shù)類型都是 int。

然后來看ComparatorIntBinaryOperator它們兩個的函數(shù)式接口定義和其中對應(yīng)的方法:

@FunctionalInterface
public?interface?Comparator<T>?{
????int?compare(T?o1,?T?o2);
}
@FunctionalInterface
public?interface?IntBinaryOperator?{
????int?applyAsInt(int?left,?int?right);
}

對不對,都能正確的匹配上,所以前面示例中用這兩個函數(shù)式接口都能正常接收。其實不止這兩個,只要是在某個函數(shù)式接口中聲明了這樣的方法:兩個參數(shù),參數(shù)類型是 int或者泛型,并且返回值是 int或者泛型的,都可以完美接收。

JDK 中定義了很多函數(shù)式接口,主要在 java.util.function包下,還有 java.util.Comparator 專門用作定制比較器。另外,前面說的 Runnable也是一個函數(shù)式接口。

自己動手實現(xiàn)一個例子

1. 定義一個函數(shù)式接口,并添加一個方法

定義了名稱為 KiteFunction 的函數(shù)式接口,使用 @FunctionalInterface注解,然后聲明了具有兩個參數(shù)的方法 run,都是泛型類型,返回結(jié)果也是泛型。

還有一點很重要,函數(shù)式接口中只能聲明一個可被實現(xiàn)的方法,你不能聲明了一個 run方法,又聲明一個 start方法,到時候編譯器就不知道用哪個接收了。而用default 關(guān)鍵字修飾的方法則沒有影響。

@FunctionalInterface
public?interface?KiteFunction<T,?R,?S>?{
????/**
?????*?定義一個雙參數(shù)的方法
?????*?@param?t
?????*?@param?s
?????*?@return
?????*/
????R?run(T?t,S?s);
}

2. 定義一個與 KiteFunction 中 run 方法對應(yīng)的方法

在 FunctionTest 類中定義了方法 DateFormat,一個將 LocalDateTime類型格式化為字符串類型的方法。

public?class?FunctionTest?{
????public?static?String?DateFormat(LocalDateTime?dateTime,?String?partten)?{
????????DateTimeFormatter?dateTimeFormatter?=?DateTimeFormatter.ofPattern(partten);
????????return?dateTime.format(dateTimeFormatter);
????}
}

3.用方法引用的方式調(diào)用

正常情況下我們直接使用 FunctionTest.DateFormat()就可以了。

而用函數(shù)式方式,是這樣的。

KiteFunction<LocalDateTime,String,String>?functionDateFormat?=?FunctionTest::DateFormat;
String?dateString?=?functionDateFormat.run(LocalDateTime.now(),"yyyy-MM-dd?HH:mm:ss");

而其實我可以不專門在外面定義 DateFormat這個方法,而是像下面這樣,使用匿名內(nèi)部類。

public?static?void?main(String[]?args)?throws?Exception?{
????String?dateString?=?new?KiteFunction<LocalDateTime,?String,?String>()?{
????????@Override
????????public?String?run(LocalDateTime?localDateTime,?String?s)?{
????????????DateTimeFormatter?dateTimeFormatter?=?DateTimeFormatter.ofPattern(s);
????????????return?localDateTime.format(dateTimeFormatter);
????????}
????}.run(LocalDateTime.now(),?"yyyy-MM-dd?HH:mm:ss");
????System.out.println(dateString);
}

前面第一個 Runnable的例子也提到了,這樣的匿名內(nèi)部類可以用 Lambda 表達式的形式簡寫,簡寫后的代碼如下:

public?static?void?main(String[]?args)?throws?Exception?{
????????KiteFunction<LocalDateTime,?String,?String>?functionDateFormat?=?(LocalDateTime?dateTime,?String?partten)?->?{
????????????DateTimeFormatter?dateTimeFormatter?=?DateTimeFormatter.ofPattern(partten);
????????????return?dateTime.format(dateTimeFormatter);
????????};
????????String?dateString?=?functionDateFormat.run(LocalDateTime.now(),?"yyyy-MM-dd?HH:mm:ss");
????????System.out.println(dateString);
}

使用(LocalDateTime dateTime, String partten) -> { } 這樣的 Lambda 表達式直接返回方法引用。

Stream API

為了說一下 Stream API 的使用,可以說是大費周章啊,知其然,也要知其所以然嗎,追求技術(shù)的態(tài)度和姿勢要正確。

當然 Stream 也不只是 Lambda 表達式就厲害了,真正厲害的還是它的功能,Stream 是 Java 8 中集合數(shù)據(jù)處理的利器,很多本來復雜、需要寫很多代碼的方法,比如過濾、分組等操作,往往使用 Stream 就可以在一行代碼搞定,當然也因為 Stream 都是鏈式操作,一行代碼可能會調(diào)用好幾個方法。

Collection接口提供了 stream()方法,讓我們可以在一個集合方便的使用 Stream API 來進行各種操作。值得注意的是,我們執(zhí)行的任何操作都不會對源集合造成影響,你可以同時在一個集合上提取出多個 stream 進行操作。

我們看 Stream 接口的定義,繼承自 BaseStream,幾乎所有的接口聲明都是接收方法引用類型的參數(shù),比如 filter方法,接收了一個 Predicate類型的參數(shù),它就是一個函數(shù)式接口,常用來作為條件比較、篩選、過濾用,JPA中也使用了這個函數(shù)式接口用來做查詢條件拼接。

public?interface?Stream<T>?extends?BaseStream<T,?Stream<T>>?{
??Stream<T>?filter(Predicate<??super?T>?predicate);
??//?其他接口
}??

下面就來看看 Stream 常用 API。

of

可接收一個泛型對象或可變成泛型集合,構(gòu)造一個 Stream 對象。

private?static?void?createStream(){
????Stream<String>?stringStream?=?Stream.of("a","b","c");
}

empty

創(chuàng)建一個空的  Stream 對象。

concat

連接兩個 Stream ,不改變其中任何一個 Steam 對象,返回一個新的 Stream 對象。

private?static?void?concatStream(){
????Stream<String>?a?=?Stream.of("a","b","c");
????Stream<String>?b?=?Stream.of("d","e");
????Stream<String>?c?=?Stream.concat(a,b);
}

max

一般用于求數(shù)字集合中的最大值,或者按實體中數(shù)字類型的屬性比較,擁有最大值的那個實體。它接收一個 Comparator<T>,上面也舉到這個例子了,它是一個函數(shù)式接口類型,專門用作定義兩個對象之間的比較,例如下面這個方法使用了 Integer::compareTo這個方法引用。

private?static?void?max(){
????Stream<Integer>?integerStream?=?Stream.of(2,?2,?100,?5);
????Integer?max?=?integerStream.max(Integer::compareTo).get();
????System.out.println(max);
}

當然,我們也可以自己定制一個 Comparator,順便復習一下 Lambda 表達式形式的方法引用。

private?static?void?max(){
????Stream<Integer>?integerStream?=?Stream.of(2,?2,?100,?5);
????Comparator<Integer>?comparator?=??(x,?y)?->?(x.intValue()?<?y.intValue())???-1?:?((x.equals(y))???0?:?1);
????Integer?max?=?integerStream.max(comparator).get();
????System.out.println(max);
}

min

與 max 用法一樣,只不過是求最小值。

findFirst

獲取 Stream 中的第一個元素。

findAny

獲取 Stream 中的某個元素,如果是串行情況下,一般都會返回第一個元素,并行情況下就不一定了。

count

返回元素個數(shù)。

Stream<String>?a?=?Stream.of("a",?"b",?"c");
long?x?=?a.count();

peek

建立一個通道,在這個通道中對 Stream 的每個元素執(zhí)行對應(yīng)的操作,對應(yīng) Consumer<T>的函數(shù)式接口,這是一個消費者函數(shù)式接口,顧名思義,它是用來消費 Stream 元素的,比如下面這個方法,把每個元素轉(zhuǎn)換成對應(yīng)的大寫字母并輸出。

private?static?void?peek()?{
????Stream<String>?a?=?Stream.of("a",?"b",?"c");
????List<String>?list?=?a.peek(e->System.out.println(e.toUpperCase())).collect(Collectors.toList());
}

forEach

和 peek 方法類似,都接收一個消費者函數(shù)式接口,可以對每個元素進行對應(yīng)的操作,但是和 peek 不同的是,forEach 執(zhí)行之后,這個 Stream 就真的被消費掉了,之后這個 Stream 流就沒有了,不可以再對它進行后續(xù)操作了,而 peek操作完之后,還是一個可操作的 Stream 對象。

正好借著這個說一下,我們在使用 Stream API 的時候,都是一串鏈式操作,這是因為很多方法,比如接下來要說到的 filter方法等,返回值還是這個 Stream 類型的,也就是被當前方法處理過的 Stream 對象,所以 Stream API 仍然可以使用。

private?static?void?forEach()?{
????Stream<String>?a?=?Stream.of("a",?"b",?"c");
????a.forEach(e->System.out.println(e.toUpperCase()));
}

forEachOrdered

功能與 forEach是一樣的,不同的是,forEachOrdered是有順序保證的,也就是對 Stream 中元素按插入時的順序進行消費。為什么這么說呢,當開啟并行的時候,forEach和 forEachOrdered的效果就不一樣了。

Stream<String>?a?=?Stream.of("a",?"b",?"c");
a.parallel().forEach(e->System.out.println(e.toUpperCase()));

當使用上面的代碼時,輸出的結(jié)果可能是 B、A、C 或者 A、C、B或者A、B、C,而使用下面的代碼,則每次都是 A、 B、C

Stream<String>?a?=?Stream.of("a",?"b",?"c");
a.parallel().forEachOrdered(e->System.out.println(e.toUpperCase()));

limit

獲取前 n 條數(shù)據(jù),類似于 MySQL 的limit,只不過只能接收一個參數(shù),就是數(shù)據(jù)條數(shù)。

private?static?void?limit()?{
????Stream<String>?a?=?Stream.of("a",?"b",?"c");
????a.limit(2).forEach(e->System.out.println(e));
}

上述代碼打印的結(jié)果是 a、b。

skip

跳過前 n 條數(shù)據(jù),例如下面代碼,返回結(jié)果是 c。

private?static?void?skip()?{
????Stream<String>?a?=?Stream.of("a",?"b",?"c");
????a.skip(2).forEach(e->System.out.println(e));
}

distinct

元素去重,例如下面方法返回元素是 a、b、c,將重復的 b 只保留了一個。

private?static?void?distinct()?{
????Stream<String>?a?=?Stream.of("a",?"b",?"c","b");
????a.distinct().forEach(e->System.out.println(e));
}

sorted

有兩個重載,一個無參數(shù),另外一個有個 Comparator類型的參數(shù)。

無參類型的按照自然順序進行排序,只適合比較單純的元素,比如數(shù)字、字母等。

private?static?void?sorted()?{
????Stream<String>?a?=?Stream.of("a",?"c",?"b");
????a.sorted().forEach(e->System.out.println(e));
}

有參數(shù)的需要自定義排序規(guī)則,例如下面這個方法,按照第二個字母的大小順序排序,最后輸出的結(jié)果是 a1、b3、c6。

private?static?void?sortedWithComparator()?{
????Stream<String>?a?=?Stream.of("a1",?"c6",?"b3");
????a.sorted((x,y)->Integer.parseInt(x.substring(1))>Integer.parseInt(y.substring(1))?1:-1).forEach(e->System.out.println(e));
}

為了更好的說明接下來的幾個 API ,我模擬了幾條項目中經(jīng)常用到的類似數(shù)據(jù),10條用戶信息。

private?static?List<User>?getUserData()?{
????Random?random?=?new?Random();
????List<User>?users?=?new?ArrayList<>();
????for?(int?i?=?1;?i?<=?10;?i++)?{
????????User?user?=?new?User();
????????user.setUserId(i);
????????user.setUserName(String.format("古時的風箏?%s?號",?i));
????????user.setAge(random.nextInt(100));
????????user.setGender(i?%?2);
????????user.setPhone("18812021111");
????????user.setAddress("無");
????????users.add(user);
????}
????return?users;
}

filter

用于條件篩選過濾,篩選出符合條件的數(shù)據(jù)。例如下面這個方法,篩選出性別為 0,年齡大于 50 的記錄。

private?static?void?filter(){
????List<User>?users?=?getUserData();
????Stream<User>?stream?=?users.stream();
????stream.filter(user?->?user.getGender().equals(0)?&&?user.getAge()>50).forEach(e->System.out.println(e));
????/**
?????*等同于下面這種形式?匿名內(nèi)部類
?????*/
//????stream.filter(new?Predicate<User>()?{
//????????@Override
//????????public?boolean?test(User?user)?{
//????????????return?user.getGender().equals(0)?&&?user.getAge()>50;
//????????}
//????}).forEach(e->System.out.println(e));
}

map

map方法的接口方法聲明如下,接受一個 Function函數(shù)式接口,把它翻譯成映射最合適了,通過原始數(shù)據(jù)元素,映射出新的類型。

<R>?Stream<R>?map(Function<??super?T,???extends?R>?mapper);

而 Function的聲明是這樣的,觀察 apply方法,接受一個 T 型參數(shù),返回一個 R 型參數(shù)。用于將一個類型轉(zhuǎn)換成另外一個類型正合適,這也是 map的初衷所在,用于改變當前元素的類型,例如將 Integer 轉(zhuǎn)為 String類型,將 DAO 實體類型,轉(zhuǎn)換為 DTO 實例類型。

當然了,T 和 R 的類型也可以一樣,這樣的話,就和 peek方法沒什么不同了。

@FunctionalInterface
public?interface?Function<T,?R>?{
????/**
?????*?Applies?this?function?to?the?given?argument.
?????*
?????*?@param?t?the?function?argument
?????*?@return?the?function?result
?????*/
????R?apply(T?t);
}

例如下面這個方法,應(yīng)該是業(yè)務(wù)系統(tǒng)的常用需求,將 User 轉(zhuǎn)換為 API 輸出的數(shù)據(jù)格式。

private?static?void?map(){
????List<User>?users?=?getUserData();
????Stream<User>?stream?=?users.stream();
????List<UserDto>?userDtos?=?stream.map(user?->?dao2Dto(user)).collect(Collectors.toList());
}
private?static?UserDto?dao2Dto(User?user){
????UserDto?dto?=?new?UserDto();
????BeanUtils.copyProperties(user,?dto);
????//其他額外處理
????return?dto;
}

mapToInt

將元素轉(zhuǎn)換成 int 類型,在 map方法的基礎(chǔ)上進行封裝。

mapToLong

將元素轉(zhuǎn)換成 Long 類型,在 map方法的基礎(chǔ)上進行封裝。

mapToDouble

將元素轉(zhuǎn)換成 Double 類型,在 map方法的基礎(chǔ)上進行封裝。

flatMap

這是用在一些比較特別的場景下,當你的 Stream 是以下這幾種結(jié)構(gòu)的時候,需要用到 flatMap方法,用于將原有二維結(jié)構(gòu)扁平化。

  • Stream<String[]>
  • Stream<Set<String>>
  • Stream<List<String>>

以上這三類結(jié)構(gòu),通過 flatMap方法,可以將結(jié)果轉(zhuǎn)化為 Stream<String>這種形式,方便之后的其他操作。

比如下面這個方法,將List<List<User>>扁平處理,然后再使用 map或其他方法進行操作。

private?static?void?flatMap(){
????List<User>?users?=?getUserData();
????List<User>?users1?=?getUserData();
????List<List<User>>?userList?=?new?ArrayList<>();
????userList.add(users);
????userList.add(users1);
????Stream<List<User>>?stream?=?userList.stream();
????List<UserDto>?userDtos?=?stream.flatMap(subUserList->subUserList.stream()).map(user?->?dao2Dto(user)).collect(Collectors.toList());
}

flatMapToInt

用法參考 flatMap,將元素扁平為 int 類型,在 flatMap方法的基礎(chǔ)上進行封裝。

flatMapToLong

用法參考 flatMap,將元素扁平為 Long 類型,在 flatMap方法的基礎(chǔ)上進行封裝。

flatMapToDouble

用法參考 flatMap,將元素扁平為 Double 類型,在 flatMap方法的基礎(chǔ)上進行封裝。

collection

在進行了一系列操作之后,我們最終的結(jié)果大多數(shù)時候并不是為了獲取 Stream 類型的數(shù)據(jù),而是要把結(jié)果變?yōu)?List、Map 這樣的常用數(shù)據(jù)結(jié)構(gòu),而 collection就是為了實現(xiàn)這個目的。

就拿 map 方法的那個例子說明,將對象類型進行轉(zhuǎn)換后,最終我們需要的結(jié)果集是一個 List<UserDto >類型的,使用 collect方法將 Stream 轉(zhuǎn)換為我們需要的類型。

下面是 collect接口方法的定義:

<R,?A>?R?collect(Collector<??super?T,?A,?R>?collector);

下面這個例子演示了將一個簡單的 Integer Stream 過濾出大于 7 的值,然后轉(zhuǎn)換成 List<Integer>集合,用的是 Collectors.toList()這個收集器。

private?static?void?collect(){
????Stream<Integer>?integerStream?=?Stream.of(1,2,5,7,8,12,33);
????List<Integer>?list?=?integerStream.filter(s?->?s.intValue()>7).collect(Collectors.toList());
}

很多同學表示看不太懂這個 Collector是怎么一個意思,來,我們看下面這段代碼,這是 collect的另一個重載方法,你可以理解為它的參數(shù)是按順序執(zhí)行的,這樣就清楚了,這就是個 ArrayList 從創(chuàng)建到調(diào)用 addAll方法的一個過程。

private?static?void?collect(){
????Stream<Integer>?integerStream?=?Stream.of(1,2,5,7,8,12,33);
????List<Integer>?list?=?integerStream.filter(s?->?s.intValue()>7).collect(ArrayList::new,?ArrayList::add,
????????????ArrayList::addAll);
}

我們在自定義 Collector的時候其實也是這個邏輯,不過我們根本不用自定義, Collectors已經(jīng)為我們提供了很多拿來即用的收集器。比如我們經(jīng)常用到Collectors.toList()、Collectors.toSet()、Collectors.toMap()。另外還有比如Collectors.groupingBy()用來分組,比如下面這個例子,按照 userId 字段分組,返回以 userId 為key,List為value 的 Map,或者返回每個 key 的個數(shù)。

//?返回?userId:List<User>
Map<String,List<User>>?map?=?user.stream().collect(Collectors.groupingBy(User::getUserId));
//?返回?userId:每組個數(shù)
Map<String,Long>?map?=?user.stream().collect(Collectors.groupingBy(User::getUserId,Collectors.counting()));

toArray

collection是返回列表、map 等,toArray是返回數(shù)組,有兩個重載,一個空參數(shù),返回的是 Object[]。

另一個接收一個 IntFunction<R>類型參數(shù)。

@FunctionalInterface
public?interface?IntFunction<R>?{
????/**
?????*?Applies?this?function?to?the?given?argument.
?????*
?????*?@param?value?the?function?argument
?????*?@return?the?function?result
?????*/
????R?apply(int?value);
}

比如像下面這樣使用,參數(shù)是 User[]::new也就是new 一個 User 數(shù)組,長度為最后的 Stream 長度。

private?static?void?toArray()?{
????List<User>?users?=?getUserData();
????Stream<User>?stream?=?users.stream();
????User[]?userArray?=?stream.filter(user?->?user.getGender().equals(0)?&&?user.getAge()?>?50).toArray(User[]::new);
}

reduce

它的作用是每次計算的時候都用到上一次的計算結(jié)果,比如求和操作,前兩個數(shù)的和加上第三個數(shù)的和,再加上第四個數(shù),一直加到最后一個數(shù)位置,最后返回結(jié)果,就是 reduce的工作過程。

private?static?void?reduce(){
????Stream<Integer>?integerStream?=?Stream.of(1,2,5,7,8,12,33);
????Integer?sum?=?integerStream.reduce(0,(x,y)->x+y);
????System.out.println(sum);
}

另外 Collectors好多方法都用到了 reduce,比如 groupingBy、minBymaxBy等等。

并行 Stream

Stream 本質(zhì)上來說就是用來做數(shù)據(jù)處理的,為了加快處理速度,Stream API 提供了并行處理 Stream 的方式。通過 users.parallelStream()或者users.stream().parallel() 的方式來創(chuàng)建并行 Stream 對象,支持的 API 和普通 Stream 幾乎是一致的。

并行 Stream 默認使用 ForkJoinPool線程池,當然也支持自定義,不過一般情況下沒有必要。ForkJoin 框架的分治策略與并行流處理正好契合。

雖然并行這個詞聽上去很厲害,但并不是所有情況使用并行流都是正確的,很多時候完全沒這個必要。

什么情況下使用或不應(yīng)使用并行流操作呢?

  • 必須在多核 CPU 下才使用并行 Stream,聽上去好像是廢話。
  • 在數(shù)據(jù)量不大的情況下使用普通串行 Stream 就可以了,使用并行 Stream 對性能影響不大。
  • CPU 密集型計算適合使用并行 Stream,而 IO 密集型使用并行 Stream 反而會更慢。
  • 雖然計算是并行的可能很快,但最后大多數(shù)時候還是要使用 collect合并的,如果合并代價很大,也不適合用并行 Stream。
  • 有些操作,比如 limit、 findFirst、forEachOrdered 等依賴于元素順序的操作,都不適合用并行 Stream。

以上就是一文帶你徹底了解Java8中的Lambda,函數(shù)式接口和Stream的詳細內(nèi)容,更多關(guān)于Java8 Lambda 函數(shù)式接口 Stream的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java多線程死鎖問題詳解(wait和notify)

    Java多線程死鎖問題詳解(wait和notify)

    線程之間形成相互等待資源的環(huán)時,就會形成順序死鎖,下面這篇文章主要給大家介紹了關(guān)于Java多線程死鎖問題(wait和notify)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-01-01
  • Spring實現(xiàn)類私有方法的幾個問題(親測通用解決方案)

    Spring實現(xiàn)類私有方法的幾個問題(親測通用解決方案)

    現(xiàn)實的業(yè)務(wù)場景中,可能需要對Spring的實現(xiàn)類的私有方法進行測試。本文給大家分享Spring實現(xiàn)類私有方法面臨的幾個問題及解決方案,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • ApiOperation和ApiParam注解依賴的安裝和使用以及注意事項說明

    ApiOperation和ApiParam注解依賴的安裝和使用以及注意事項說明

    這篇文章主要介紹了ApiOperation和ApiParam注解依賴的安裝和使用以及注意事項說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 深入理解Netty核心類及其作用

    深入理解Netty核心類及其作用

    本文介紹了Netty框架中的核心類及其作用,包括Channel、EventLoop、ChannelPipeline、ByteBuf等,通過對這些類的深入理解,可以更好地使用Netty進行網(wǎng)絡(luò)編程開發(fā)
    2023-04-04
  • java實現(xiàn)簡單的汽車租賃系統(tǒng)

    java實現(xiàn)簡單的汽車租賃系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現(xiàn)簡單的汽車租賃系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Java線程中的notifyAll喚醒操作(推薦)

    Java線程中的notifyAll喚醒操作(推薦)

    這篇文章主要介紹了Java線程中的notifyAll喚醒操作,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-03-03
  • 如何通過idea實現(xiàn)springboot集成mybatis

    如何通過idea實現(xiàn)springboot集成mybatis

    這篇文章主要介紹了如何通過idea實現(xiàn)springboot集成mybatis,使用springboot 集成 mybatis后,通過http請求接口,使得通過http請求可以直接操作數(shù)據(jù)庫,本文結(jié)合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-09-09
  • JAVA 區(qū)分集合和數(shù)組

    JAVA 區(qū)分集合和數(shù)組

    這篇文章主要介紹了JAVA如何區(qū)分集合和數(shù)組,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-06-06
  • Java中的Lambda表達式詳解

    Java中的Lambda表達式詳解

    這篇文章主要介紹了Java中的Lambda表達式詳解,Lambda 表達式是 JDK8 的一個新特性,可以取代大部分的匿名內(nèi)部類,寫出更優(yōu)雅的 Java 代碼,尤其在集合的遍歷和其他集合操作中,可以極大地優(yōu)化代碼結(jié)構(gòu),需要的朋友可以參考下
    2024-01-01
  • Java如何優(yōu)雅關(guān)閉異步中的ExecutorService

    Java如何優(yōu)雅關(guān)閉異步中的ExecutorService

    在并發(fā)編程領(lǐng)域,Java的ExecutorService是線程池管理的關(guān)鍵接口,這篇文章主要為大家介紹了如何優(yōu)雅關(guān)閉異步中的ExecutorService,需要的可以了解下
    2025-02-02

最新評論