Java Stream API入門篇

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

  1. 代碼簡潔,函數式編程寫出的代碼簡潔且意圖明確,使用stream接口讓你今後告別for循環。
  2. 多核友好,Java函數式編程使得編寫並行程序從未如此簡單,你須要的所有就是調用一下parallel()方法。

這一節咱們學習stream,也就是Java函數式編程的主角。對於Java 7來講stream徹底是個陌生東西,stream並非某種數據結構,它只是數據源的一種視圖。這裏的數據源能夠是一個數組,Java容器或I/O channel等。正因如此要獲得一個stream一般不會手動建立,而是調用對應的工具方法,好比:java

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

常見的stream接口繼承關係如圖:git

Java_stream_Interfaces

圖中4種stream接口繼承自BaseStream,其中IntStream, LongStream, DoubleStream對應三種基本類型(int, long, double,注意不是包裝類型),Stream對應全部剩餘類型的stream視圖。爲不一樣數據類型設置不一樣stream接口,能夠1.提升性能,2.增長特定接口函數。github


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的這個特色應該不陌生。app

下表彙總了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規約操做的介紹將刷新你如今的認識。

本文github地址,歡迎關注。

相關文章
相關標籤/搜索