淺聊java8中數(shù)值流的使用
簡介
java8
為我提供的簡單快捷的數(shù)值流計算API
,本文就基于幾個常見的場景介紹一下數(shù)值流API
的使用。
基礎(chǔ)示例
我們以一個食物熱量計算的功能展開演示,如下所示,可以看到Dish
類它記錄了每一個食物的名稱、熱量、類型等信息:
public class Dish { /** * 名稱 */ private final String name; /** * 是否是素食 */ private final boolean vegetarian; /** * 卡路里 */ private final int calories; /** * 類型 */ private final Type type; //類型枚舉 分別是是:肉類 魚類 其他 public enum Type {MEAT, FISH, OTHER} public Dish(String name, boolean vegetarian, int calories, Type type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } //...... get set }
基于這個食物類,我們給出一個食物類的集合作為模擬數(shù)據(jù):
public static final List<Dish> menuList = Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), new Dish("beef", false, 700, Dish.Type.MEAT), new Dish("chicken", false, 400, Dish.Type.MEAT), new Dish("french fries", true, 530, Dish.Type.OTHER), new Dish("rice", true, 350, Dish.Type.OTHER), new Dish("season fruit", true, 120, Dish.Type.OTHER), new Dish("pizza", true, 550, Dish.Type.OTHER), new Dish("prawns", false, 400, Dish.Type.FISH), new Dish("salmon", false, 450, Dish.Type.FISH) );
我們希望計算出這個菜肴集合的總熱量,我們可能會這樣寫:
public static void main(String[] args) { int total = menuList.stream() //獲取每個食物的卡路里 .map(Dish::getCalories) //調(diào)用reduce,從0開始累加每個食物的熱量 .reduce(0, Integer::sum); System.out.println(total); }
輸出結(jié)果如下:
4300
盡管它盡可能的簡潔并計算出了總熱量,但是它存在許多隱患,首先時map時,它會將基本類型的calories
裝箱成Integer
,這一點我們查看map
的返回值即可知曉。
Stream<Integer> integerStream = menuList.stream() //獲取每個食物的卡路里 .map(Dish::getCalories);
因為拿到的是包裝類的流,調(diào)用reduce進行數(shù)值計算時,有需要對其進行拆箱,拆箱時就會調(diào)用到Integer
的intValue
方法:
public int intValue() { return value; }
所以若在大量數(shù)值計算的情況下,頻繁的拆箱和裝箱勢必導致程序的執(zhí)行效率低下。
特值流
那么有沒有什么辦法可以保證在數(shù)據(jù)收集的時候避免頻繁裝箱和拆箱呢?答案是特化流,就以本案例來說,我們在數(shù)值收集的時候直接調(diào)用mapToInt
方法,通過該方法即可得到每一個數(shù)值的特值流IntStream
,隨后我們直接調(diào)用特值流計算方法sum
即可完成熱量統(tǒng)計:
對應的代碼示例如下:
public static void main(String[] args) { int total = menuList.stream() //將每一個卡路里轉(zhuǎn)換為特值流IntStream .mapToInt(Dish::getCalories) //將所有數(shù)值累加 .sum(); System.out.println(total); }
最終輸出結(jié)果也是4300:
4300
相較于reduce
方法,特值流提供了更多更方便的計算API
:
average
:計算所有數(shù)值的平均數(shù)。count
:獲取數(shù)值總數(shù)。max
:獲取收集數(shù)據(jù)中的最大值。min
:獲取收集數(shù)據(jù)中的最小值。
特化流還原會原始流
有時候我們希望這些特化流轉(zhuǎn)為原始流即包裝類的流,那么我們可直接調(diào)用boxed
方法完成對特值流的裝箱:
public static void main(String[] args) { Stream<Integer> integerStream = menuList.stream() //拿到所有數(shù)值的特值流 .mapToInt(Dish::getCalories) //將所有特值流裝箱 .boxed(); //輸出特值流對象的數(shù)值 integerStream.forEach(i -> System.out.println(i)); }
特化流空數(shù)值問題
我們都知道特化流可以直接獲取收集到數(shù)值的最大值或者最小值,我們假設(shè)這樣一個場景,食物類對象的卡路里字段為Integer
:
private final Integer calories;
并且我們食物類的集合為空:
public static final List<Dish> menuList = new ArrayList<>();
面對可能存在的空結(jié)果問題,要如何解決呢?
實際上java8
已經(jīng)考慮到這個問題了,當我們調(diào)用max
等計算API
獲取結(jié)果時,它實際返回的對象是OptionalInt
,該對象提供了各種API用于判斷數(shù)值是否為空,當我們最大值為空,就直接返回1時,我們可以直接使用orElse
方法:
public static void main(String[] args) { OptionalInt max = menuList.stream() .mapToInt(Dish::getCalories) .max(); //不存在最大值時,直接返回1 System.out.println(max.orElse(0)); }
亦或者我們需要判斷是否存在最大值時,可以直接調(diào)用isPresent
方法:
public static void main(String[] args) { OptionalInt max = menuList.stream() .mapToInt(Dish::getCalories) .max(); //若存在最大值直接返回true System.out.println(max.isPresent()); }
數(shù)值流的范圍操作
我們希望統(tǒng)計1-100之間的偶數(shù)數(shù)量,在java8
之前,你可能會這樣做:
for
循環(huán)1-100。- 判斷是否是偶數(shù)。
- 如果是偶數(shù),則臨時變量
count
自增一下。
而java8
的步驟則精簡許多:
- 基于特值流生成1-100全閉區(qū)間數(shù)據(jù)。
- 過濾出偶數(shù)。
- 調(diào)用
count
進行統(tǒng)計。
public static void main(String[] args) { //生成1-100全閉區(qū)間數(shù)據(jù) long count = IntStream.rangeClosed(1, 100) //過濾出偶數(shù) .filter(i -> i % 2 == 0) //計算統(tǒng)計結(jié)果 .count(); System.out.println(count); }
輸出結(jié)果:
50
當然,如果你要生成左閉右開即1-99,則可以調(diào)用range
方法生成:
IntStream.rangeClosed(1, 99)
數(shù)值流的應用——勾股數(shù)
現(xiàn)在我們來寫一個獲取1-100以內(nèi)前3個勾股數(shù)的小功能。由公式:
a^2 + b^2=c^2
可知,要想得到勾股數(shù),我們只需判斷a^2 + b^2
的和再開根號是否可以被整除,即:
Math.sqrt(a * a + b * b) % 1 == 0
所以我們可以按照下面這樣的步驟執(zhí)行:
- 創(chuàng)建1-100全閉區(qū)間作為第一條邊a。
- 為避免計算的勾股數(shù)重復,出現(xiàn)[
3,4,5]
,[4,3,5]
這種情況,我們的第二條邊b范圍為a-100。 - 拿著a和b,計算這兩個數(shù)值的平方和再開根號看看是否為整數(shù)。
- 將開根號結(jié)果為整數(shù)的結(jié)果生成數(shù)組。
- 獲取前3個這樣的數(shù)組。
所以我們寫出下面這段代碼,需要注意的是筆者在生成b的時候用到了flatMap,原因很簡單,因為生成a時boxed
返回的對象是Stream<Integer>
,假如把這個流直接用map
和b進行映射操作的話,最終結(jié)果只能是[Stream<Integer>,Integer,Integer]
,所以我們需要使用flatMap
將a進行扁平化從而得到一個Integer
:
public static void main(String[] args) { //生成a Stream<int[]> result = IntStream.rangeClosed(1, 100).boxed() //基于a的范圍生成 a-100范圍的b,并過濾出平方再開方后可以整除的b,構(gòu)成數(shù)組 .flatMap(a -> IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a * a + b * b) % 1 == 0).boxed().map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})) //取前3個 .limit(3); //打印輸出 result.forEach(r -> System.out.println(r[0] + " " + r[1] + " " + r[2])); }
最終輸出結(jié)果如下:
3 4 5
5 12 13
6 8 10
但是這種寫法不夠好,可以看到我們得到合適a和b時,還需要手動調(diào)用boxed
將其還原為原始流,再用map
映射為數(shù)組,這樣實在太麻煩了。
還記得我們特化流還原為原始流的一個方法mapToxxx
方法嗎?如果我們希望將其轉(zhuǎn)為數(shù)組,我們在得到a和b之后,直接調(diào)用mapToObj
,代碼一步到位:
public static void main(String[] args) { //生成a Stream<int[]> result = IntStream.rangeClosed(1, 100).boxed() //基于a的范圍生成 a-100范圍的b,并過濾出平方再開方后可以整除的b,構(gòu)成數(shù)組 .flatMap(a -> IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a * a + b * b) % 1 == 0).mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})) //取前3個 .limit(3); //打印輸出 result.forEach(r -> System.out.println(r[0] + " " + r[1] + " " + r[2])); }
以上就是淺聊java8中數(shù)值流的使用的詳細內(nèi)容,更多關(guān)于java8數(shù)值流的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用kafka如何選擇分區(qū)數(shù)及kafka性能測試
這篇文章主要介紹了使用kafka如何選擇分區(qū)數(shù)及kafka性能測試,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot項目創(chuàng)建單元測試的流程步驟
在日常開發(fā)的過程中,對自己的代碼進行單元測試是個非常重要的過程,一方面可以最小范圍的針對一個方法進行測試,提高測試的簡便性以及測試的成本,本篇文章主要是為了總結(jié)一下如何優(yōu)雅的在Springboot項目中使用單元測試去測試功能,需要的朋友可以參考下2024-11-11springboot接收excel數(shù)據(jù)文件去重方式
文章主要介紹了如何在Spring?Boot中實現(xiàn)文件上傳并入庫的功能,包括讀取Excel文件、生成Entity對象、使用MergeInto語句進行數(shù)據(jù)庫操作以及注意事項2024-12-12SpringBoot集成thymeleaf瀏覽器404的解決方案
前后端不分離的古早 SpringMVC 項目通常會使用 thymeleaf 模板引擎來完成 html 頁面與后端接口之間的交互,如果要將項目架構(gòu)升級成 SpringBoot , thymeleaf 也可以照常集成,但有時候會踩到一些坑,所以本文給大家介紹了SpringBoot集成thymeleaf瀏覽器404的解決方案2024-12-12ElasticSearch學習之多條件組合查詢驗證及示例分析
這篇文章主要為大家介紹了ElasticSearch 多條件組合查詢驗證及示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02基于Servlet實現(xiàn)技術(shù)問答網(wǎng)站系統(tǒng)
這篇文章主要為大家詳細介紹了基于Servlet實現(xiàn)技術(shù)問答網(wǎng)站系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04