Java Stream API入門篇
本文github地址
你可能沒意識到Java對函數語言程式設計的重視程度,看看Java 8加入函數語言程式設計擴充多少功能就清楚了。Java 8之所以費這麼大功夫引入函數語言程式設計,原因有二:
- 程式碼簡潔,函數語言程式設計寫出的程式碼簡潔且意圖明確,使用stream介面讓你從此告別for迴圈。
- 多核友好,Java函數語言程式設計使得編寫並行程式從未如此簡單,你需要的全部就是呼叫一下
parallel()
方法。
這一節我們學習stream,也就是Java函數語言程式設計的主角。對於Java 7來說stream完全是個陌生東西,stream並不是某種資料結構,它只是資料來源的一種檢視。這裡的資料來源可以是一個數組,Java容器或I/O channel等。正因如此要得到一個stream
- 呼叫
Collection.stream()
或者Collection.parallelStream()
方法 - 呼叫
Arrays.stream(T[] array)
方法
常見的stream介面繼承關係如圖:
圖中4種stream介面繼承自BaseStream
,其中IntStream, LongStream, DoubleStream
對應三種基本型別(int, long, double
,注意不是包裝型別),Stream
對應所有剩餘型別的stream檢視。為不同資料型別設定不同stream介面,可以1.提高效能,2.增加特定介面函式。
你可能會奇怪為什麼不把IntStream
Stream
的子介面?畢竟這介面中的方法名大部分是一樣的。答案是這些方法的名字雖然相同,但是返回型別不同,如果設計成父子介面關係,這些方法將不能共存,因為Java不允許只有返回型別不同的方法過載。
雖然大部分情況下stream是容器呼叫Collection.stream()
方法得到的,但stream和collections有以下不同:
- 無儲存。stream不是一種資料結構,它只是某種資料來源的一個檢視,資料來源可以是一個數組,Java容器或I/O channel等。
- 為函數語言程式設計而生。對stream的任何修改都不會修改背後的資料來源,比如對stream執行過濾操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream
- 惰式執行。stream上的操作並不會立即執行,只有等到使用者真正需要結果的時候才會執行。
- 可消費性。stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。
對stream的操作分為為兩類,中間操作(intermediate operations)和結束操作(terminal operations),二者特點是:
- 中間操作總是會惰式執行,呼叫中間操作只會生成一個標記了該操作的新stream,僅此而已。
- 結束操作會觸發實際計算,計算髮生時會把所有中間操作積攢的操作以pipeline的方式執行,這樣可以減少迭代次數。計算完成之後stream就會失效。
如果你熟悉Apache Spark RDD,對stream的這個特點應該不陌生。
下表彙總了Stream
介面的部分常見方法:
操作型別 | 介面方法 |
---|---|
中間操作 | concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered() |
結束操作 | allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray() |
區分中間操作和結束操作最簡單的方法,就是看方法的返回值,返回值為stream的大都是中間操作,否則是結束操作。
stream方法使用
stream跟函式介面關係非常緊密,沒有函式介面stream就無法工作。回顧一下:函式介面是指內部只有一個抽象方法的介面。通常函式接口出現的地方都可以使用Lambda表示式,所以不必記憶函式介面的名字。
forEach()
我們對forEach()
方法並不陌生,在Collection
中我們已經見過。方法簽名為void forEach(Consumer<? super E> action)
,作用是對容器中的每個元素執行action
指定的動作,也就是對元素進行遍歷。
// 使用Stream.forEach()迭代
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.forEach(str -> System.out.println(str));
由於forEach()
是結束方法,上述程式碼會立即執行,輸出所有字串。
filter()
函式原型為Stream<T> filter(Predicate<? super T> predicate)
,作用是返回一個只包含滿足predicate
條件元素的Stream
。
// 保留長度等於3的字串
Stream<String> stream= Stream.of("I", "love", "you", "too");
stream.filter(str -> str.length()==3)
.forEach(str -> System.out.println(str));
上述程式碼將輸出為長度等於3的字串you
和too
。注意,由於filter()
是個中間操作,如果只調用filter()
不會有實際計算,因此也不會輸出任何資訊。
distinct()
函式原型為Stream<T> distinct()
,作用是返回一個去除重複元素之後的Stream
。
Stream<String> stream= Stream.of("I", "love", "you", "too", "too");
stream.distinct()
.forEach(str -> System.out.println(str));
上述程式碼會輸出去掉一個too
之後的其餘字串。
sorted()
排序函式有兩個,一個是用自然順序排序,一個是使用自定義比較器排序,函式原型分別為Stream<T> sorted()
和Stream<T> sorted(Comparator<? super T> comparator)
。
Stream<String> stream= Stream.of("I", "love", "you", "too");
stream.sorted((str1, str2) -> str1.length()-str2.length())
.forEach(str -> System.out.println(str));
上述程式碼將輸出按照長度升序排序後的字串,結果完全在預料之中。
map()
函式原型為<R> Stream<R> map(Function<? super T,? extends R> mapper)
,作用是返回一個對當前所有元素執行執行mapper
之後的結果組成的Stream
。直觀的說,就是對每個元素按照某種操作進行轉換,轉換前後Stream
中元素的個數不會改變,但元素的型別取決於轉換之後的型別。
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.map(str -> str.toUpperCase())
.forEach(str -> System.out.println(str));
上述程式碼將輸出原字串的大寫形式。
flatMap()
函式原型為<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
,作用是對每個元素執行mapper
指定的操作,並用所有mapper
返回的Stream
中的元素組成一個新的Stream
作為最終返回結果。說起來太拗口,通俗的講flatMap()
的作用就相當於把原stream中的所有元素都"攤平"之後組成的Stream
,轉換前後元素的個數和型別都可能會改變。
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2), Arrays.asList(3, 4, 5));
stream.flatMap(list -> list.stream())
.forEach(i -> System.out.println(i));
上述程式碼中,原來的stream
中有兩個元素,分別是兩個List<Integer>
,執行flatMap()
之後,將每個List
都“攤平”成了一個個的數字,所以會新產生一個由5個數字組成的Stream
。所以最終將輸出1~5這5個數字。
結語
截止到目前我們感覺良好,已介紹Stream
API理解起來並不費勁兒。如果你就此以為函數語言程式設計不過如此,恐怕是高興地太早了。下一節對Stream
規約操作的介紹將重新整理你現在的認識。
相關推薦
Java Stream API入門篇
本文github地址 你可能沒意識到Java對函數語言程式設計的重視程度,看看Java 8加入函數語言程式設計擴充多少功能就清楚了。Java 8之所以費這麼大功夫引入函數語言程式設計,原因有二: 程式碼簡潔,函數語言程式設計寫出的程式碼簡潔且意圖明確,使用stream介面讓你從此告別for迴圈。 多核友好,
Java Stream API進階篇
上一節介紹了部分Stream常見介面方法,理解起來並不困難,但Stream的用法不止於此,本節我們將仍然以Stream為例,介紹流的規約操作。 規約操作(reduction operation)又被稱作摺疊操作(fold),是通過某個連線動作將所有元素彙總成一個彙總結果的過程。元素求和、求最大值或最小值、求
java註解詳細-----入門篇
epo 自帶 In 常見 upd ima OS font 分享 一、註解入門 JDK自帶註解:@Override , @Deprecated , @Suppresswarnings 常見的第三方註解:Spring框架:@Autowired , @Ser
[java]Stream API——reduce
聚合操作 得到 span 用戶 red item 計算 integer 循環 聚合操作reduce T reduce(T identity, BinaryOperator accumulator) 代碼: int value = Stream.of(1, 2, 3, 4)
[java]Stream API——collect
求和 pin -s mil toc tor 最大 api system 一、R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) supplier:一個能創造目標類型實例的方法。
asp.net mvc+httpclient+asp.net mvc api入門篇
第一步:建立一個ASP.NET MVC API專案 第二步:在api專案裡面建立一個類 public class Student { public int Id { get; set; } public string Name { get; set; }
Java8系列--Java Stream進階篇(流的操作)
1 流的操作的特點 1.1 流的操作的核心機制 流的操作區別於傳統的集合操作的一大特點是,在Java 8中,流的操作是通過將外部迭代轉向內部迭代來實現的。 在Java 8 Stream API中,流的操作實際上相當於對資料進行一系列的”篩選”操
Java Stream API效能測試
已經對Stream API的用法鼓吹夠多了,用起簡潔直觀,但效能到底怎麼樣呢?會不會有很高的效能損失?本節我們對Stream API的效能一探究竟。 為保證測試結果真實可信,我們將JVM執行在-server模式下,測試資料在GB量級,測試機器採用常見的商用伺服器,配置如下: OSCentOS 6.7 x86_
記一次用Java Stream Api的經歷
最近有個專案需要用到推薦系統,弄了個簡單的相似度推薦演算法。 資料為: 化簡為: public class Worker { /** * 使用者編號 */ private long userId; /** * 期
教妹學Java:Spring 入門篇
你好呀,我是沉默王二,一個和黃家駒一樣身高,劉德華一樣顏值的程式設計師(管你信不信呢)。從兩位偶像的年紀上,你就可以斷定我的碼齡至少在 10 年以上,但實話實說,我一直堅信自己只有 18 歲,因為我有一顆好學的心。本篇文章就打算通過我和三妹對話的形式來學一學“Spring 的基礎”。 教妹學 Java,沒見過
Java8系列--Java Stream入門篇(流的操作)
1 Stream介面 1.1 介面情況 java.util.stream介面下共有DoubleStream、LongStream、IntStream、Stream四個介面,如下圖所示: 1.2 流的分類和使用 其中,Stream介面定義了
Java應用基礎微專業-入門篇-第1章用程序來做計算
version mac ear 浮點 spa class pin system font p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 20.0px "PingFang SC" } p.p2 { margin: 0.0px 0.
Java 8 Stream API具體解釋
介紹 lis 控制臺輸出 output 排序。 case 編程效率 func 遍歷 Java 8 Stream API具體解釋 一、Stream API介紹 Java 8引入了全新的Stream API,此Stream與Java I/O包裏的In
百度地圖api入門介紹(js篇)
顯示 .com cnblogs androi web 入門介紹 步驟 api 地圖api 最近因為用到了百度地圖的api,感覺還有點用記錄一下,一方面充實一下自己,第二也希望有用到的同學可以參考一下;因為之前用過android baidu api 所以再用web
《Java從入門到放棄》入門篇:Struts2的基本訪問方式
java action struts Struts2是個什麽玩意呢?引用百度百科的介紹:Struts2是一個基於MVC設計模式的Web應用框架,它本質上相當於一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的數據交互。介紹完畢···其核心原理
《Java從入門到放棄》入門篇:Struts2的基本數據傳遞方式
java struts action 把這個和JSP的數據傳遞方式對比一下,你就會發現·······真的可以少寫兩句代碼!!!struts2中常用的兩種數據傳遞方式如下:屬性匹配方式ModelDriven接口匹配方式(常用於自定義類型)個人比較喜歡使用第一種,為什麽呢?因為············
《Java從入門到放棄》入門篇:Struts2的常用基本標簽
java struts action s標簽庫 說起Struts2中的標簽,這真是個好東西,為什麽呢?因為··························就算你會這玩意,別人也可能會說,這玩意居然還有人學,用JSTL和EL表達式不就行了麽!還有一種情況。如果你不會,你可以理直氣狀的說:這是
《Java從入門到放棄》入門篇:Struts2的常用驗證方式
java struts action 感覺過了一個周末,人都懶得不要不要的,今天就來點簡單的內容吧 - -,各位看官如果欲求不滿的話,可以自行解決或再去寵幸其他“勃主”···struts2的驗證方式主要有四種方式:一、直接在功能方法中寫驗證代碼(不要扔雞蛋,這種辦法確實算一種)二、重寫valida
《Java從入門到放棄》入門篇:Struts2的常用驗證方式(二)
java struts action validate 數據驗證 前一回,我們講完了“直接在功能方法中寫驗證代碼”這種驗證方式,接下來,我們繼續搞定後續的三種方式。二、重寫validate方法(註意這個方法會驗證該類中所有的方法) 使用重寫驗證方法的好處就是,又可以少寫一句代碼了!!
《Java從入門到放棄》入門篇:spring中IOC的註入姿勢
java ioc spring IOC到底是個什麽東東呢?控制反轉(Inversion of Control,英文縮寫為IoC),其實就是這個東東。你隨便百度一下就會得到比較書面的解釋:通過引入實現了IoC模式的IoC容器,即可由IoC容器來管理對象的生命周期、依賴關系等,從而使得應用程序的配置和