Java?Stream.reduce()用法詳細解析
在學(xué)習這個函數(shù)的用法之前,我們要先知道這個函數(shù)參數(shù)的意義
基本使用
先舉一個簡單的例子:
算法題:Words
題目描述
每個句子由多個單詞組成,句子中的每個單詞的長度都可能不一樣,我們假設(shè)每個單詞的長度Ni為該單詞的重量,你需要做的就是給出整個句子的平均重量V。解答要求
時間限制:1000ms, 內(nèi)存限制:100MB
輸入
輸入只有一行,包含一個字符串S(長度不會超過100),代表整個句子,句子中只包含大小寫的英文字母,每個單詞之間有一個空格。輸出
輸出句子S的平均重量V(四舍五入保留兩位小數(shù))。Who Love Solo
輸出樣例
3.67
這道題的意思是求一句話中每個單詞的平均長度,我們求得總長度然后除以單詞數(shù)量即可,剛好能用到reduce()這個方法。
public class Demo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String[] s = sc.nextLine().split(" ");
double res = Arrays.stream(s).mapToDouble(a ->a.length()).reduce(0,(a,b)->a+b);
System.out.println(String.format("%.2f",res/s.length));
}
}
在代碼中,.reduce(0,(a,b)->a+b);這一塊就是我們經(jīng)典的使用案例,我們要先明白其中a,b的含義,然后再學(xué)習如何使用
關(guān)鍵概念:初始值的定義(Identity),累加器(Accumulator),組合器(Combiner)
- Identity : 定義一個元素代表是歸并操作的初始值,如果Stream 是空的,也是Stream 的默認結(jié)果
- Accumulator: 定義一個帶兩個參數(shù)的函數(shù),第一個參數(shù)是上個歸并函數(shù)的返回值,第二個是Strem 中下一個元素。
- Combiner: 調(diào)用一個函數(shù)來組合歸并操作的結(jié)果,當歸并是并行執(zhí)行或者當累加器的函數(shù)和累加器的實現(xiàn)類型不匹配時才會調(diào)用此函數(shù)。
也就是說0就是我們的初始值,(a,b)->a+b就是我們的累加器,其中a就是上一次的計算結(jié)果,b就是Stream流中當前元素,而后面的a+b則是計算規(guī)則,比如如果我們改成a*b,那就是計算乘積了,當然我們也可以用方法引用來代替 lambda 表達式。
double res = Arrays.stream(s).mapToDouble(a ->a.length()).reduce(0,Double::sum);
這就是最基本的使用了,不知道小伙伴們有沒有學(xué)會呢?
額外舉例
當然,我們可以用reduce 方法處理其他類型的 stream,例如,可以操作一個 String 類型的數(shù)組,把數(shù)組的字符串進行拼接。
List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
String result = letters
.stream()
.reduce("", (partialString, element) -> partialString + element);
assertThat(result).isEqualTo("abcde");
同樣也可以用方法引用來簡化代碼
String result = letters.stream().reduce("", String::concat);
assertThat(result).isEqualTo("abcde");我們再把上面的拼接字符串的例子改下需求,先把字符串轉(zhuǎn)變成大寫然后再拼接
String result = letters
.stream()
.reduce(
"", (partialString, element) -> partialString.toUpperCase() + element.toUpperCase());
assertThat(result).isEqualTo("ABCDE");另外,我們可以并行地歸并元素(并行歸并,下面會詳細講解),如下并行歸并一個數(shù)字數(shù)組來求和
List<Integer> ages = Arrays.asList(25, 30, 45, 28, 32); int computedAges = ages.parallelStream().reduce(0, a, b -> a + b, Integer::sum);
當對一個流進行并行操作時,在運行時會把流分割多個子流來并行操作。在上面例子中,我們需要一個函數(shù)來組合各個子流返回的結(jié)果,這個函數(shù)就是前面提到的Combiner(組合器)。
有一個注意點,下面的代碼無法通過編譯
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges =
users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());上代碼無法編譯的原因是,流中包含的是User 對象,但是累加函數(shù)的參數(shù)分別是數(shù)字和user 對象,而累加器的實現(xiàn)是求和,所以編譯器無法推斷參數(shù) user 的類型。可以把代碼改為如下可以通過編譯
int result = users.stream() .reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(), Integer::sum); assertThat(result).isEqualTo(65);
當順序讀流或者累加器的參數(shù)和它的實現(xiàn)的類型匹配時,我們不需要使用組合器。
并行讀流
如上文提到的,我們可以并行的使用 reduce() 方法。并行使用時,要注意一下幾點:
- 結(jié)果和處理的順序無關(guān)
- 操作不影響原有數(shù)據(jù)
- 操作沒有狀態(tài)和同樣的輸入有一樣的輸出結(jié)果
我們注意上面3點,以防出現(xiàn)不預(yù)期的結(jié)果,一般并行處理包含大量數(shù)據(jù)的流或者耗時的操作。
處理異常
在以上的例子中,reduce 方法都沒拋出異常,如果出現(xiàn)異常我們該如何優(yōu)雅的處理異常呢?看下面例子:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); int divider = 2; int result = numbers.stream().reduce(0, a / divider + b / divider);
如果 divider =0 , 會拋出 ArithmeticException,遇到這種情況,一般的處理方法使用 try/catch 捕獲異常
public static int divideListElements(List<Integer> values, int divider) {
return values.stream()
.reduce(0, (a, b) -> {
try {
return a / divider + b / divider;
} catch (ArithmeticException e) {
LOGGER.log(Level.INFO, "Arithmetic Exception: Division by Zero");
}
return 0;
});
}如果直接使用 try/catch 會影響代碼的可讀性,我們可以把 divide 的操作封裝一個單獨的方法,并在里面捕獲異常,如下:
rivate static int divide(int value, int factor) {
int result = 0;
try {
result = value / factor;
} catch (ArithmeticException e) {
LOGGER.log(Level.INFO, "Arithmetic Exception: Division by Zero");
}
return result
}divideListElements 調(diào)用 divide 方法
public static int divideListElements(List<Integer> values, int divider) {
return values.stream().reduce(0, (a, b) -> divide(a, divider) + divide(b, divider));
}復(fù)雜對象的處理
我們可以使用 reduce 方法處理復(fù)雜的對象,reduce 需要接受和復(fù)雜對象相對應(yīng)的 identity、accumulator、combiner。
假設(shè)一個場景:計算一個網(wǎng)站用戶的評分,該評分是所有用戶所有評論的平均值。
有個類 Review 定義如下:
public class Review {
private int points;
private String review;
// constructor, getters and setters
}類 Rating 引用 Review 計算用戶的評分
public class Rating {
double points;
List<Review> reviews = new ArrayList<>();
public void add(Review review) {
reviews.add(review);
computeRating();
}
private double computeRating() {
double totalPoints =
reviews.stream().map(Review::getPoints).reduce(0, Integer::sum);
this.points = totalPoints / reviews.size();
return this.points;
}
public static Rating average(Rating r1, Rating r2) {
Rating combined = new Rating();
combined.reviews = new ArrayList<>(r1.reviews);
combined.reviews.addAll(r2.reviews);
combined.computeRating();
return combined;
}
}先組裝一些用戶和用戶的評論
User john = new User("John", 30);
john.getRating().add(new Review(5, ""));
john.getRating().add(new Review(3, "not bad"));
User julie = new User("Julie", 35);
john.getRating().add(new Review(4, "great!"));
john.getRating().add(new Review(2, "terrible experience"));
john.getRating().add(new Review(4, ""));
List<User> users = Arrays.asList(john, julie);調(diào)用 reduce 方法處理評分
Rating averageRating = users.stream()
.reduce(new Rating(),
(rating, user) -> Rating.average(rating, user.getRating()),
Rating::average);不知道大家學(xué)會了嗎?
總結(jié)
到此這篇關(guān)于Java Stream.reduce()用法的文章就介紹到這了,更多相關(guān)Stream.reduce()用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis修改int型數(shù)據(jù)無法修改成0的解決
這篇文章主要介紹了mybatis修改int型數(shù)據(jù)無法修改成0的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot整合JavaMail通過阿里云企業(yè)郵箱發(fā)送郵件的實現(xiàn)
這篇文章主要介紹了SpringBoot整合JavaMail通過阿里云企業(yè)郵箱發(fā)送郵件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2020-11-11
mybatis中@Param注解總是報取不到參數(shù)問題及解決
這篇文章主要介紹了mybatis中@Param注解總是報取不到參數(shù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
Spring三級緩存思想解決循環(huán)依賴總結(jié)分析
這篇文章主要為大家介紹了Spring三級緩存思想解決循環(huán)依賴總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
Java 集合中關(guān)于Iterator和ListIterator的用法說明
這篇文章主要介紹了Java 集合中關(guān)于Iterator和ListIterator的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

