摘要:將在非空的時候返回值,否則會拋出沒有這個元素的異常。構建流現(xiàn)在我們已經(jīng)能夠使用從集合生成流了。由文件生成流不重復的單詞數(shù)預處理獲取流,使用后不用手動關閉流。我們使用得到流,其中每個元素就是文本里的一行。
篩選和切片 filter
filter 會接受一個謂詞作為參數(shù),并返回符合該條件的元素流。
ListvegetarianMenu = menu .stream() .filter(Dish::getVegetarian) .collect(Collectors.toList());
會篩選出素食的菜肴。
distinctdistinct 會返回一個元素各異的流,也就是去重。
ListcaloriesMenu = menu .stream() .filter((Dish dish) -> 350 == dish.getCalories()) .map(Dish::getCalories) .distinct() .collect(Collectors.toList());
會篩選出卡路里為350的菜肴并去重(米飯和對蝦二選一)。
limit(n)limit(n) 會返回一個不超過給定長度的流。
ListcaloriesMenu = menu .stream() .filter((Dish dish) -> 350 < dish.getCalories()) .limit(3) .collect(Collectors.toList());
List 是有序集合所以會順序篩選出三個,而如果是 Set 無序的則不會以任何順序排列。
skip(n)skip(n) 返回一個人掉了前 n 個元素的流,和 limit(n) 是互補的。
List映射caloriesMenu = menu .stream() .filter((Dish dish) -> 350 < dish.getCalories()) .skip(3) .collect(Collectors.toList());
map 返回一個映射好的元素流。如果我們要找出每個菜肴的名稱有多長,那么可以這樣思考:第一,我們需要知道菜肴的名稱。第二,計算菜肴名稱的長度。
List流的扁平化caloriesMenu = menu .stream() // 映射名稱字符串 .map(Dish::getName) // 映射字符串長度 .map(String::length) .collect(Collectors.toList());
返回菜肴名稱的長度已經(jīng)做到了,如果有奇怪的需求,比如將菜肴名稱打碎成字符并去重……
ListcaloriesMenu = menu .stream() // 映射名稱字符串 .map(Dish::getName) // 將字符串分割為字符數(shù)組 String[] .map((String string) -> string.split("")) // Arrays.stream() 可以接收一個數(shù)組并產(chǎn)生一個流,再調(diào)用 flatMap 將其轉成單個流 .flatMap(Arrays::stream) // 去重 .distinct() // 保存 .collect(Collectors.toList());
扁平化在這里就是讓字符數(shù)組不是分別映射成一個流,而是映射成流的內(nèi)容,然后將使用 Arrays::stream 時生成的單個流都被合并起來。
查找和匹配 anyMatchanyMatch 可以檢查謂詞是否至少匹配一個元素。
// 檢查是否至少含有一道是素食的菜肴 Boolean b = menu .stream() .anyMatch(Dish::getVegetarian);allMatch
allMatch 可以檢查謂詞是否全部匹配。
// 檢查是否全部是素食 Boolean b = menu .stream() .anyMatch(Dish::getVegetarian);noneMatch
noneMatch 可以檢查謂詞是否全部不匹配。
// 檢查是否全部不是素食 Boolean b = menu .stream() .noneMatch(Dish::getVegetarian);
以上這三種操作都用到了所謂的短路,也和 Java 中!、&&和||運算符意義一樣。
findAnyfindAny 將返回當前流中的任意元素。
OptionaldishOptional = menu .stream() .findAny();
Optional
isPresent() 將在非空的時候返回 true。
ifPresent(Consumer super T> consumer) 將在非空的時候執(zhí)行 Consumer 的代碼,再第二章的時候我們知道 Consumer 可以接受一個對象并進行操作。
T get() 將在非空的時候返回值,否則會拋出沒有這個元素的異常。
T orElse(T other) 將在非空的時候返回值,否則返回一個默認值。
比如上面的代碼可以變成這樣
menu.stream().findAny().ifPresent(System.out::println);
就可以打印輸出任意菜肴的信息。
findFirst與 findAny 類似,只不過返回有序集合的第一個元素。
OptionaldishOptional = menu .stream() .findFirst();
那么 findAny 和 findFirst 有什么區(qū)別呢?在并行計算里面,找到第一個元素會需要很多思考,所以一般在并行處理時用 findAny。
歸納reduce 操作可以吧一個流中的元素組合起來,比如菜單里的總卡路里。此類查詢需要將流中所有元素反復結合起來,得到一個值。這樣的查詢操作可以被歸類為 歸約 操作,用函數(shù)式編程語言的術語來說,這稱為 折疊。
元素求和平常使用 for-each 來處理總卡路里我們一般會這樣做
Integer integer = 0; for (Dish dish : menu) { integer += dish.getCalories(); }
在流中可以使用 reduce 方法來處理
Integer integer = menu .stream() .map(Dish::getCalories) .reduce(0, (Integer a, Integer b) -> a + b);
好像有點復雜,沒關系,接下來可以詳細講解。
首先我們用 map 將 Dish 對象映射成其卡路里值的 Integer 類型。然后調(diào)用 reduce 將每個映射好的 Integer 組合(相加)。其中 reduce 方法第一個參數(shù)代表初始值,后面跟著一個 BinaryOperator
可以看一下 BinaryOperator 的源碼
@FunctionalInterface public interface BinaryOperatorextends BiFunction { public static BinaryOperator minBy(Comparator super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } public static BinaryOperator maxBy(Comparator super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; } }
實際上我們調(diào)用的是第三章有提到過的 BiFunction 里的 apply 方法
@FunctionalInterface public interface BiFunction{ R apply(T t, U u); }
BinaryOperator 它接受兩個同類型參數(shù)并返回一個同類型的對象。
所以我們可以這樣來寫
BinaryOperatorintegerBinaryOperator = (Integer a, Integer b) -> a + b;
代表計算 a + b。而 reduce 自帶循環(huán),所以會在背后進行類似于 a += b 一樣的邏輯操作。reduce還有一個重載的版本,可以不接受初始值返回一個 Optional 對象。
Optionalinteger = menu .stream() .map(Dish::getCalories) .reduce((Integer a, Integer b) -> a + b);
用 Optional 的意義與之前一樣,考慮到了集合為空的情況。
最大和最小reduce 同樣可以用來做大小比較,因為其需要的 BinaryOperator 自帶有比較方法。
最大
Optionalinteger = menu .stream() .map(Dish::getCalories) .reduce(Integer::max);
其中 reduce(Integer::max) 是
reduce((Integer integer1, Integer integer2) -> Integer.max(integer1, integer2))
的縮寫。計算最小值只需要將 max 換成 min 就行。歸納方法的對比傳統(tǒng)的 for-each 優(yōu)勢是可以把內(nèi)部迭代抽象化,這讓其內(nèi)部可以并行化而不用我們自己去實現(xiàn)并行處理。
但是這種并行是有限制的,不是將所有的 stream 換成 parallelStream 就行了。
像 map 或 filter 等操作會從輸入流中獲取每一個元素并在輸出流中得到至多一個結果。這些操作一般是無狀態(tài)的,做并行是可行的。
像 reduce、max 等操作需要內(nèi)部狀態(tài)來累積結果,因為求和肯定是需要之前數(shù)的和加上現(xiàn)在選擇的這個元素,但無論哪個都是有限的,所以稱為有界,是可以直接做并行。
相反,如同 distinct 操作,是需要接受一個流再生成一個流,并且去重操作是需要知道先前的歷史流是什么樣的,例如把所有質數(shù)倒序,就需要最大的那個質數(shù),但這是不存在的,所以稱為無界狀態(tài)。做并行需要好生思考一番。
關于去重,其實可以換一種思路,將有序集合(List)轉為無序集合(Set)就自動去重了。
Set數(shù)值范圍caloriesMenu = menu .stream() .filter((Dish dish) -> 350 == dish.getCalories()) .map(Dish::getCalories) .collect(Collectors.toSet());
在和數(shù)字打交道時,比較常用的就是在一個數(shù)值范圍內(nèi)生成數(shù)字。Java 8引入了兩個可以用于 IntStream 和 LongStream 的靜態(tài)方法,幫助生成范圍:range 和 rangeClosed。是不包含結束值的,比如傳入(0,100)就會運算到99結束。
比如我們需要統(tǒng)計0~100里偶數(shù)的個數(shù)(包含100)
long l = IntStream .rangeClosed(0, 100) .filter((int n) -> n % 2 == 0) .count();
這樣就統(tǒng)計出了51個偶數(shù)個數(shù),如果不包含100就用 range,會統(tǒng)計50個。
構建流現(xiàn)在我們已經(jīng)能夠使用 Stream 從集合生成流了。那么如何從值序列、數(shù)組和文件生成流,甚至函數(shù)來創(chuàng)建無限流?
創(chuàng)建一個空流Stream由值創(chuàng)建流stringStream = Stream.empty();
StreamstringStream = Stream.of("qwe", "asd", "zxc"); stringStream.map(String::toUpperCase).forEach(System.out::println);
這樣會把小寫字符串全部轉成大寫。
由數(shù)組創(chuàng)建流int[] ints = {1, 2, 3, 4, 5}; int sum = Arrays.stream(ints).sum();
這樣會把所有的數(shù)字相加。
由文件生成流/* /Users/cciradih/java: Java Platform, Standard Edition (Java SE) lets you develop and deploy Java applications on desktops and servers, as well as in today"s demanding embedded environments. Java offers the rich user interface, performance, versatility, portability, and security that today"s applications require. */ // 不重復的單詞數(shù) long uniqueWords = 0; // 預處理獲取流,使用后不用手動關閉流。 try (StreamstringStream = Files.lines(Paths.get("/Users/cciradih/java"), Charset.defaultCharset())) { // 從流中統(tǒng)計 uniqueWords = stringStream // 扁平化字符串數(shù)組 .flatMap((String line) -> Arrays.stream(line.split(" "))) // 去重 .distinct() // 統(tǒng)計 .count(); } catch (IOException e) { e.printStackTrace(); }
這里是用到了新的 NIO,以便利用 Stream。我們使用 Files.lines 得到流,其中每個元素就是文本里的一行。然后使用 split 拆分成單詞,并用 flatMap 將其扁平化。最后用 distinct 去重再用 count 統(tǒng)計。
由函數(shù)生成流Stream 提供了兩個靜態(tài)方法來從函數(shù)生成流 iterate 和 gengrate。這兩個操作都可以創(chuàng)建無線流。
iterate
Stream.iterate(0, (Integer integer) -> integer + 2);
這會創(chuàng)建一個從0開始無限的偶數(shù)流。
gengrate
Stream.generate(Math::random);
這會創(chuàng)建一個0到1之間無限的隨機數(shù)。
這一章詳細講了流的使用場景和需要注意的地方,特別是和傳統(tǒng)的操作做對比,能更好地支持并行處理。
Java 8 實戰(zhàn) 第五章 使用流 讀書筆記
歡迎加入咖啡館的春天(338147322)。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70209.html
摘要:在包下主要包括輸入輸出兩種流,每種輸入輸出流又可分為字節(jié)流和字符流兩大類。輸入輸出是從程序運行所在的內(nèi)存的角度而言的。的輸入流主要由和作為基類,而輸出流主要由和作為基類。 本章主要參考和摘自瘋狂java講義上面的(java編程思想的后面看過后有新的內(nèi)容再補充進去吧)。 輸入輸出是所有程序都必需的部分————使用輸入機制允許程序讀取外部數(shù)據(jù)(包括磁盤、光盤等存儲設備上的數(shù)據(jù)和用戶輸入的...
摘要:一面試題及剖析今日面試題今天壹哥帶各位復習一塊可能會令初學者比較頭疼的內(nèi)容,起碼當時讓我很有些頭疼的內(nèi)容,那就是流。在這里壹哥會從兩部分展開介紹流,即與流。除此之外盡量使用字節(jié)流。關閉此輸入流并釋放與流相關聯(lián)的任何系統(tǒng)資源。 一. 面試題及剖析 1. 今日面試題 今天 壹哥 帶各位復習一塊可...
摘要:分類一按操作方式類結構字節(jié)流和字符流字節(jié)流以字節(jié)為單位,每次次讀入或讀出是位數(shù)據(jù)。該對象并不是流體系中的一員,其封裝了字節(jié)流,同時還封裝了一個緩沖區(qū)字符數(shù)組,通過內(nèi)部的指針來操作字符數(shù)組中的數(shù)據(jù)。 分類一:按操作方式(類結構) 字節(jié)流和字符流: 字節(jié)流:以字節(jié)為單位,每次次讀入或讀出是8位數(shù)據(jù)。可以讀任何類型數(shù)據(jù)。 字符流:以字符為單位,每次次讀入或讀出是16位數(shù)據(jù)。其只能讀取字符類...
摘要:是一個系統(tǒng)支持的所有字符的集合,包括各國家文字標點符號圖形符號數(shù)字等字符集簡體中文碼表。支持中國國內(nèi)少數(shù)民族的文字,同時支持繁體漢字以及日韓漢字等字符集為表達任意語言的任意字符而設計,是業(yè)界的一種標準,也稱為統(tǒng)一碼標準萬國碼。 1 File1.1 File類的概述和構造方法File: 它是文件和目錄路徑名的抽象...
摘要:當使用節(jié)點流進行輸入輸出時,程序直接連接到實際的數(shù)據(jù)源,和時間的輸入輸出節(jié)點連接處理流則用于對一個已存在的流進行連接或封裝,通過封裝后的流來實現(xiàn)數(shù)據(jù)讀寫功能,處理流也被稱為高級流。 文件的編碼 文本文件就是字節(jié)序列,可以是任意編碼形式。在中文操作系統(tǒng)上直接創(chuàng)建文本文件,則該文本文件只能識別ANSI編碼,其他編碼方式會產(chǎn)生亂碼 package imooc.io; import java...
摘要:的是實現(xiàn)輸入輸出的基礎中把不同的輸入輸出源鍵盤文件網(wǎng)絡連接抽象的表述為流流的分類輸入流和輸出流按照流的流向來分輸入流只能從中讀數(shù)據(jù)而不能向其中寫數(shù)據(jù)輸出流只能向其中寫出數(shù)據(jù)而不能從中讀取數(shù)據(jù)此處的輸入輸出涉及到一個方向問題數(shù)據(jù)從內(nèi)存到硬盤被 Java的IO是實現(xiàn)輸入輸出的基礎,Java中把不同的輸入/輸出源(鍵盤,文件,網(wǎng)絡連接)抽象的表述為流,stream. 流的分類 輸入流和輸...
閱讀 1246·2021-09-01 10:30
閱讀 2118·2021-07-23 10:38
閱讀 895·2019-08-29 15:06
閱讀 3151·2019-08-29 13:53
閱讀 3277·2019-08-26 11:54
閱讀 1822·2019-08-26 11:38
閱讀 2370·2019-08-26 10:29
閱讀 3128·2019-08-23 18:15