1. 程式人生 > >Java Stream API入門篇

Java Stream API入門篇

本文github地址
你可能沒意識到Java對函數語言程式設計的重視程度,看看Java 8加入函數語言程式設計擴充多少功能就清楚了。Java 8之所以費這麼大功夫引入函數語言程式設計,原因有二:

  1. 程式碼簡潔,函數語言程式設計寫出的程式碼簡潔且意圖明確,使用stream介面讓你從此告別for迴圈。
  2. 多核友好,Java函數語言程式設計使得編寫並行程式從未如此簡單,你需要的全部就是呼叫一下parallel()方法。

這一節我們學習stream,也就是Java函數語言程式設計的主角。對於Java 7來說stream完全是個陌生東西,stream並不是某種資料結構,它只是資料來源的一種檢視。這裡的資料來源可以是一個數組,Java容器或I/O channel等。正因如此要得到一個stream

通常不會手動建立,而是呼叫對應的工具方法,比如:

  • 呼叫Collection.stream()或者Collection.parallelStream()方法
  • 呼叫Arrays.stream(T[] array)方法

常見的stream介面繼承關係如圖:

Java_stream_Interfaces

圖中4種stream介面繼承自BaseStream,其中IntStream, LongStream, DoubleStream對應三種基本型別(int, long, double,注意不是包裝型別),Stream對應所有剩餘型別的stream檢視。為不同資料型別設定不同stream介面,可以1.提高效能,2.增加特定介面函式。

WRONG_Java_stream_Interfaces

你可能會奇怪為什麼不把IntStream

等設計成Stream的子介面?畢竟這介面中的方法名大部分是一樣的。答案是這些方法的名字雖然相同,但是返回型別不同,如果設計成父子介面關係,這些方法將不能共存,因為Java不允許只有返回型別不同的方法過載。

雖然大部分情況下stream是容器呼叫Collection.stream()方法得到的,但streamcollections有以下不同:

  • 無儲存stream不是一種資料結構,它只是某種資料來源的一個檢視,資料來源可以是一個數組,Java容器或I/O channel等。
  • 為函數語言程式設計而生。對stream的任何修改都不會修改背後的資料來源,比如對stream執行過濾操作並不會刪除被過濾的元素,而是會產生一個不包含被過濾元素的新stream
  • 惰式執行stream上的操作並不會立即執行,只有等到使用者真正需要結果的時候才會執行。
  • 可消費性stream只能被“消費”一次,一旦遍歷過就會失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。

stream的操作分為為兩類,中間操作(intermediate operations)和結束操作(terminal operations),二者特點是:

  1. 中間操作總是會惰式執行,呼叫中間操作只會生成一個標記了該操作的新stream,僅此而已。
  2. 結束操作會觸發實際計算,計算髮生時會把所有中間操作積攢的操作以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 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的字串youtoo。注意,由於filter()是個中間操作,如果只調用filter()不會有實際計算,因此也不會輸出任何資訊。

distinct()

Stream 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()

Stream 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()

Stream 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個數字。

結語

截止到目前我們感覺良好,已介紹StreamAPI理解起來並不費勁兒。如果你就此以為函數語言程式設計不過如此,恐怕是高興地太早了。下一節對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容器來管理對象的生命周期、依賴關系等,從而使得應用程序的配置和