List<String> words = Arrays.asList("hello","world"); List<String> chars = words.stream() .flatMap(word -> Stream.of(word.split(""))) .collect(Collectors.toList());Jdk8中新增的特性旨在幫助程序員寫出更好的代碼,其中對核心類庫的改進是很關鍵的一部分。對核心類庫的改進主要包括集合類的API和新引入的流(Stream)。流使程序員得以站在更高的抽象層次上對集合進行操做。程序員
1.什麼是流(Stream)?
Stream是特定類型的對象造成的一個隊列並支持聚合操做。 Java中的Stream並不會存儲元素,而是按需計算。流的來源能夠是集合,數組,I/O channel, 產生器generator 等。聚合操做相似SQL語句同樣的操做, 好比filter, map, reduce, find, match, sorted等。和之前的Collection操做不一樣, Stream操做還有兩個基礎的特徵:Pipelining中間操做都會返回流對象自己。 這樣多個操做能夠串聯成一個管道,如同流式風格(fluent style)。 這樣作能夠對操做進行優化, 好比延遲執行(laziness)和短路( short-circuiting)。
在 Java 8 中, 集合接口有兩個方法來生成流:數組
- stream() − 爲集合建立串行流。
- parallelStream() − 爲集合建立並行流。
List<String> list = Arrays.asList("a", "b", "c", "d", null); List<String> filtered = list.stream().filter(e -> Objects.nonNull(e)).collect(Collectors.toList()); long count = list.parallelStream().filter(e -> Objects.nonNull(e)).count();
2.內部迭代
Java程序員在使用集合時,一個通用的模式時在集合上進行迭代,在迭代過程當中處理每個元素並返回處理結果。一般代碼時這樣的:函數
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); int sum = 0; Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { sum += iterator.next(); } System.out.println(sum);
這段代碼自己沒什麼問題就寫起來有點麻煩,就其背後原理來看其實就是一個封裝了迭代的語法糖,首先調用iterator方法產生一個新的Iterator對象進而控制整個迭代過程,這就是外部迭代。學習
然而外部迭代的問題在於首先,很難抽象出後續提到的複雜操做,另外從本質上來說只是一種串行化操做。整體來看使用for循環會將行爲和方法混爲一談。另外一種方法是內部迭代,和調用iterator()做用同樣使用stream()方法,該方法不是返回一個控制迭代的Iterator對象,而是返回內部迭代中的相應接口:Stream。示例以下:優化
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer sum = list.stream().filter(e -> e > 5).reduce(0, Integer::sum); System.out.println(sum);
3.實現機制
一般在Java 中調用一個方法計算機會隨即執行操做:好比,System.out.println("hello world!")會在終端上輸出一條信息。Stream裏的一些方法略有不一樣,它們雖是普通Java方法可是返回的Stream對象並非一個新的集合,而是建立新集合的配方。ui
list.stream().filter(e -> e > 5);
上述代碼並未作什麼實際性的工做,filter只是刻畫出了Stream可是並無產生新的集合,像filter這樣只描述Stream,最終不產生新集合的方法惰性求值方法,而像sum()這樣最終從Stream產出值的方法叫作及早求值方法。若是在filter方法中添加一條輸出語句就會比較清楚的看出差別。spa
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); list.stream().filter(e -> { System.out.println(e); return e > 5; });
若是將上述語句添加一個及早求值方法就能夠看到集合中的元素被打印出來。scala
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); list.stream().filter(e -> { System.out.println(e); return e > 5; }).findFirst();
判斷一個操做是惰性求值仍是及早求值很簡單,只要返回值是Stream那麼是惰性求值。使用這些操做的理想方式就是造成一個惰性求值鏈,最後用一個及早求值的操做返回想要的結果,這正是他的合理之處。那爲何要區分惰性求值和及早求值呢?緣由很簡單隻須要一次迭代就能夠獲得所有結果,例如 :上述代碼只須要找到第一個大於5的數而不須要比較全部集合元素。設計
4.經常使用的流操做
爲了更好的學習Stream,咱們接下來學習一些經常使用的stream操做。code
- filter
若是須要在遍歷集合元素時清洗非法元素可使用filter方法,咱們在上述討論過程當中已經使用過該方法。filter接收一個函數做爲參數,該函數使用lambda表達式表示,若是被清洗元素符合預期則返回true,其實看源碼會發現這個lambda表達式是咱們以前講過的 Predicate<T>函數。
- collect
這個方法是由Stream生成一個List,是一個及早求值操做。Jdk8中借鑑了不少Scala的設計思想,Stream的of方法能夠用來生成一個新的Stream,咱們來看下邊的一個例子。
List<String> list = Stream.of("a", "b", "c","d","e").collect(Collectors.toList());
- map
map函數是用來說一個類型轉換爲另一種類型,參數一樣是一個lambda表達式對應咱們以前講過的 Function<T, R> 函數。由於map是一個惰性求值方法咱們配合以前的collect看一個例子。
List<String> list = Stream.of("a", "b", "c").map(e -> e.toUpperCase()).collect(Collectors.toList()); System.out.println(list);
- flatMap
和map相似,不一樣的是其每一個元素轉換獲得的是Stream對象,最終會把轉換獲得的多個Stream鏈接成一個Stream。
List<String> words = Arrays.asList("hello","world"); List<String> chars = words.stream() .flatMap(word -> Stream.of(word.split(""))) .collect(Collectors.toList());
上述代碼咱們能夠獲得輸出結果 :[h, e, l, l, o, w, o, r, l, d],若是將flatMap換成map會發現咱們獲得了一個List<Stream<String>> ,你們感興趣能夠上手一試。
- max和min
和以前講的函數不一樣,這兩個方法要求參數是一個比較器 Comparator<T>
Integer min = Stream.of(1,2,3,4,5).min(Integer::compare).get(); System.out.println(min); Integer max = Stream.of(1,2,3,4,5).max(Integer::compare).get(); System.out.println(max);
- reduce
該函數能夠實現從一組值中生成一個值,在上述例子中用到的 sum、max、min本質上都是reduce操做。Stream的求和結果每一步都將Stream中的元素累加至accumulator,遍歷至Stream中的最後一個元素時,accumulator的值就是全部元素的和。咱們看以下例子
Integer sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (acc, e) -> acc + e); System.out.println(sum);
這段代碼若是你對scala熟悉那麼會很容易理解,acc做爲累加器存儲了中間計算結果,整個lambda函數本質上是咱們以前講過的BinaryOperator。